Fixed #55 : DefConceptParser: failed to recognize concept
Fixed #62 : DefConceptParser: parsing error Fixed #64 : DefConceptParser: Failed to parse when too many concept keyword Fixed #65 : DefConceptParser : Add auto_eval keyword Fixed #66 : DefConceptParser : Add def_var keyword Fixed #67 : Add get_errors()
This commit is contained in:
@@ -14,7 +14,12 @@ from parsers.PythonParser import get_python_node
|
||||
|
||||
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class MandatoryVariable:
|
||||
class ConceptEvaluatorVariable:
|
||||
name: str
|
||||
|
||||
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class MandatoryVariable(ConceptEvaluatorVariable):
|
||||
"""
|
||||
When we are searching for variables, we are searching for potential variable
|
||||
So if the variable found has no match in the concept definition, it's not a problem
|
||||
@@ -24,19 +29,51 @@ class MandatoryVariable:
|
||||
|
||||
But there are cases where the variable found must exist, otherwise, it's an error
|
||||
example:
|
||||
def concept foo from bnf xxx
|
||||
'xxx' is detected as a variable (assuming that there is no concept named 'xxx' and a match must be
|
||||
found in the the name of the variable
|
||||
def concept foo from bnf unknown_concept
|
||||
'unknown_concept' will be detected and considered as a variable . But it is not, as it's not
|
||||
declared in the name of the concept.
|
||||
|
||||
To distinguish between mandatory and not mandatory variable, we use MandatoryVariable
|
||||
We return MandatoryVariable (instead of a variable name) to let the evaluator know that if the variable is not
|
||||
declared in the name of the concept, it's an error
|
||||
"""
|
||||
name: str
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("MandatoryVariable", self.name))
|
||||
|
||||
|
||||
class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class PossibleVariable(ConceptEvaluatorVariable):
|
||||
"""
|
||||
When a name/identifier is found in a concept part (pre, post, where, body...)
|
||||
It is considered as a possible variable. It will only added as a variable if the exact name / identifier
|
||||
is found in the name of the concept
|
||||
|
||||
example:
|
||||
def concept a plus b as a + b
|
||||
'a' and 'b' are found in the body and thy also exist in the name of the concept
|
||||
-> They will be added as variable
|
||||
"""
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("PossibleVariable", self.name))
|
||||
|
||||
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class CertainVariable(ConceptEvaluatorVariable):
|
||||
"""
|
||||
A certain variable will be added as a variable regardless of a possible match in the name
|
||||
|
||||
example:
|
||||
def concept number
|
||||
def concept plus from bnf number=n1 plus number=n2
|
||||
'n1' and 'n2' do not appear in the name of the concept, but they are variables
|
||||
"""
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("PossibleVariable", self.name))
|
||||
|
||||
|
||||
class ConceptOrRuleVariableVisitor(ParsingExpressionVisitor):
|
||||
"""
|
||||
Gets the concepts referenced by BNF
|
||||
If a rule_name is given, it will also be considered as a potential property
|
||||
@@ -44,22 +81,22 @@ class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.names = set()
|
||||
self.variables = []
|
||||
|
||||
def visit_ConceptExpression(self, node):
|
||||
if node.rule_name:
|
||||
self.names.add(node.rule_name)
|
||||
self.variables.append(CertainVariable(node.rule_name))
|
||||
elif isinstance(node.concept, Concept):
|
||||
self.names.add(node.concept.name)
|
||||
self.variables.append(CertainVariable(node.concept.name))
|
||||
else:
|
||||
self.names.add(node.concept)
|
||||
self.variables.append(CertainVariable(node.concept))
|
||||
|
||||
def visit_VariableExpression(self, node):
|
||||
self.names.add(MandatoryVariable(node.rule_name))
|
||||
self.variables.append(MandatoryVariable(node.rule_name))
|
||||
|
||||
def visit_all(self, node):
|
||||
if node.rule_name:
|
||||
self.names.add(node.rule_name)
|
||||
self.variables.append(CertainVariable(node.rule_name))
|
||||
|
||||
|
||||
class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
@@ -89,12 +126,18 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
# validate the node
|
||||
variables_found = set()
|
||||
mandatory_variables = set() # these variable MUST have a match in the name (if the name is not None)
|
||||
certain_variables = []
|
||||
skip_variables_resolution = False
|
||||
|
||||
concept = Concept(str(def_concept_node.name))
|
||||
concept.get_metadata().definition_type = def_concept_node.definition_type
|
||||
name_to_use = self.get_name_to_use(def_concept_node)
|
||||
|
||||
# get variables
|
||||
if def_concept_node.variables != NotInit:
|
||||
certain_variables = def_concept_node.variables.copy()
|
||||
skip_variables_resolution = True
|
||||
|
||||
# get variables and set the sources
|
||||
for prop in ("definition", "where", "pre", "post", "body", "ret"):
|
||||
|
||||
part_ret_val = getattr(def_concept_node, prop)
|
||||
@@ -111,26 +154,29 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
raise Exception("Unexpected")
|
||||
setattr(concept.get_metadata(), prop, source)
|
||||
|
||||
if skip_variables_resolution:
|
||||
continue
|
||||
|
||||
# Do not try to resolve variables from itself
|
||||
if prop == "definition" and concept.get_metadata().definition_type == DEFINITION_TYPE_DEF:
|
||||
continue
|
||||
|
||||
# try to find what can be a property
|
||||
for p in self.get_variables(context, part_ret_val, name_to_use):
|
||||
variables_found.add(p.name)
|
||||
if isinstance(p, MandatoryVariable):
|
||||
variables_found.add(p.name)
|
||||
mandatory_variables.add(p.name)
|
||||
else:
|
||||
variables_found.add(p)
|
||||
elif isinstance(p, CertainVariable):
|
||||
certain_variables.append(p.name)
|
||||
|
||||
# add variables by order of appearance when possible
|
||||
for name_part in name_to_use:
|
||||
# add variables by order of appearance
|
||||
for name_part in [name_part for name_part in name_to_use if str(name_part).isalnum()]:
|
||||
if name_part in variables_found:
|
||||
concept.def_var(name_part, None)
|
||||
|
||||
# check that all mandatory variables are defined in the name
|
||||
# KSI: 2021-02-17
|
||||
# The mandatory variables come for bnf definition where it was not possible to resolve to a concept
|
||||
# The mandatory variables come from bnf definition where it was not possible to resolve to a concept
|
||||
# So rather that issuing a 'UnresolvedVariableError' I prefer UNKNOWN_CONCEPT
|
||||
if (diff := mandatory_variables.difference(set(name_to_use))) != set():
|
||||
unknown_concepts = [sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body={"name": c}) for c in sorted(diff)]
|
||||
@@ -139,7 +185,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
|
||||
# add the remaining properties
|
||||
# They mainly come from BNF definition
|
||||
for p in variables_found:
|
||||
for p in certain_variables:
|
||||
if p not in concept.values():
|
||||
concept.def_var(p, None)
|
||||
|
||||
@@ -154,6 +200,10 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
def_concept_node.definition_type == DEFINITION_TYPE_BNF:
|
||||
concept.set_bnf(def_concept_node.definition.value.value)
|
||||
|
||||
# manage auto eval
|
||||
if def_concept_node.auto_eval:
|
||||
concept.add_prop(BuiltinConcepts.ISA, sheerka.new(BuiltinConcepts.AUTO_EVAL))
|
||||
|
||||
ret = sheerka.create_new_concept(context, concept)
|
||||
if not ret.status:
|
||||
error_cause = sheerka.objvalue(ret.body)
|
||||
@@ -172,24 +222,38 @@ 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
|
||||
"""
|
||||
|
||||
def get_inner_concept(parsing_result):
|
||||
if not isinstance(parsing_result, ParserResultConcept):
|
||||
return None
|
||||
|
||||
if isinstance(parsing_result.body, Concept):
|
||||
return parsing_result.body
|
||||
|
||||
# manage other cases (conceptNode) later
|
||||
return None
|
||||
|
||||
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)]
|
||||
names = [str(t.value) for t in ret_value.tokens if t.type in (TokenKind.IDENTIFIER,
|
||||
TokenKind.STRING,
|
||||
TokenKind.KEYWORD)]
|
||||
possible_vars = filter(lambda x: x in concept_name and context.sheerka.is_not_a_variable(x), names)
|
||||
debugger.debug_var("names", names, hint="from NameNode")
|
||||
return set(filter(lambda x: x in concept_name and context.sheerka.is_not_a_variable(x), names))
|
||||
debugger.debug_var("possible_vars", possible_vars, hint="from NameNode")
|
||||
return [PossibleVariable(v) for v in possible_vars]
|
||||
|
||||
#
|
||||
# case of BNF
|
||||
#
|
||||
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, ParsingExpression):
|
||||
visitor = ConceptOrRuleNameVisitor()
|
||||
visitor = ConceptOrRuleVariableVisitor()
|
||||
visitor.visit(ret_value.value.value)
|
||||
debugger.debug_var("names", visitor.names, hint="from BNF")
|
||||
return set(visitor.names)
|
||||
debugger.debug_var("names", visitor.variables, hint="from BNF")
|
||||
return visitor.variables
|
||||
|
||||
#
|
||||
# Case of python code
|
||||
@@ -198,31 +262,43 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
if len(concept_name) > 1:
|
||||
visitor = UnreferencedVariablesVisitor(context)
|
||||
names = visitor.get_names(python_node.ast_)
|
||||
possible_vars = filter(lambda x: x in concept_name and context.sheerka.is_not_a_variable(x), names)
|
||||
debugger.debug_var("names", names, hint="from python node")
|
||||
return set(filter(lambda x: x in concept_name and context.sheerka.is_not_a_variable(x), names))
|
||||
debugger.debug_var("possible_vars", possible_vars, hint="from python node")
|
||||
return [PossibleVariable(v) for v in possible_vars]
|
||||
else:
|
||||
return set()
|
||||
return []
|
||||
|
||||
#
|
||||
# Concept
|
||||
# Case of Concept
|
||||
#
|
||||
if (concept := get_inner_concept(ret_value.value)) is not None and len(concept_name) > 1:
|
||||
# use the variables of the concept is any
|
||||
names = [var_value or var_name for var_name, var_value in concept.get_metadata().variables]
|
||||
possible_vars = filter(lambda x: context.sheerka.is_not_a_variable(x), names)
|
||||
debugger.debug_var("names", names, hint="from concept")
|
||||
debugger.debug_var("possible_vars", possible_vars, hint="from concept")
|
||||
return [PossibleVariable(v) for v in possible_vars]
|
||||
|
||||
#
|
||||
# Other cases
|
||||
#
|
||||
if isinstance(ret_value.value, ParserResultConcept) and len(concept_name) > 1:
|
||||
variables = set()
|
||||
source = ret_value.value.source.as_text() if isinstance(ret_value.value.source,
|
||||
ParserInput) else ret_value.value.source
|
||||
|
||||
source = ret_value.value.source.as_text() if isinstance(ret_value.value.source, ParserInput) else \
|
||||
ret_value.value.source
|
||||
tokens = ret_value.value.tokens or list(Tokenizer(source, yield_eof=False))
|
||||
possible_vars = set()
|
||||
names = []
|
||||
for t in tokens:
|
||||
if t.type == TokenKind.RULE:
|
||||
for v in [v for v in t.value if v is not None]:
|
||||
possible_vars.add(v)
|
||||
names.append(v)
|
||||
else:
|
||||
possible_vars.add(t.str_value)
|
||||
names.append(t.str_value)
|
||||
|
||||
for identifier in [i for i in concept_name if str(i).isalnum()]:
|
||||
if identifier in possible_vars:
|
||||
variables.add(identifier)
|
||||
debugger.debug_var("names", variables, hint="from concept")
|
||||
return variables
|
||||
possible_vars = filter(lambda x: context.sheerka.is_not_a_variable(x), names)
|
||||
debugger.debug_var("names", names, hint="from source")
|
||||
debugger.debug_var("possible_vars", possible_vars, hint="from source")
|
||||
return [PossibleVariable(v) for v in possible_vars]
|
||||
|
||||
return set()
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user