Fixed #30 : Add variable support in BNF concept definition
Fixed #31 : Add regex support in BNF Concept Fixed #33 : Do not memorize object during restore
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import core.utils
|
||||
from core.ast_helpers import UnreferencedVariablesVisitor
|
||||
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept, BuiltinConcepts
|
||||
@@ -11,6 +13,29 @@ from parsers.DefConceptParser import DefConceptNode, NameNode
|
||||
from parsers.PythonParser import get_python_node
|
||||
|
||||
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class MandatoryVariable:
|
||||
"""
|
||||
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
|
||||
for example:
|
||||
def concept foo x as isinstance(x, str)
|
||||
{x, str} will be detected as potential variable, but 'str' will find no match.
|
||||
|
||||
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
|
||||
|
||||
To distinguish between mandatory and not mandatory variable, we use MandatoryVariable
|
||||
"""
|
||||
name: str
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("MandatoryVariable", self.name))
|
||||
|
||||
|
||||
class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
|
||||
"""
|
||||
Gets the concepts referenced by BNF
|
||||
@@ -29,6 +54,9 @@ class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
|
||||
else:
|
||||
self.names.add(node.concept)
|
||||
|
||||
def visit_VariableExpression(self, node):
|
||||
self.names.add(MandatoryVariable(node.rule_name))
|
||||
|
||||
def visit_all(self, node):
|
||||
if node.rule_name:
|
||||
self.names.add(node.rule_name)
|
||||
@@ -60,11 +88,13 @@ 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)
|
||||
|
||||
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
|
||||
for prop in ("definition", "where", "pre", "post", "body", "ret"):
|
||||
|
||||
part_ret_val = getattr(def_concept_node, prop)
|
||||
@@ -87,13 +117,26 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
|
||||
# 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)
|
||||
if isinstance(p, MandatoryVariable):
|
||||
variables_found.add(p.name)
|
||||
mandatory_variables.add(p.name)
|
||||
else:
|
||||
variables_found.add(p)
|
||||
|
||||
# add variables by order of appearance when possible
|
||||
for name_part in name_to_use:
|
||||
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
|
||||
# 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)]
|
||||
error = sheerka.new(BuiltinConcepts.ERROR, body=unknown_concepts)
|
||||
return sheerka.ret(self.name, False, error, parents=[return_value])
|
||||
|
||||
# add the remaining properties
|
||||
# They mainly come from BNF definition
|
||||
for p in variables_found:
|
||||
|
||||
Reference in New Issue
Block a user