Fixed #131 : Implement ExprToConditions
Fixed #130 : ArithmeticOperatorParser Fixed #129 : python_wrapper : create_namespace Fixed #128 : ExpressionParser: Cannot parse func(x) infixed concept 'xxx'
This commit is contained in:
@@ -2,14 +2,16 @@ import functools
|
||||
from dataclasses import dataclass
|
||||
|
||||
import core.builtin_helpers
|
||||
from core.ast_helpers import UnreferencedVariablesVisitor
|
||||
from core.builtin_concepts import ReturnValueConcept
|
||||
from core.builtin_concepts_ids import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.global_symbols import SyaAssociativity, NotFound, NotInit, ErrorObj
|
||||
from core.concept import AllConceptParts, Concept
|
||||
from core.global_symbols import ErrorObj, NotFound, NotInit, SyaAssociativity
|
||||
from core.rule import Rule
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from core.sheerka.services.SheerkaAdmin import SheerkaAdmin
|
||||
from core.tokenizer import Token, TokenKind
|
||||
from core.utils import sheerka_hasattr, sheerka_getattr
|
||||
from core.utils import get_inner_set, sheerka_getattr, sheerka_hasattr
|
||||
from core.var_ref import VariableRef
|
||||
|
||||
TO_DISABLED = ["breakpoint", "callable", "compile", "delattr", "eval", "exec", "exit", "input", "locals", "open",
|
||||
@@ -277,3 +279,121 @@ def create_namespace(context, who, names, sheerka_names, objects, expression_onl
|
||||
result[name] = obj
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_variables_from_concept_asts(context, concept, known_variables, parameters_only=True):
|
||||
"""
|
||||
From a given concept that is already compiled,
|
||||
browse the compiled to see if there is any symbol that is unknown, eg variable
|
||||
:param context:
|
||||
:param concept:
|
||||
:param known_variables:
|
||||
:param parameters_only: only return concept variables that are also parameters
|
||||
:return:
|
||||
"""
|
||||
if not concept.get_hints().is_instance and not known_variables:
|
||||
return {}
|
||||
|
||||
core.builtin_helpers.ensure_asts(context, concept)
|
||||
variables = {}
|
||||
|
||||
for prop_name, prop_value in concept.get_compiled().items():
|
||||
if isinstance(prop_value, Concept):
|
||||
prop_value_vars = get_variables_from_concept_asts(context,
|
||||
prop_value,
|
||||
known_variables,
|
||||
parameters_only)
|
||||
inner_variables = get_inner_set(prop_value_vars)
|
||||
if inner_variables:
|
||||
variables[prop_name] = inner_variables
|
||||
else:
|
||||
return_values = [prop_value] if not isinstance(prop_value, list) else prop_value
|
||||
unreferenced_names_visitor = UnreferencedVariablesVisitor(context)
|
||||
for ret_val in [r for r in return_values if isinstance(r, ReturnValueConcept)]:
|
||||
if isinstance(ret_val.body.body, list) and len(ret_val.body.body) != 1:
|
||||
continue
|
||||
elif isinstance(ret_val.body.body, list) and len(ret_val.body.body) == 1:
|
||||
body = ret_val.body.body[0]
|
||||
else:
|
||||
body = ret_val.body.body
|
||||
|
||||
if hasattr(body, "get_python_node"):
|
||||
node = body.get_python_node()
|
||||
possibles_vars = unreferenced_names_visitor.get_names(node.ast_)
|
||||
variables_found = {v for v in possibles_vars if is_concept_variable(context,
|
||||
concept,
|
||||
v,
|
||||
prop_name,
|
||||
known_variables)}
|
||||
if variables_found:
|
||||
variables.setdefault(prop_name, set()).update(variables_found)
|
||||
|
||||
elif hasattr(body, "get_concept"):
|
||||
sub_concept = body.get_concept()
|
||||
if sub_concept.name in known_variables:
|
||||
variables.setdefault(prop_name, set()).add(sub_concept.name)
|
||||
|
||||
elif hasattr(body, "get_expr_node"):
|
||||
expr_node = body.get_expr_node()
|
||||
for compiled in expr_node.compiled:
|
||||
variables.setdefault(prop_name, set()).update(compiled.variables)
|
||||
if prop_name not in variables:
|
||||
# add an empty entry to handle cases like '__o_00__ + 1'
|
||||
# No variable is required, but the property must be computed
|
||||
variables[prop_name] = set()
|
||||
|
||||
if parameters_only:
|
||||
variables = {k: v for k, v in variables.items() if k in concept.get_metadata().parameters}
|
||||
|
||||
return variables
|
||||
|
||||
|
||||
def is_variable(context, name):
|
||||
"""
|
||||
tells whether or not the name can be a variable
|
||||
:param context:
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not name.isidentifier():
|
||||
return False
|
||||
|
||||
if context.sheerka.is_a_concept_name(name):
|
||||
return False
|
||||
|
||||
try:
|
||||
eval(name, sheerka_globals)
|
||||
except:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def is_concept_variable(context, concept, variable, current_property, known_variables):
|
||||
"""
|
||||
Tells whether or not a symbol is unknown for the concept
|
||||
ie, Can the concept be evaluated without resolving this symbol ?
|
||||
|
||||
First the variable must be a valid variable (variable name + not a concept)
|
||||
Plus it must not be the name of a concept parameter
|
||||
:param context:
|
||||
:param concept:
|
||||
:param known_variables:
|
||||
:param variable:
|
||||
:param current_property:
|
||||
:return:
|
||||
"""
|
||||
if variable in known_variables:
|
||||
# forced variable
|
||||
return True
|
||||
|
||||
if not is_variable(context, variable):
|
||||
# not a valid identifier or may be a known concept name
|
||||
return False
|
||||
|
||||
if current_property not in AllConceptParts:
|
||||
# variable referencing other variable
|
||||
return True
|
||||
|
||||
return variable not in concept.variables()
|
||||
|
||||
Reference in New Issue
Block a user