import types from core.builtin_concepts import BuiltinConcepts from core.concept import Concept from core.global_symbols import NotInit 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.custom_concepts_printers = {} self.reset() def reset(self): self.custom_concepts_printers = { str(BuiltinConcepts.EXPLANATION): self.print_explanation, str(BuiltinConcepts.RETURN_VALUE): self.print_return_value, str(BuiltinConcepts.TO_LIST): self.print_to_list, } self.formatter.reset_formats() self.formatter.register_format_l(EXECUTION_CONTEXT_CLASS, "[{id:3}] %tab%{desc} ({status})") self.formatter.register_format_l(SyntaxError, '%red%{self.__class__.__name__}: {msg}\\n{text}\\n{"^": >{offset}}%reset%') self.formatter.register_format_l(Exception, "%red%{self}%reset%") 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, obj, properties=None, format_type=FormatDetailType.Props_In_Line): self.formatter.register_format_d(obj, properties, format_type) def print(self, to_print, instructions=None): """ Print using SheerkaPrinter.out :param to_print: :param instructions: FormatInstructions :return: """ instructions = instructions or FormatInstructions() try: self.fp(instructions, to_print) except Exception as ex: self.fp(instructions, ex) return def fp(self, instructions, item): """ fp stands for format and print :param instructions: :param item: :return: """ # first, get the merged instructions instructions = self.merge_instructions(instructions, item) # We can only print string if 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 # if list or generator, print one by one elif hasattr(item, "__iter__"): for i in item: self.fp(instructions, i) return # Custom print required elif isinstance(item, Concept) and item.key in self.custom_concepts_printers: self.custom_concepts_printers[item.key](self, instructions, item) # get the format per line and print else: self.fp(instructions, self.formatter.format_l(item, instructions.format_l)) # print details format_d = self.formatter.format_d(item, instructions.format_d) if format_d: self.fp(instructions, format_d) if instructions.recursive_props: for k, v in instructions.recursive_props.items(): if hasattr(item, k) and v > 0 and (value := getattr(item, k)) != NotInit: self.fp(instructions.recurse(k), value) @staticmethod def merge_instructions(instructions, obj): """ Merge the format instruction coming from the context with the one from obj Note that if obj is not a Concept, there is not instruction to merge :param instructions: :param obj: :return: """ if not hasattr(obj, "get_format_instructions"): return instructions obj_instructions = obj.get_format_instructions() if obj_instructions is None: return instructions return instructions.clone().merge(obj_instructions) @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_to_list(printer, instructions, item): explanation_instructions = instructions.clone().merge(item.get_format_instructions()) 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 printer.sheerka.isinstance(item.body, BuiltinConcepts.TO_LIST): return printer.fp(instructions, item.body) if isinstance(item.body, (list, tuple, types.GeneratorType)): return printer.fp(instructions, item.body) status = item.status return printer.fp(instructions, str(item) if status else f"%red%{item}%reset%")