Fixed #9 : I can parse 'def concept'
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
from common.global_symbols import NotInit
|
||||
from core.ExecutionContext import ContextActions, ExecutionContext
|
||||
from core.ReturnValue import ReturnValue
|
||||
from core.concept import DefinitionType
|
||||
from core.error import ErrorContext, SheerkaException
|
||||
from evaluators.base_evaluator import EvaluatorEvalResult, EvaluatorMatchResult, OneReturnValueEvaluator
|
||||
from parsers.BnfDefinitionParser import BnfDefinitionParser
|
||||
from parsers.ConceptDefinitionParser import ConceptDefinition
|
||||
from parsers.parser_utils import ParsingException
|
||||
from parsers.tokenizer import TokenKind, Tokenizer
|
||||
|
||||
NAMES_TOKEN_TYPES = {TokenKind.IDENTIFIER,
|
||||
TokenKind.STRING}
|
||||
CONCEPT_PARTS_TO_USE = [
|
||||
"body",
|
||||
"where",
|
||||
"pre",
|
||||
"post",
|
||||
"ret",
|
||||
]
|
||||
|
||||
|
||||
class DefConceptEvaluator(OneReturnValueEvaluator):
|
||||
"""
|
||||
This evaluator create the definition of a new concept
|
||||
after it is recognized by the RecognizeDefConcept parser
|
||||
"""
|
||||
|
||||
NAME = "DefConcept"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(self.NAME, ContextActions.EVALUATION, 50)
|
||||
|
||||
def matches(self, context: ExecutionContext,
|
||||
return_value: ReturnValue) -> EvaluatorMatchResult:
|
||||
return EvaluatorMatchResult(return_value.status and isinstance(return_value.value, ConceptDefinition))
|
||||
|
||||
def eval(self, context: ExecutionContext,
|
||||
evaluation_context: object,
|
||||
return_value: ReturnValue) -> EvaluatorEvalResult:
|
||||
|
||||
try:
|
||||
concept_def = return_value.value
|
||||
variables = self._get_variables(context, concept_def)
|
||||
parameters = None
|
||||
|
||||
if concept_def.definition_type == DefinitionType.BNF:
|
||||
self._validate_bnf(context, concept_def)
|
||||
|
||||
ret_val = context.sheerka.define_new_concept(context,
|
||||
concept_def.name,
|
||||
False,
|
||||
False,
|
||||
concept_def.body,
|
||||
concept_def.where,
|
||||
concept_def.pre,
|
||||
concept_def.post,
|
||||
concept_def.ret,
|
||||
concept_def.definition,
|
||||
concept_def.definition_type,
|
||||
concept_def.auto_eval,
|
||||
variables=variables,
|
||||
parameters=parameters)
|
||||
|
||||
if ret_val.status:
|
||||
new = ReturnValue(self.NAME, True, ret_val.value, parents=[return_value])
|
||||
return EvaluatorEvalResult([new], [return_value])
|
||||
else:
|
||||
error_ret_val = ReturnValue(self.NAME, False, ret_val.value, [return_value])
|
||||
return EvaluatorEvalResult([error_ret_val], [])
|
||||
|
||||
except ParsingException as ex:
|
||||
error_context = ErrorContext(self.NAME, context, ex.error)
|
||||
error_ret_val = ReturnValue(self.NAME, False, error_context, [return_value])
|
||||
return EvaluatorEvalResult([error_ret_val], [])
|
||||
|
||||
def _get_variables(self, context: ExecutionContext, concept_def: ConceptDefinition):
|
||||
variables_found = set() # list of names, there is no tuple
|
||||
definition = concept_def.definition or concept_def.name
|
||||
possible_vars_from_name = self._get_possible_vars_from_def(context, definition)
|
||||
|
||||
possible_vars_from_name_as_set = set(possible_vars_from_name)
|
||||
for part in CONCEPT_PARTS_TO_USE:
|
||||
# if these possibles variables are referenced in other parts of the definition, they may be variables
|
||||
part_value = getattr(concept_def, part)
|
||||
if part_value == "":
|
||||
continue
|
||||
|
||||
possible_vars_from_part = self._get_possible_vars_from_part(context, part_value)
|
||||
variables_found.update(possible_vars_from_name_as_set & possible_vars_from_part)
|
||||
|
||||
# add variables from add_var
|
||||
if concept_def.def_var:
|
||||
variables_found.update(concept_def.def_var)
|
||||
|
||||
with_default_value = [v if isinstance(v, tuple) else (v, NotInit) for v in variables_found]
|
||||
|
||||
# variables are sorted
|
||||
sorted_vars = []
|
||||
for possible_var in possible_vars_from_name:
|
||||
for found in with_default_value:
|
||||
if possible_var == found[0]:
|
||||
sorted_vars.append(found)
|
||||
|
||||
return sorted_vars
|
||||
|
||||
@staticmethod
|
||||
def _get_possible_vars_from_def(context, definition):
|
||||
"""
|
||||
|
||||
:param context:
|
||||
:type context:
|
||||
:param definition:
|
||||
:type definition:
|
||||
:return: list of names
|
||||
:rtype:
|
||||
"""
|
||||
names = (str(t.value) for t in Tokenizer(definition) if t.type in NAMES_TOKEN_TYPES)
|
||||
possible_vars = filter(lambda x: not context.sheerka.is_a_concept_name(x), names)
|
||||
|
||||
return list(possible_vars)
|
||||
|
||||
@staticmethod
|
||||
def _get_possible_vars_from_part(context, part):
|
||||
"""
|
||||
|
||||
:param context:
|
||||
:type context:
|
||||
:param part:
|
||||
:type part:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
# not the final implementation
|
||||
# In the final impl,
|
||||
# we first need to check if the part is a concept call (rather than a concept name)
|
||||
names = (str(t.value) for t in Tokenizer(part) if t.type in NAMES_TOKEN_TYPES)
|
||||
possible_vars = filter(lambda x: not context.sheerka.is_a_concept_name(x), names)
|
||||
|
||||
return set(possible_vars)
|
||||
|
||||
@staticmethod
|
||||
def _validate_bnf(context, definition):
|
||||
parser = BnfDefinitionParser(context, definition.definition, definition.name)
|
||||
parser.parse()
|
||||
if parser.error_sink:
|
||||
raise ParsingException(parser.error_sink[0])
|
||||
|
||||
return True
|
||||
Reference in New Issue
Block a user