First implementation of questions management

This commit is contained in:
2020-08-14 08:16:33 +02:00
parent e84b394da2
commit 351c16f946
47 changed files with 1582 additions and 400 deletions
+101 -99
View File
@@ -6,9 +6,15 @@ from core.ast.nodes import CallNodeConcept
from core.ast.visitors import UnreferencedNamesVisitor
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, NotInit, ConceptParts
from core.tokenizer import Keywords
# from evaluators.BaseEvaluator import BaseEvaluator
from parsers.BaseNodeParser import SourceCodeNode, ConceptNode, UnrecognizedTokensNode
from parsers.BaseParser import BaseParser, ErrorNode
PARSE_STEPS = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
EVAL_STEPS = PARSE_STEPS + [BuiltinConcepts.BEFORE_EVALUATION, BuiltinConcepts.EVALUATION,
BuiltinConcepts.AFTER_EVALUATION]
def is_same_success(context, return_values):
"""
@@ -105,7 +111,7 @@ def expect_one(context, return_values):
sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, body=successful_results),
parents=return_values)
# only errors, i cannot help you
# number_of_successful == 0, only errors, i cannot help you
if context.logger and context.logger.isEnabledFor(logging.DEBUG):
context.log(f"Too many errors found by expect_one()", context.who)
for s in successful_results:
@@ -170,88 +176,11 @@ def only_successful(context, return_values):
sheerka.new(BuiltinConcepts.ONLY_SUCCESSFUL, body=successful_results),
parents=return_values)
#
# def remove_ambiguity(context, return_values):
# """
# When multiple concepts are matching, try to find the one(s) which is/are the more accurate
# :param context:
# :param return_values:
# :return:
# """
# # example :
# # Concept("x is a y", PRE=QUESTION)
# # Concept("x is a y")
# # Concept("x is a command")
# # with the source 'foo is a command'
# # The first one do not respect the pre condition (assuming that QUESTION is not in context)
# # The second one is Ok, but the third is more specific (as it's a concept that explicitly asks for 'command')
# # The third one will remain
# if not return_values:
# return return_values
#
# sheerka = context.sheerka
# by_number_of_vars = {} # sorted by number of variables
# to_keep = [] # return values values that are not eligible (ex non parser result)
# passed_validation = []
#
# # sort by number of variables
# # The less variables there are, the more accurate is the concept
# for r in return_values:
# if (r.status and
# sheerka.isinstance(r.body, BuiltinConcepts.PARSER_RESULT) and
# isinstance(r.body.body, Concept)):
# by_number_of_vars.setdefault(len(r.body.body.metadata.variables), []).append(r)
# else:
# to_keep.append(r)
#
# # Check the concepts, starting with the ones that have the less variables
# # Once a concept with a given number of variables is found, we do not process the concepts with an higher
# # number of variables
# for nb_vars in sorted(by_number_of_vars.keys()):
# for r in by_number_of_vars[nb_vars]:
# concept = r.body.body
# if concept.metadata.pre is None or concept.metadata.pre.strip() == "":
# passed_validation.append(r)
# else:
# evaluated = context.sheerka.evaluate_concept(context, concept, metadata=["pre"])
# if evaluated.key == concept.key and evaluated.get_value(ConceptParts.PRE):
# passed_validation.append(r)
# if len(passed_validation) > 0:
# break
#
# # Nothing was filtered, return the original return values
# if len(passed_validation) + len(to_keep) == len(return_values):
# return return_values # nothing to filter
#
# # All return_values fail, return empty list
# if len(passed_validation) == 0:
# return sheerka.ret(
# context.who,
# True,
# sheerka.new(BuiltinConcepts.FILTERED,
# body=to_keep,
# iterable=return_values,
# predicate="remove_ambiguity(context, iterable)"),
# parents=return_values)
#
# # Final check, we consider that the concepts that match a PRE condition are better than concepts with no condition
# by_condition_complexity = {}
# for r in passed_validation:
# by_condition_complexity.setdefault(get_condition_complexity(r.body.body, "pre"), []).append(r)
#
# return sheerka.ret(
# context.who,
# True,
# sheerka.new(BuiltinConcepts.FILTERED,
# body=to_keep + by_condition_complexity[max(by_condition_complexity.keys())],
# iterable=return_values,
# predicate="remove_ambiguity(context, iterable)"),
# parents=return_values)
def resolve_ambiguity(context, concepts):
"""
From the list of concept, elect the one(s) that best suit(s) the context
From the list of concepts, elect the one(s) that best suit(s) the context
Use the PRE metadata to choose the correct concepts
:param context:
:param concepts:
:return:
@@ -265,10 +194,13 @@ def resolve_ambiguity(context, concepts):
remaining_concepts = []
for complexity in sorted(by_complexity.keys(), reverse=True):
for c in by_complexity[complexity]:
evaluated = context.sheerka.evaluate_concept(context, c, metadata=["pre"])
if evaluated.key == c.key:
remaining_concepts.append(c)
if complexity == 0:
remaining_concepts.extend(by_complexity[complexity])
else:
for c in by_complexity[complexity]:
evaluated = context.sheerka.evaluate_concept(context, c, metadata=["pre"])
if evaluated.key == c.key:
remaining_concepts.append(c)
if len(remaining_concepts) > 0:
break # no need to check concept with lower complexity
@@ -349,30 +281,48 @@ def only_parsers_results(context, return_values):
parents=return_values)
def parse_unrecognized(context, source, parsers):
def parse_unrecognized(context, source, parsers, who=None, prop=None, filter_func=None):
"""
Try to recognize concepts or code from source using the given parsers
:param context:
:param source:
:param parsers:
:param who: who is asking the parsing ?
:param prop: Extra info, when parsing a property
:param filter_func: filter function to call is provided
:return:
"""
steps = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
sheerka = context.sheerka
with context.push(BuiltinConcepts.PARSING, source, desc=f"Parsing unrecognized '{source}'") as sub_context:
# disable all parsers but the following ones
sub_context.add_preprocess(BaseParser.PREFIX + "*", enabled=False)
for parser in parsers:
sub_context.add_preprocess(BaseParser.PREFIX + parser, enabled=True)
if prop:
action_context = {"prop": prop, "source": source}
desc = f"Parsing attribute '{prop}'"
else:
action_context = source
desc = f"Parsing '{source}'"
with context.push(BuiltinConcepts.PARSING, action_context, who=who, desc=desc) as sub_context:
# disable all parsers but the requested ones
if parsers != "all":
sub_context.add_preprocess(BaseParser.PREFIX + "*", enabled=False)
for parser in parsers:
sub_context.add_preprocess(BaseParser.PREFIX + parser, enabled=True)
if prop in (Keywords.WHERE, Keywords.PRE, ConceptParts.WHERE, ConceptParts.PRE):
sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
sub_context.add_inputs(source=source)
to_parse = sheerka.ret(
context.who,
True,
sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
res = sheerka.execute(sub_context, to_parse, steps)
to_parse = sheerka.ret(context.who,
True,
sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
res = sheerka.execute(sub_context, to_parse, PARSE_STEPS)
if filter_func:
res = filter_func(sub_context, res)
sub_context.add_values(return_values=res)
if not hasattr(res, "__iter__"):
return res
# discard Python response if accepted by AtomNode
is_concept = False
@@ -383,13 +333,65 @@ def parse_unrecognized(context, source, parsers):
if not is_concept:
return res
filtered = []
no_python = []
for r in res:
if r.who == "parsers.Python":
continue
filtered.append(r)
no_python.append(r)
return filtered
return no_python
def evaluate(context,
source,
evaluators="all",
desc=None,
eval_body=True,
eval_where=True,
expect_success=False,
stm=None):
"""
:param context:
:param source:
:param evaluators:
:param desc:
:param eval_body:
:param eval_where:
:param expect_success:
:param stm: short term memories entries
:return:
"""
sheerka = context.sheerka
desc = desc or f"Eval '{source}'"
with context.push(BuiltinConcepts.EVALUATE_SOURCE, source, desc=desc) as sub_context:
if eval_body:
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
if eval_where:
sub_context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED)
if expect_success:
sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
if stm:
for k, v in stm.items():
sub_context.add_to_short_term_memory(k, v)
# disable all evaluators but the requested ones
if evaluators != "all":
from evaluators.BaseEvaluator import BaseEvaluator
sub_context.add_preprocess(BaseEvaluator.PREFIX + "*", enabled=False)
for evaluator in evaluators:
sub_context.add_preprocess(BaseEvaluator.PREFIX + evaluator, enabled=True)
user_input = sheerka.ret(context.who, True, sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
ret = sheerka.execute(sub_context, [user_input], EVAL_STEPS)
sub_context.add_values(return_values=ret)
return ret
def get_lexer_nodes(return_values, start, tokens):