First but not optimized version of AstFormatDict

This commit is contained in:
2020-11-24 13:43:04 +01:00
parent ab30ab3345
commit cac732bd93
21 changed files with 898 additions and 217 deletions
+2
View File
@@ -105,6 +105,7 @@ class BuiltinConcepts:
# formatting
TO_LIST = "__TO_LIST"
TO_DICT = "__TO_DICT"
AllBuiltinConcepts = [v for n, v in BuiltinConcepts.__dict__.items() if not n.startswith("__")]
@@ -176,6 +177,7 @@ BuiltinContainers = [
BuiltinConcepts.FILTERED,
BuiltinConcepts.EXPLANATION,
BuiltinConcepts.TO_LIST,
BuiltinConcepts.TO_DICT,
]
"""
+16 -1
View File
@@ -113,7 +113,8 @@ class Sheerka(Concept):
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
self.sheerka_methods = {
"test": SheerkaMethod(self.test, False),
"test_using_context": SheerkaMethod(self.test_using_context, False)
"test_using_context": SheerkaMethod(self.test_using_context, False),
"test_dict": SheerkaMethod(self.test_dict, False)
}
self.sheerka_pipeables = {}
@@ -1028,6 +1029,20 @@ class Sheerka(Concept):
# uncomment the following line to enable colors
# logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
def test_dict(self):
bag2 = {
"alpha": "value4",
"beta": ["item1", "item2", "item3", ]
}
bag = {
"a": "value1",
"baba": "value2",
"c": "value1",
"de": ["item1", "item2", "item3", ],
"e": bag2
}
return self.new(BuiltinConcepts.TO_DICT, body=bag)
def to_profile():
sheerka = Sheerka()
+24
View File
@@ -3,6 +3,7 @@ import time
from os import path
from core.builtin_concepts import BuiltinConcepts, BuiltinContainers
from core.builtin_helpers import ensure_concept
from core.concept import Concept
from core.sheerka.services.sheerka_service import BaseService
@@ -22,6 +23,7 @@ class SheerkaAdmin(BaseService):
self.sheerka.bind_service_method(self.cache, False)
self.sheerka.bind_service_method(self.restore, True)
self.sheerka.bind_service_method(self.concepts, False)
self.sheerka.bind_service_method(self.desc, False)
self.sheerka.bind_service_method(self.last_created_concept, False)
self.sheerka.bind_service_method(self.last_ret, False)
self.sheerka.bind_service_method(self.last_error_ret, False)
@@ -122,6 +124,28 @@ class SheerkaAdmin(BaseService):
def concepts(self):
return self.sheerka.new(BuiltinConcepts.TO_LIST, body=self.sheerka.sdp.list(self.sheerka.CONCEPTS_BY_ID_ENTRY))
def desc(self, *concepts):
ensure_concept(*concepts)
res = []
for c in concepts:
bag = {
"id": c.id,
"name": c.name,
"key": c.key,
"definition": c.get_metadata().definition,
"type": c.get_metadata().definition_type,
"body": c.get_metadata().body,
"where": c.get_metadata().where,
"pre": c.get_metadata().pre,
"post": c.get_metadata().post,
"ret": c.get_metadata().ret,
"vars": c.get_metadata().variables,
"props": c.get_metadata().props,
}
res.append(self.sheerka.new(BuiltinConcepts.TO_DICT, body=bag))
return res[0] if len(res) == 1 else self.sheerka.new(BuiltinConcepts.TO_LIST, body=res)
def format_rules(self):
return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules())
@@ -34,13 +34,16 @@ class BaseDebugLogger:
def debug_entering(self, **kwargs):
pass
def debug_var(self, name, value, is_error=False):
def debug_var(self, name, value, is_error=False, hint=None):
pass
def debug_rule(self, rule, results):
pass
def debug_log(self, text):
def debug_log(self, text, is_error=False):
pass
def is_enabled(self):
pass
@@ -48,6 +51,9 @@ class NullDebugLogger(BaseDebugLogger):
def __init__(self):
pass
def is_enabled(self):
return False
class ConsoleDebugLogger(BaseDebugLogger):
@@ -60,6 +66,9 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_id = debug_id
self.is_highlighted = ""
def is_enabled(self):
return True
def debug_entering(self, **kwargs):
super().debug_entering(**kwargs)
@@ -71,17 +80,18 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars)
def debug_var(self, name, value, is_error=False):
enabled = self.debug_manager.compute_var_debug(self.service_name,
self.method_name,
self.context_id,
name,
self.context_id)
def debug_var(self, name, value, is_error=False, hint=None):
enabled = is_error or self.debug_manager.compute_var_debug(self.service_name,
self.method_name,
self.context_id,
name,
self.context_id)
if enabled == False:
return
color = 'red' if is_error else 'green'
str_text = f"{CCM[color]}..{name}={CCM['reset']}"
hint_str = f"({hint})" if hint is not None else ""
str_text = f"{CCM[color]}..{name}{hint_str}={CCM['reset']}"
str_vars = "" if isinstance(enabled, str) else pp.pformat(value)
if "\n" not in str(str_vars):
self.debug_manager.debug(self.prefix() + str_text + str_vars)
@@ -101,8 +111,9 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars)
def debug_log(self, text):
self.debug_manager.debug(self.prefix() + f"{CCM['blue']}..{text}{CCM['reset']}")
def debug_log(self, text, is_error=False):
color = 'red' if is_error else 'blue'
self.debug_manager.debug(self.prefix() + f"{CCM[color]}..{text}{CCM['reset']}")
def prefix(self):
return f"[{self.context_id:2}][{self.debug_id:2}] {self.is_highlighted}"
@@ -271,12 +282,8 @@ class SheerkaDebugManager(BaseService):
res = {}
for prop in props:
res[prop] = evaluate_expression(prop, bag)
# res = {
# "return_values": to_inspect.values.get("return_values", None)
# }
pp.pprint(res)
return None
return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
def debug(self, *args, **kwargs):
print(*args, **kwargs)
@@ -437,4 +444,4 @@ class SheerkaDebugManager(BaseService):
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def get_debug_settings(self):
return self.debug_vars_settings
return self.sheerka.new(BuiltinConcepts.TO_LIST, body=self.debug_vars_settings)
+1 -1
View File
@@ -21,7 +21,7 @@ class SheerkaDump(BaseService):
super().__init__(sheerka)
def initialize(self):
self.sheerka.bind_service_method(self.dump_desc, True, "desc") # has_side_effect 'cause concept is evaluated
self.sheerka.bind_service_method(self.dump_desc, True, "old_desc") # has_side_effect 'cause concept is evaluated
self.sheerka.bind_service_method(self.dump_sdp, False, "dump_sdp")
def dump_desc(self, *concept_names, eval=False):
+4 -6
View File
@@ -16,7 +16,8 @@ class SheerkaOut(BaseService):
self.sheerka.bind_service_method(self.process_return_values, False)
def create_out_tree(self, context, obj):
return self.create_out_tree_recursive(context, {'__obj': obj}, DeveloperVisitor(self, set(), 0))
debugger = context.get_debugger("Out.visitor", "create_out_tree")
return self.create_out_tree_recursive(context, {'__obj': obj}, DeveloperVisitor(self, debugger, set(), 0))
def create_out_tree_recursive(self, context, bag, visitor):
debugger = context.get_debugger(SheerkaOut.NAME, "create_out_tree")
@@ -40,8 +41,7 @@ class SheerkaOut(BaseService):
visitor.already_seen.add(rule.id)
bag.update(as_bag(current_obj)) # update with the current obj attributes
visitor.visit(context, rule.compiled_action, bag)
res = visitor.get_result()
res = visitor.visit(context, rule.compiled_action, bag)
if res is None:
debugger.debug_log(f"No matching rule.")
@@ -67,9 +67,7 @@ class SheerkaOut(BaseService):
if out_tree:
for visitor in self.out_visitors:
visitor.visit(context, out_tree, None)
if hasattr(visitor, "finalize"):
visitor.finalize()
visitor.visit(out_tree)
return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
return self.sheerka.ret(self.NAME, False, self.sheerka.new(BuiltinConcepts.NO_RESULT))
+106 -26
View File
@@ -63,6 +63,15 @@ class FormatAstNode:
return ", ".join(repr(item) for item in items)
def clone(self, instance, props, **kwargs):
for prop_name in props:
setattr(instance, prop_name, getattr(self, prop_name))
for k, v in kwargs.items():
setattr(instance, k, v)
return instance
@dataclass
class FormatAstRawText(FormatAstNode):
@@ -73,9 +82,15 @@ class FormatAstRawText(FormatAstNode):
class FormatAstVariable(FormatAstNode):
name: str
format: Union[str, None] = None
debug: bool = False
value: object = None
index: object = None
def clone(self, **kwargs):
return super().clone(FormatAstVariable(self.name),
("format", "debug", "value", "index"),
**kwargs)
@dataclass
class FormatAstVariableNotFound(FormatAstNode):
@@ -93,29 +108,50 @@ class FormatAstList(FormatAstNode):
items_prop: str = None # where to search the list if variable does not resolve to an iterable
recurse_on: str = None
recursion_depth: int = 0
debug: bool = False
prefix: str = None
suffix: str = None
show_index: bool = False
items: object = None
def clone(self, **kwargs):
return super().clone(
FormatAstList(self.variable),
("items_prop", "recurse_on", "recursion_depth", "debug", "prefix", "suffix", "show_index", "items"),
**kwargs)
@dataclass
class FormatAstDict(FormatAstNode):
variable: str
items_prop: str = None # where to search the dict if variable does not resolve to an iterable
debug: bool = False
prefix: str = None
suffix: str = None
items: object = None
def clone(self, **kwargs):
return super().clone(
FormatAstDict(self.variable),
("items_prop", "debug", "prefix", "suffix", "items"),
**kwargs)
@dataclass
class FormatAstColor(FormatAstNode):
def __init__(self, color, format_ast):
self.color = color
self.format_ast = format_ast
color: str
format_ast: FormatAstNode
def __repr__(self):
return f"{self.color}({self.format_ast})"
def __eq__(self, other):
if id(self) == id(other):
return True
if not isinstance(other, FormatAstColor):
return False
return self.color == other.color and self.format_ast == other.format_ast
def __hash__(self):
return hash((self.color, self.format_ast))
def clone(self, **kwargs):
return super().clone(
FormatAstColor(self.color, self.format_ast),
(),
**kwargs)
@dataclass
@@ -125,21 +161,19 @@ class FormatAstFunction(FormatAstNode):
kwargs: dict = None
@dataclass
class FormatAstSequence(FormatAstNode):
def __init__(self, items):
self.items = items
items: list
debug: bool = False
def __repr__(self):
return "FormatAstSequence(" + self.repr_value(self.items) + ")"
def __eq__(self, other):
if id(self) == id(other):
return True
if not isinstance(other, FormatAstSequence):
return False
return self.items == other.items
def clone(self, **kwargs):
return super().clone(
FormatAstSequence(self.items),
("debug",),
**kwargs)
class FormatRuleParser(IterParser):
@@ -170,6 +204,9 @@ class FormatRuleParser(IterParser):
if value[0] in ("'", '"'):
return value[1:-1]
if value in ("True", "False"):
return bool(value)
try:
return int(value)
except ValueError:
@@ -278,6 +315,8 @@ class FormatRuleParser(IterParser):
return self.return_color(func_name.value, args, kwargs)
elif func_name.value == "list":
return self.return_list(args, kwargs)
elif func_name.value == "dict":
return self.return_dict(args, kwargs)
return FormatAstFunction(func_name.value, self.to_text(args), self.to_text(kwargs))
@@ -347,13 +386,19 @@ class FormatRuleParser(IterParser):
return FormatAstColor(color, FormatAstVariable(variable, vformat))
def return_list(self, args, kwargs):
"""
Looking for variable_name, [recurse_on], [recursion_depth], [items_prop]
:param args:
:param kwargs:
:return:
"""
len_args = len(args)
if len_args < 1:
self.error_sink = FormatRuleSyntaxError("variable name not found", None)
return None
if len_args > 3:
self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[3][0])
if len_args > 4:
self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[4][0])
return None
variable_name = get_text_from_tokens(args[0])
@@ -364,6 +409,10 @@ class FormatRuleParser(IterParser):
elif len_args == 3:
recursion_depth = self.to_value(args[1])
recurse_on = self.to_value(args[2])
elif len_args == 4:
recursion_depth = self.to_value(args[1])
recurse_on = self.to_value(args[2])
items_prop = self.to_value(args[3])
if "recurse_on" in kwargs:
recurse_on = self.to_value(kwargs["recurse_on"])
@@ -383,6 +432,34 @@ class FormatRuleParser(IterParser):
return FormatAstList(variable_name, items_prop, recurse_on, recursion_depth)
def return_dict(self, args, kwargs):
len_args = len(args)
if len_args < 1:
self.error_sink = FormatRuleSyntaxError("variable name not found", None)
return None
if len_args > 1:
self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[1][0])
return None
variable_name = get_text_from_tokens(args[0])
kwargs_parameters = {}
for prop in ("items_prop", "prefix", "suffix", "debug"):
if prop in kwargs:
kwargs_parameters[prop] = self.to_value(kwargs[prop])
if "debug" in kwargs_parameters:
if "prefix" not in kwargs_parameters:
kwargs_parameters["prefix"] = "{"
if "suffix" not in kwargs_parameters:
kwargs_parameters["suffix"] = "}"
if self.error_sink:
return None
return FormatAstDict(variable_name, **kwargs_parameters)
@dataclass()
class RulePredicate:
@@ -556,6 +633,9 @@ class SheerkaRuleManager(BaseService):
Rule("print", "Display formatted list",
"isinstance(__ret_container, BuiltinConcepts.TO_LIST)",
"list(__ret_container)"),
Rule("print", "Display formatted dict",
"isinstance(__ret_container, BuiltinConcepts.TO_DICT)",
"dict(__ret_container)"),
]
for r in rules:
+10 -1
View File
@@ -2,11 +2,11 @@ import ast
import importlib
import inspect
import pkgutil
import re
from cache.Cache import Cache
from core.ast_helpers import ast_to_props
from core.tokenizer import TokenKind, Tokenizer
from pyparsing import *
default_debug_name = "*default*"
debug_activated = set()
@@ -38,6 +38,15 @@ PRIMITIVES_TYPES = (str, bool, type(None), int, float, list, dict, set, bytes, t
expressions_cache = Cache()
ESC = Literal('\x1b')
integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer, ';')) +
oneOf(list(alphas)))
def no_color_str(text):
return Suppress(escapeSeq).transformString(str(text))
def my_debug(*args, check_started=None):
"""
+5
View File
@@ -129,12 +129,14 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
This function can only be a draft, as there may be tons of different situations
I guess that it can only be complete when will we have access to Sheerka memory
"""
debugger = context.get_debugger(DefConceptEvaluator.NAME, "get_variables")
#
# Case of NameNode
#
if isinstance(ret_value, NameNode):
names = [str(t.value) for t in ret_value.tokens if t.type in (
TokenKind.IDENTIFIER, TokenKind.STRING, TokenKind.KEYWORD)]
debugger.debug_var("names", names, hint="from NameNode")
return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names))
#
@@ -143,6 +145,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, ParsingExpression):
visitor = ConceptOrRuleNameVisitor()
visitor.visit(ret_value.value.value)
debugger.debug_var("names", visitor.names, hint="from BNF")
return set(visitor.names)
#
@@ -152,6 +155,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
if len(concept_name) > 1:
visitor = UnreferencedVariablesVisitor(context)
names = visitor.get_names(python_node.ast_)
debugger.debug_var("names", names, hint="from python node")
return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names))
else:
return set()
@@ -168,6 +172,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
for identifier in [i for i in concept_name if str(i).isalnum()]:
if identifier in tokens:
variables.add(identifier)
debugger.debug_var("names", variables, hint="from concept")
return variables
return set()
+9 -3
View File
@@ -92,6 +92,9 @@ class PythonEvaluator(OneReturnValueEvaluator):
debugger = context.get_debugger(PythonEvaluator.NAME, "eval")
debugger.debug_entering(node=node)
exception_debugger = context.get_debugger("Exceptions", PythonEvaluator.NAME + ".eval")
get_trace_back = context.debug_enabled or exception_debugger.is_enabled()
context.log(f"Evaluating python node {node}.", self.name)
# If we evaluate a Concept metadata which is NOT the body ex (pre, post, where...)
@@ -136,9 +139,12 @@ class PythonEvaluator(OneReturnValueEvaluator):
except Exception as ex:
if concepts_entries is None:
concepts_entries = self.get_concepts_entries_from_globals(my_globals)
errors.append(PythonEvalError(ex,
traceback.format_exc() if context.debug_enabled else None,
self.get_concepts_values_from_globals(globals_, concepts_entries)))
eval_error = PythonEvalError(ex,
traceback.format_exc() if get_trace_back else None,
self.get_concepts_values_from_globals(globals_, concepts_entries))
errors.append(eval_error)
exception_debugger.debug_var("exception", eval_error.error, is_error=True)
exception_debugger.debug_var("trace", eval_error.traceback, is_error=True)
if evaluated == NotInit:
if len(errors) == 1:
+13 -6
View File
@@ -1,7 +1,8 @@
from IPython.core.display import JSON
from core.sheerka.Sheerka import Sheerka
from ipykernel.ipkernel import IPythonKernel
from ipykernel.kernelapp import IPKernelApp
from sheerkapickle import encode
from sheerkapickle.SheerkaPickler import SheerkaPickler
class SheerkaKernel(IPythonKernel):
@@ -25,12 +26,19 @@ class SheerkaKernel(IPythonKernel):
def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
result = self.sheerka.evaluate_user_input(code)
# md = Markdown(f'<span style="color: {hexcolor}">{input_data}</span>')
as_json = JSON(SheerkaPickler(self.sheerka).flatten(result))
a = {
"a": "value1",
"b": "value2",
}
if not silent:
display_data_content = {
'data': {
'text/plain': str(result),
'application/json': encode(self.sheerka, result)
# 'text/plain': str(result),
# "text/markdown": md._repr_markdown_(),
'application/json': JSON(a)._repr_json_(),
},
'metadata': {
'application/json': {'expanded': True}
@@ -38,9 +46,8 @@ class SheerkaKernel(IPythonKernel):
'execution_count': self.sheerka.execution_count,
}
stream_content = {'name': 'stdout', 'text': display_data_content}
# self.send_response(self.iopub_socket, "stream", stream_content)
self.send_response(self.iopub_socket, "execute_result", display_data_content)
self.send_response(self.iopub_socket, "display_data", display_data_content)
return {'status': 'ok',
# The base class increments the execution count
+93
View File
@@ -0,0 +1,93 @@
import re
from core.sheerka.services.SheerkaRuleManager import FormatAstNode
from core.utils import CONSOLE_COLORS_MAP as CCM, no_color_str
from out.OutVisitor import OutVisitor
get_start = re.compile(r"^([\(\{\[]\w*)")
class AsStrVisitor(OutVisitor):
def __init__(self, expand=False):
self.expand = expand
def visit_FormatAstRawText(self, format_ast):
return str(format_ast.text)
def visit_FormatAstVariable(self, format_ast):
if isinstance(format_ast.value, FormatAstNode):
value = self.visit(format_ast.value)
else:
value = format_ast.value
if format_ast.debug and isinstance(value, str):
value = "'" + str(value).translate(str.maketrans({"'": r"\'"})) + "'"
return str(value)
def visit_FormatAstVariableNotFound(self, format_ast):
return CCM["red"] + format_ast.name + CCM["reset"]
def visit_FormatAstColor(self, format_ast):
return CCM[format_ast.color] + self.visit(format_ast.format_ast) + CCM["reset"]
def visit_FormatAstSequence(self, format_ast):
return "".join([self.visit(item) for item in format_ast.items])
def visit_FormatAstList(self, format_ast):
first = True
result = ""
if format_ast.prefix:
result += format_ast.prefix
sep = ",\n" if self.expand else ", " if format_ast.debug else "\n"
for item in format_ast.items:
if not first:
result += sep
if format_ast.show_index:
result += f"[{item.index}] "
result += self.visit(item)
first = False
if format_ast.suffix:
result += format_ast.suffix
return result
def visit_FormatAstDict(self, format_ast):
first = True
result = ""
keys_values = []
max_len = 0
for k, v in format_ast.items:
value = self.visit(k)
len_value = len(no_color_str(value))
if len_value > max_len:
max_len = len_value
keys_values.append(value)
if format_ast.prefix:
result += format_ast.prefix
sep = ",\n" if self.expand else ", " if format_ast.debug else "\n"
for i, (k, v) in enumerate(format_ast.items):
start = "" if first else sep
key = f"{keys_values[i]:<{max_len}}" if (self.expand or not format_ast.debug) else keys_values[i]
colon = ": "
indent = len(no_color_str(key)) + len(colon)
value = self.visit(v)
if self.expand:
if m := get_start.match(no_color_str(value)):
indent += len(m.group(1))
value = value.replace("\n", "\n" + " " * indent)
first = False
result += start + key + colon + value
if format_ast.suffix:
result += format_ast.suffix
return result
+28 -40
View File
@@ -1,55 +1,43 @@
from out.OutVisitor import OutVisitor
from core.sheerka.services.SheerkaRuleManager import FormatAstNode
from out.AsStrVisitor import AsStrVisitor
class ConsoleVisitor(OutVisitor):
class ConsoleVisitor(AsStrVisitor):
"""
Prints to the console
"""
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",
}
def __init__(self):
def __init__(self, expand_mode="auto"):
super().__init__()
self.out = print
self.expand_mode = expand_mode
def finalize(self):
self.out("")
def visit_FormatAstRawText(self, format_ast):
self.out(super().visit_FormatAstRawText(format_ast))
def visit_FormatAstRawText(self, context, format_ast, bag):
self.out(format_ast.text, end="")
def visit_FormatAstVariable(self, format_ast):
self.out(super().visit_FormatAstVariable(format_ast))
def visit_FormatAstVariable(self, context, format_ast, bag):
if isinstance(format_ast.value, FormatAstNode):
self.visit(context, format_ast.value, bag)
return
self.out(format_ast.value, end="")
def visit_FormatAstVariableNotFound(self, format_ast):
self.out(super().visit_FormatAstVariableNotFound(format_ast))
def visit_FormatAstVariableNotFound(self, context, format_ast, bag):
self.out(self.COLORS["red"] + format_ast.name + self.COLORS["reset"], end="")
def visit_FormatAstColor(self, format_ast):
self.out(super().visit_FormatAstColor(format_ast))
def visit_FormatAstSequence(self, context, format_ast, bag):
for item in format_ast.items:
self.visit(context, item, bag)
def visit_FormatAstSequence(self, format_ast):
visitor = AsStrVisitor()
self.out(visitor.visit_FormatAstSequence(format_ast))
def visit_FormatAstList(self, context, format_ast, bag):
first = True
for item in format_ast.items:
if not first:
self.out("") # print new line
self.visit(context, item, bag)
first = False
def visit_FormatAstList(self, format_ast):
visitor = AsStrVisitor()
res = visitor.visit_FormatAstList(format_ast)
self.out(res)
def visit_FormatAstColor(self, context, format_ast, bag):
self.out(self.COLORS[format_ast.color], end="")
self.visit(context, format_ast.format_ast, bag)
self.out(self.COLORS["reset"], end="")
def visit_FormatAstDict(self, format_ast):
if self.expand_mode == "always":
expand = True
else:
expand = False
visitor = AsStrVisitor(expand)
res = visitor.visit_FormatAstDict(format_ast)
self.out(res)
+101 -46
View File
@@ -1,31 +1,43 @@
from core.sheerka.services.SheerkaRuleManager import FormatAstVariable, FormatAstVariableNotFound, FormatAstSequence, \
FormatAstColor, FormatAstList, FormatAstRawText
from core.sheerka.services.SheerkaRuleManager import FormatAstVariable, FormatAstVariableNotFound, FormatAstColor, \
FormatAstList, FormatAstRawText, FormatAstDict
from core.utils import evaluate_expression, as_bag
from out.OutVisitor import OutVisitor
fstring = compile('f"{value:{format}}"', "DeveloperVisitor.fstring", mode="eval")
class DeveloperVisitor(OutVisitor):
class DeveloperVisitor:
"""
This visitor is used to resolve all the variables as well as all the lists
Once completed, it will be passed to the ConsoleVisitor for console print
"""
def __init__(self, sheerka_out, already_seen, list_recursion_depth):
def __init__(self, sheerka_out, debugger, already_seen, list_recursion_depth):
self._result = None
self.sheerka_out = sheerka_out
self.debugger = debugger
self.already_seen = already_seen
self.list_recursion_depth = list_recursion_depth
def visit(self, context, format_ast, bag):
name = format_ast.__class__.__name__
method = 'visit_' + name
visit_method = getattr(self, method, self.generic_visit)
return visit_method(context, format_ast, bag)
def generic_visit(self, context, format_ast, bag):
pass
def visit_FormatAstRawText(self, context, format_ast, bag):
if context.debug_enabled:
context.debug_entering("DeveloperVisitor", "visit_FormatAstRawText", format_ast=format_ast, bag=bag)
return self.set_result(format_ast)
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstRawText with {debug_bag}")
return format_ast
def visit_FormatAstVariable(self, context, format_ast, bag):
if context.debug_enabled:
context.debug_entering("DeveloperVisitor", "visit_FormatAstVariable", format_ast=format_ast, bag=bag)
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstVariable with {debug_bag}")
try:
value = evaluate_expression(format_ast.name, bag)
@@ -33,6 +45,8 @@ class DeveloperVisitor(OutVisitor):
"__obj": value,
format_ast.name: value
}
# create a bag entry with the last part of the variable
# eg: for __ret.status, creates an entry status
try:
index = format_ast.name.rindex(".")
sub_bag[format_ast.name[index + 1:]] = value
@@ -43,32 +57,31 @@ class DeveloperVisitor(OutVisitor):
if format_ast.format:
res = eval(fstring, {"value": res, "format": format_ast.format})
return self.set_result(FormatAstVariable(format_ast.name,
format_ast.format,
res,
format_ast.index))
return format_ast.clone(value=res)
except NameError as error:
context.debug("DeveloperVisitor", "visit_FormatAstList", "evaluate_expression", error, is_error=True)
return self.set_result(FormatAstVariableNotFound(format_ast.name))
return FormatAstVariableNotFound(format_ast.name)
def visit_FormatAstSequence(self, context, format_ast, bag):
if context.debug_enabled:
context.debug_entering("DeveloperVisitor", "visit_FormatAstSequence", format_ast=format_ast, bag=bag)
return self.set_result(FormatAstSequence([self.visit(context, item, bag) for item in format_ast.items]))
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstSequence with {debug_bag}")
return format_ast.clone(items=[self.visit(context, item, bag) for item in format_ast.items])
def visit_FormatAstColor(self, context, format_ast, bag):
if context.debug_enabled:
context.debug_entering("DeveloperVisitor", "visit_FormatAstColor", format_ast=format_ast, bag=bag)
return self.set_result(FormatAstColor(format_ast.color, self.visit(context, format_ast.format_ast, bag)))
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstColor with {debug_bag}")
return format_ast.clone(format_ast=self.visit(context, format_ast.format_ast, bag))
def visit_FormatAstList(self, context, format_ast, bag):
if context.debug_enabled:
context.debug_entering("DeveloperVisitor", "visit_FormatAstList", format_ast=format_ast, bag=bag)
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstList with {debug_bag}")
try:
value = evaluate_expression(format_ast.variable, bag)
if value is None:
return self.set_result(FormatAstVariable(format_ast.variable, format_ast.format, None))
return FormatAstVariable(format_ast.variable, format_ast.format, value=None)
if hasattr(value, "__iter__"):
items = value
@@ -78,22 +91,29 @@ class DeveloperVisitor(OutVisitor):
items = evaluate_expression(f"self.{items_props}", {"self": value})
if not hasattr(items, "__iter__"):
# Definition error ? No list found
return self.set_result(FormatAstVariable(format_ast.variable, None, value))
return FormatAstVariable(format_ast.variable, value=value)
recursion_depth, recurse_on = self.get_recurse_info(value, format_ast.recursion_depth, format_ast.recurse_on)
recursion_depth, recurse_on = self.get_recurse_info(value, format_ast.recursion_depth,
format_ast.recurse_on)
result = [] # TODO change into generator
for i, item in enumerate(items):
bag["__item"] = item
sub_visitor = DeveloperVisitor(self.sheerka_out, set(), self.list_recursion_depth)
result.append(sub_visitor.visit(context, FormatAstVariable("__item", None, item, i), bag))
sub_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
result.append(sub_visitor.visit(context, FormatAstVariable("__item",
debug=format_ast.debug,
value=item,
index=i), bag))
# recursion management
recursion_depth, recurse_on = self.get_recurse_info(item, recursion_depth, recurse_on)
if recursion_depth > 0:
sub_items = evaluate_expression(recurse_on, as_bag(item))
if sub_items and hasattr(sub_items, "__iter__"):
sub_visitor = DeveloperVisitor(self.sheerka_out, set(), self.list_recursion_depth + 1)
sub_visitor = DeveloperVisitor(self.sheerka_out,
self.debugger,
set(),
self.list_recursion_depth + 1)
bag[f"__{recurse_on}"] = sub_items
sub_items = sub_visitor.visit(context, FormatAstList(f"__{recurse_on}",
@@ -102,29 +122,64 @@ class DeveloperVisitor(OutVisitor):
recursion_depth - 1), bag)
result.append(sub_items)
return self.set_result(FormatAstList(variable=format_ast.variable,
items_prop=format_ast.items_prop,
recurse_on=recurse_on,
recursion_depth=recursion_depth,
items=result))
return format_ast.clone(recurse_on=recurse_on, recursion_depth=recursion_depth, items=result)
except NameError as error:
context.debug("DeveloperVisitor", "visit_FormatAstList", "evaluate_expression", error, is_error=True)
self.debugger.debug_log(error, is_error=True)
var_name = format_ast.variable if error.args[0] == format_ast.variable else \
format_ast.variable + "." + error.args[0]
return self.set_result(FormatAstVariableNotFound(var_name))
return FormatAstVariableNotFound(var_name)
def visit_FormatAstFunction(self, context, format_ast, bag):
if context.debug_enabled:
context.debug_entering("DeveloperVisitor", "visit_FormatAstFunction", format_ast=format_ast, bag=bag)
unknown = FormatAstColor("red", FormatAstRawText(f"function '{format_ast.name}' is unknown"))
return self.set_result(unknown)
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstFunction with {debug_bag}")
return FormatAstColor("red", FormatAstRawText(f"function '{format_ast.name}' is unknown"))
def set_result(self, result):
self._result = result
return result
def visit_FormatAstDict(self, context, format_ast, bag):
if self.debugger.is_enabled():
debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstDict with {debug_bag}")
def get_result(self):
return self._result
try:
result = []
value = evaluate_expression(format_ast.variable, bag)
if value is None:
return FormatAstVariable(format_ast.variable, format_ast.format, value=None)
if isinstance(value, dict):
items = value
else:
# the variable does not resolve to a dictionary, let's look at one of its attribute
items_props = format_ast.items_prop or "body"
items = evaluate_expression(f"self.{items_props}", {"self": value})
if not isinstance(items, dict):
# Definition error ? No Dictionary found
return FormatAstVariable(format_ast.variable, value=value)
for i, (k, v) in enumerate(items.items()):
bag["__key"] = k
key_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
key_res = key_visitor.visit(context,
FormatAstVariable("__key", value=k, index=i, debug=format_ast.debug),
bag)
bag["__value"] = v
value_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
to_visit = FormatAstDict("__value", debug=True, prefix='{', suffix='}') if isinstance(v, dict) else \
FormatAstList("__value", debug=True, prefix='[', suffix=']') if isinstance(v, list) else \
FormatAstVariable("__value", value=v, index=k, debug=format_ast.debug)
value_res = value_visitor.visit(context, to_visit, bag)
result.append((key_res, value_res))
return format_ast.clone(items=result)
except NameError as error:
self.debugger.debug_log(error, is_error=True)
var_name = format_ast.variable if error.args[0] == format_ast.variable else \
format_ast.variable + "." + error.args[0]
return FormatAstVariableNotFound(var_name)
@staticmethod
def get_recurse_info(obj, recursion_depth, recurse_on):
+3 -3
View File
@@ -1,10 +1,10 @@
class OutVisitor:
def visit(self, context, format_ast, bag):
def visit(self, format_ast):
name = format_ast.__class__.__name__
method = 'visit_' + name
visit_method = getattr(self, method, self.generic_visit)
return visit_method(context, format_ast, bag)
return visit_method(format_ast)
def generic_visit(self, context, format_ast, bag):
def generic_visit(self, format_ast):
pass
+11 -4
View File
@@ -37,10 +37,15 @@ class PythonNode(Node):
def __init__(self, source, ast_=None, objects=None):
self.source = source
self.ast_ = ast_ if ast_ else ast.parse(source, mode="eval") if source else None
self.ast_ = ast_ # if ast_ else ast.parse(source, mode="eval") if source else None
self.objects = objects or {} # when objects (mainly concepts or rules) are recognized in the expression
self.compiled = None
def init_ast(self):
if self.ast_ is None and self.source:
self.ast_ = ast.parse(self.source, mode="eval")
return self
def get_compiled(self):
if self.compiled is None:
self.compiled = compile(self.ast_, "<string>", "eval")
@@ -60,10 +65,12 @@ class PythonNode(Node):
if self.source != other.source:
return False
self_dump = self.get_dump(self.ast_)
other_dump = self.get_dump(other.ast_)
if self.ast_ and other.ast_:
self_dump = self.get_dump(self.ast_)
other_dump = self.get_dump(other.ast_)
return self_dump == other_dump
return self_dump == other_dump
return True
def __hash__(self):
return hash((self.source, self.ast_.hash))