First version of explain. Creating a new parser was a wrong approach. Need to reimplement
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import copy
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Dict
|
||||
|
||||
from core.concept import Concept
|
||||
from core.utils import get_full_qualified_name
|
||||
from parsers.ExpressionParser import ExprNode
|
||||
|
||||
|
||||
class FormatDetailType(Enum):
|
||||
Props_In_Line = "in line" # the properties are listed line by line
|
||||
Props_In_Column = "in column" # the properties are listed row by row
|
||||
|
||||
|
||||
@dataclass
|
||||
class FormatDetailDesc:
|
||||
"""
|
||||
class that describes how to print the details
|
||||
"""
|
||||
predicate: ExprNode # the detail will be printed if the predicate is matched
|
||||
format_type: FormatDetailType
|
||||
properties: Dict[str, str] # name of the property, format to use
|
||||
|
||||
|
||||
class FormatInstructions:
|
||||
def __init__(self, tab_indent=None, tab=None, no_color=None):
|
||||
self._tab_indent = 2
|
||||
self._tab = ""
|
||||
self._no_color = False
|
||||
|
||||
self.recursive_props = {} # which property that does in recursion and what depth
|
||||
self.format_l = {} # what format to use when printing obj line by line
|
||||
self.format_d = [] # list of FormatDetailDesc
|
||||
|
||||
# keep track of the modifications
|
||||
self.modified = set()
|
||||
self.recursive_props_modified = set()
|
||||
self.format_l_modified = set()
|
||||
|
||||
if tab_indent is not None:
|
||||
self.tab_indent = tab_indent
|
||||
if tab:
|
||||
self.tab = tab
|
||||
if no_color:
|
||||
self.no_color = no_color
|
||||
|
||||
@property
|
||||
def tab(self):
|
||||
return self._tab
|
||||
|
||||
@tab.setter
|
||||
def tab(self, value):
|
||||
self._tab = value
|
||||
self.modified.add("tab")
|
||||
|
||||
@property
|
||||
def tab_indent(self):
|
||||
return self._tab_indent
|
||||
|
||||
@tab_indent.setter
|
||||
def tab_indent(self, value):
|
||||
self._tab_indent = value
|
||||
self.modified.add("tab_indent")
|
||||
|
||||
@property
|
||||
def no_color(self):
|
||||
return self._no_color
|
||||
|
||||
@no_color.setter
|
||||
def no_color(self, value):
|
||||
self._no_color = value
|
||||
self.modified.add("no_color")
|
||||
|
||||
def set_recurse(self, prop_name, depth):
|
||||
self.recursive_props[prop_name] = depth
|
||||
self.recursive_props_modified.add(prop_name)
|
||||
return self
|
||||
|
||||
def recurse(self, property_name):
|
||||
clone = self.clone()
|
||||
clone.tab = self.tab + (" " * self.tab_indent)
|
||||
clone.recursive_props[property_name] -= 1
|
||||
return clone
|
||||
|
||||
def set_format_l(self, obj, template):
|
||||
key = self.get_obj_key(obj)
|
||||
self.format_l[key] = template
|
||||
self.format_l_modified.add(key)
|
||||
return self
|
||||
|
||||
def add_format_d(self, predicate, properties, format_type=FormatDetailType.Props_In_Line):
|
||||
if isinstance(properties, list):
|
||||
properties = dict((p, "{" + p + " }") for p in properties)
|
||||
self.format_d.append(FormatDetailDesc(predicate, format_type, properties))
|
||||
return self
|
||||
|
||||
def clone(self):
|
||||
clone = copy.deepcopy(self)
|
||||
return clone
|
||||
|
||||
def merge(self, other):
|
||||
for prop in other.modified:
|
||||
setattr(self, prop, getattr(other, prop))
|
||||
|
||||
for prop in other.recursive_props_modified:
|
||||
self.set_recurse(prop, other.recursive_props[prop])
|
||||
|
||||
for key in other.format_l_modified:
|
||||
self.set_format_l(key, other.format_l[key])
|
||||
|
||||
self.format_d.extend(other.format_d)
|
||||
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def get_obj_key(obj):
|
||||
return obj.id if isinstance(obj, Concept) else \
|
||||
obj if isinstance(obj, str) else \
|
||||
get_full_qualified_name(obj)
|
||||
@@ -0,0 +1,78 @@
|
||||
from printer.FormatInstructions import FormatDetailDesc, FormatDetailType, FormatInstructions
|
||||
|
||||
|
||||
class Formatter:
|
||||
|
||||
def __init__(self):
|
||||
self.custom_l_formats = {}
|
||||
self.custom_d_formats = []
|
||||
|
||||
def register_format_l(self, obj, template):
|
||||
key = FormatInstructions.get_obj_key(obj)
|
||||
self.custom_l_formats[key] = template
|
||||
return self
|
||||
|
||||
def register_format_d(self, predicate, properties, format_type=FormatDetailType.Props_In_Line):
|
||||
if isinstance(properties, list):
|
||||
properties = dict([(p, "{" + p + "}") for p in properties])
|
||||
self.custom_d_formats.append(FormatDetailDesc(predicate, format_type, properties))
|
||||
return self
|
||||
|
||||
def compute_format_l(self, custom_formats_override, key):
|
||||
if custom_formats_override and key in custom_formats_override:
|
||||
custom_template = custom_formats_override[key]
|
||||
if custom_template in ("+", "\\+", "+\\"):
|
||||
return custom_template
|
||||
elif custom_template.startswith("+"):
|
||||
registered_template = self.custom_l_formats[key] if key in self.custom_l_formats else ""
|
||||
return registered_template + custom_template[1:]
|
||||
elif custom_template.startswith("\\+"):
|
||||
return custom_template[1:]
|
||||
elif custom_template.endswith("\\+"):
|
||||
return custom_template[:-2] + "+"
|
||||
elif custom_template.endswith("+"):
|
||||
registered_template = self.custom_l_formats[key] if key in self.custom_l_formats else ""
|
||||
return custom_template[:-1] + registered_template
|
||||
else:
|
||||
return custom_template
|
||||
elif key in self.custom_l_formats:
|
||||
return self.custom_l_formats[key]
|
||||
else:
|
||||
return None
|
||||
|
||||
def compute_format_d(self, custom_formats_override):
|
||||
if custom_formats_override and not self.custom_d_formats:
|
||||
return custom_formats_override
|
||||
if self.custom_d_formats and not custom_formats_override:
|
||||
return self.custom_d_formats
|
||||
if self.custom_d_formats and custom_formats_override:
|
||||
return self.custom_d_formats + custom_formats_override
|
||||
return []
|
||||
|
||||
def format_l(self, obj, custom_formats_override=None):
|
||||
key = FormatInstructions.get_obj_key(obj)
|
||||
format_l = self.compute_format_l(custom_formats_override, key)
|
||||
return self.to_string(obj, format_l) if format_l else str(obj)
|
||||
|
||||
def format_d(self, obj, format_d_desc: FormatDetailDesc):
|
||||
max_prop_length = self.get_properties_max_length(format_d_desc.properties.keys())
|
||||
res = ""
|
||||
for prop, template in format_d_desc.properties.items():
|
||||
if res:
|
||||
res += "\n"
|
||||
#value = getattr(obj, prop) if hasattr(obj, prop) else "*Undefined*"
|
||||
res += prop.ljust(max_prop_length) + ": " + self.to_string(obj, template)
|
||||
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def get_properties_max_length(properties):
|
||||
return max((len(p) for p in properties))
|
||||
|
||||
@staticmethod
|
||||
def to_string(obj, template):
|
||||
try:
|
||||
bag = obj.to_bag() if hasattr(obj, "to_bag") else obj.__dict__
|
||||
return template.format(**bag)
|
||||
except KeyError:
|
||||
return "*Undefined*"
|
||||
@@ -0,0 +1,103 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from printer.FormatInstructions import FormatInstructions, FormatDetailType
|
||||
from printer.Formatter import Formatter
|
||||
|
||||
COLORS = {
|
||||
"reset": "\u001b[0m",
|
||||
"black": "\u001b[30m",
|
||||
"red": "\u001b[31m",
|
||||
"green": "\u001b[32m",
|
||||
"yellow": "\u001b[33m",
|
||||
"blue": "\u001b[34m",
|
||||
"magenta": "\u001b[35m",
|
||||
"cyan": "\u001b[36m",
|
||||
"white": "\u001b[37m",
|
||||
}
|
||||
EXECUTION_CONTEXT_CLASS = "core.sheerka.ExecutionContext.ExecutionContext"
|
||||
|
||||
|
||||
class SheerkaPrinter:
|
||||
"""
|
||||
Class use to format the output
|
||||
"""
|
||||
|
||||
out = print
|
||||
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
self.formatter = Formatter()
|
||||
self.formatter.register_format_l(EXECUTION_CONTEXT_CLASS, "[{id:3}] %tab%{desc} ({status})")
|
||||
self.custom_concepts_printers = {
|
||||
str(BuiltinConcepts.EXPLANATION): self.print_explanation,
|
||||
str(BuiltinConcepts.RETURN_VALUE): self.print_return_value,
|
||||
}
|
||||
|
||||
def register_custom_printer(self, concept, custom_format):
|
||||
key = concept.key if isinstance(concept, Concept) else concept
|
||||
self.custom_concepts_printers[str(key)] = custom_format
|
||||
return self
|
||||
|
||||
def register_format_l(self, obj, template):
|
||||
self.formatter.register_format_l(obj, template)
|
||||
|
||||
def register_format_d(self, predicate, properties, format_type=FormatDetailType.Props_In_Line):
|
||||
self.formatter.register_format_d(predicate, properties, format_type)
|
||||
|
||||
def print(self, to_print, instructions=None):
|
||||
instructions = instructions or FormatInstructions()
|
||||
self.fp(instructions, to_print)
|
||||
|
||||
def fp(self, instructions, item):
|
||||
"""
|
||||
fp stands for format and print
|
||||
:param instructions:
|
||||
:param item:
|
||||
:return:
|
||||
"""
|
||||
if isinstance(item, (list, tuple)):
|
||||
for i in item:
|
||||
self.fp(instructions, i)
|
||||
return
|
||||
elif isinstance(item, str):
|
||||
for color in COLORS:
|
||||
item = item.replace("%" + color + "%", "" if instructions.no_color else COLORS[color])
|
||||
if "%tab%" in item:
|
||||
self.out(item.replace("%tab%", instructions.tab))
|
||||
else:
|
||||
self.out(instructions.tab + item)
|
||||
return
|
||||
|
||||
elif isinstance(item, Concept) and item.key in self.custom_concepts_printers:
|
||||
self.custom_concepts_printers[item.key](self, instructions, item)
|
||||
else:
|
||||
self.fp(instructions, self.formatter.format_l(item, instructions.format_l))
|
||||
|
||||
# print details
|
||||
format_d = self.formatter.compute_format_d(instructions.format_d)
|
||||
for format_d_desc in reversed(format_d):
|
||||
if format_d_desc.predicate.eval(item):
|
||||
self.fp(instructions, self.formatter.format_d(item, format_d_desc))
|
||||
break
|
||||
|
||||
if instructions.recursive_props:
|
||||
for k, v in instructions.recursive_props.items():
|
||||
if hasattr(item, k) and v > 0 and (value := getattr(item, k)) is not None:
|
||||
self.fp(instructions.recurse(k), value)
|
||||
|
||||
@staticmethod
|
||||
def print_explanation(printer, instructions, item):
|
||||
explanation_instructions = instructions.clone().merge(item.instructions)
|
||||
printer.fp(explanation_instructions, f"%blue%{item.digest}%reset% : {item.command}")
|
||||
printer.fp(explanation_instructions, item.body)
|
||||
|
||||
@staticmethod
|
||||
def print_return_value(printer, instructions, item):
|
||||
if printer.sheerka.isinstance(item.body, BuiltinConcepts.EXPLANATION):
|
||||
return printer.fp(instructions, item.body)
|
||||
|
||||
if isinstance(item.body, (list, tuple)):
|
||||
return printer.fp(instructions, item.body)
|
||||
|
||||
status = item.status
|
||||
return printer.fp(instructions, str(item) if status else f"%red%{item}%reset%")
|
||||
Reference in New Issue
Block a user