150 lines
5.9 KiB
Python
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
|