Files
Sheerka/src/evaluators/DefConceptEvaluator.py
T

150 lines
5.9 KiB
Python

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