Refactored sheerka execution flow + Enhanced log management
This commit is contained in:
+26
-8
@@ -1,5 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
from core.tokenizer import TokenKind, Keywords
|
||||
from core.sheerka_logger import get_logger
|
||||
import logging
|
||||
|
||||
|
||||
@dataclass()
|
||||
@@ -34,9 +36,13 @@ class UnexpectedTokenErrorNode(ErrorNode):
|
||||
|
||||
|
||||
class BaseParser:
|
||||
PREFIX = "Parsers:"
|
||||
PREFIX = "parsers."
|
||||
|
||||
def __init__(self, name):
|
||||
self.log = get_logger("parsers." + self.__class__.__name__)
|
||||
self.init_log = get_logger("init." + self.PREFIX + self.__class__.__name__)
|
||||
self.verbose_log = get_logger("verbose." + self.PREFIX + self.__class__.__name__)
|
||||
|
||||
self.name = self.PREFIX + name
|
||||
self.has_error = False
|
||||
self.error_sink = []
|
||||
@@ -52,6 +58,25 @@ class BaseParser:
|
||||
def parse(self, context, text):
|
||||
pass
|
||||
|
||||
def log_result(self, context, source, ret):
|
||||
if not self.log.isEnabledFor(logging.DEBUG):
|
||||
return
|
||||
|
||||
if ret.status:
|
||||
value = context.return_value_to_str(ret)
|
||||
context.log(self.log, f"Recognized '{source}' as {value}", self.name)
|
||||
else:
|
||||
context.log(self.log, f"Failed to recognize '{source}'", self.name)
|
||||
|
||||
def log_multiple_results(self, context, source, list_of_ret):
|
||||
if not self.log.isEnabledFor(logging.DEBUG):
|
||||
return
|
||||
|
||||
context.log(self.log, f"Recognized '{source}' as multiple concepts", self.name)
|
||||
for r in list_of_ret:
|
||||
value = context.return_value_to_str(r)
|
||||
context.log(self.log, f" Recognized '{value}'", self.name)
|
||||
|
||||
@staticmethod
|
||||
def get_text_from_tokens(tokens):
|
||||
if tokens is None:
|
||||
@@ -65,10 +90,3 @@ class BaseParser:
|
||||
value = Keywords(token.value).value if token.type == TokenKind.KEYWORD else token.value
|
||||
res += value
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def log_result(log, text, ret):
|
||||
if ret.status:
|
||||
log.debug(f"Recognized '{text}' as {ret.value}")
|
||||
else:
|
||||
log.debug(f"Failed to recognize '{text}'")
|
||||
|
||||
@@ -30,7 +30,7 @@ class BnfParser:
|
||||
def __init__(self):
|
||||
self.has_error = False
|
||||
self.error_sink = []
|
||||
self.name = BaseParser.PREFIX + "RegexParser"
|
||||
self.name = BaseParser.PREFIX + "Bnf"
|
||||
|
||||
self.lexer_iter = None
|
||||
self._current = None
|
||||
|
||||
@@ -13,9 +13,6 @@ from core.concept import Concept
|
||||
from core.tokenizer import TokenKind, Tokenizer, Token
|
||||
from parsers.BaseParser import BaseParser, Node, ErrorNode
|
||||
import core.utils
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def flatten(iterable):
|
||||
@@ -588,8 +585,8 @@ class ConceptLexerParser(BaseParser):
|
||||
return self.sheerka.ret(self.name, True, self.concepts_grammars)
|
||||
|
||||
def get_concept(self, concept_name):
|
||||
if concept_name in self.context.concepts_cache:
|
||||
return self.context.concepts_cache[concept_name]
|
||||
if concept_name in self.context.concepts:
|
||||
return self.context.concepts[concept_name]
|
||||
return self.sheerka.get(concept_name)
|
||||
|
||||
def get_model(self, concept_def, concepts_to_resolve):
|
||||
@@ -668,6 +665,9 @@ class ConceptLexerParser(BaseParser):
|
||||
if self.sheerka.isinstance(e, BuiltinConcepts.UNKNOWN_CONCEPT):
|
||||
continue
|
||||
|
||||
if e not in self.concepts_grammars:
|
||||
continue
|
||||
|
||||
to_resolve = self.concepts_grammars[e]
|
||||
if _is_infinite_recursion(e, to_resolve):
|
||||
removed_concepts.append(e)
|
||||
@@ -730,7 +730,7 @@ class ConceptLexerParser(BaseParser):
|
||||
|
||||
# manage when nothing is recognized (or other error)
|
||||
if self.has_error:
|
||||
return self.sheerka.ret(
|
||||
ret = self.sheerka.ret(
|
||||
self.name,
|
||||
False,
|
||||
self.sheerka.new(
|
||||
@@ -739,6 +739,8 @@ class ConceptLexerParser(BaseParser):
|
||||
source=text,
|
||||
body=self.error_sink,
|
||||
try_parsed=concepts_found[0] if len(concepts_found) == 1 else concepts_found))
|
||||
self.log_result(context, text, ret)
|
||||
return ret
|
||||
|
||||
# else
|
||||
# returns as many ReturnValue than choices found
|
||||
@@ -755,7 +757,12 @@ class ConceptLexerParser(BaseParser):
|
||||
body=choice,
|
||||
try_parsed=choice)))
|
||||
|
||||
return ret[0] if len(ret) == 1 else ret
|
||||
if len(ret) == 1:
|
||||
self.log_result(context, text, ret[0])
|
||||
return ret[0]
|
||||
else:
|
||||
self.log_multiple_results(context, text, ret)
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_bests(results):
|
||||
|
||||
+29
-21
@@ -5,11 +5,8 @@ import core.utils
|
||||
from parsers.BaseParser import BaseParser, Node, ErrorNode, NotInitializedNode
|
||||
from core.tokenizer import Tokenizer, TokenKind, Token, Keywords
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
|
||||
from parsers.BnfParser import BnfParser
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
from core.sheerka import ExecutionContext
|
||||
|
||||
|
||||
@dataclass()
|
||||
@@ -207,10 +204,10 @@ class DefaultParser(BaseParser):
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
BaseParser.__init__(self, "DefaultParser")
|
||||
BaseParser.__init__(self, "Default")
|
||||
self.lexer_iter = None
|
||||
self._current = None
|
||||
self.context = None
|
||||
self.context: ExecutionContext = None
|
||||
self.text = None
|
||||
self.sheerka = None
|
||||
|
||||
@@ -287,11 +284,12 @@ class DefaultParser(BaseParser):
|
||||
def parse(self, context, text):
|
||||
# default parser can only manage string text
|
||||
if not isinstance(text, str):
|
||||
log.debug(f"Failed to recognize '{text}'")
|
||||
return context.sheerka.ret(
|
||||
ret = context.sheerka.ret(
|
||||
self.name,
|
||||
False,
|
||||
context.sheerka.new(BuiltinConcepts.NOT_FOR_ME, body=text))
|
||||
self.log_result(context, text, ret)
|
||||
return ret
|
||||
|
||||
self.reset_parser(context, text)
|
||||
tree = self.parse_statement()
|
||||
@@ -299,23 +297,29 @@ class DefaultParser(BaseParser):
|
||||
# If a error is found it must be sent to error_sink
|
||||
# tree must contain what was recognized
|
||||
|
||||
ret = self.sheerka.ret(
|
||||
self.name,
|
||||
not self.has_error,
|
||||
self.sheerka.new(
|
||||
if self.has_error and isinstance(self.error_sink[0], CannotHandleErrorNode):
|
||||
body = self.sheerka.new(BuiltinConcepts.NOT_FOR_ME, body=self.error_sink)
|
||||
else:
|
||||
body = self.sheerka.new(
|
||||
BuiltinConcepts.PARSER_RESULT,
|
||||
parser=self,
|
||||
source=text,
|
||||
body=self.error_sink if self.has_error else tree,
|
||||
try_parsed=tree))
|
||||
try_parsed=tree)
|
||||
|
||||
self.log_result(log, text, ret)
|
||||
ret = self.sheerka.ret(
|
||||
self.name,
|
||||
not self.has_error,
|
||||
body)
|
||||
|
||||
self.log_result(context, text, ret)
|
||||
return ret
|
||||
|
||||
def parse_statement(self):
|
||||
token = self.get_token()
|
||||
if token.value == Keywords.DEF:
|
||||
self.next_token()
|
||||
self.context.log(self.verbose_log, "Keyword DEF found.", self.name)
|
||||
return self.parse_def_concept(token)
|
||||
else:
|
||||
return self.add_error(CannotHandleErrorNode([], self.text))
|
||||
@@ -326,7 +330,6 @@ class DefaultParser(BaseParser):
|
||||
"""
|
||||
|
||||
# init
|
||||
log.debug("It may be a definition of a concept")
|
||||
keywords_tokens = [def_token]
|
||||
concept_found = DefConceptNode(keywords_tokens)
|
||||
|
||||
@@ -354,7 +357,6 @@ class DefaultParser(BaseParser):
|
||||
concept_found.post = asts_found_by_parts[Keywords.POST]
|
||||
concept_found.body = asts_found_by_parts[Keywords.AS]
|
||||
|
||||
log.debug(f"Found DefConcept node '{concept_found}'")
|
||||
return concept_found
|
||||
|
||||
def regroup_tokens_by_parts(self, keywords_tokens):
|
||||
@@ -412,7 +414,8 @@ class DefaultParser(BaseParser):
|
||||
if TokenKind.NEWLINE in [t.type for t in name_tokens]:
|
||||
self.add_error(SyntaxErrorNode(tokens_found_by_parts[Keywords.CONCEPT], "Newline are not allowed in name."))
|
||||
|
||||
return NameNode(name_tokens[name_first_token_index:]) # skip the first token
|
||||
name_node = NameNode(name_tokens[name_first_token_index:]) # skip the first token
|
||||
return name_node
|
||||
|
||||
def get_concept_definition(self, tokens_found_by_parts):
|
||||
if tokens_found_by_parts[Keywords.FROM] is None:
|
||||
@@ -448,8 +451,6 @@ class DefaultParser(BaseParser):
|
||||
if keyword == Keywords.CONCEPT or keyword == Keywords.FROM:
|
||||
continue # already done
|
||||
|
||||
log.debug("Processing part '" + keyword.name + "'")
|
||||
|
||||
tokens = tokens_found_by_parts[keyword]
|
||||
if tokens is None:
|
||||
continue # nothing to do
|
||||
@@ -464,8 +465,15 @@ class DefaultParser(BaseParser):
|
||||
continue
|
||||
|
||||
# ask the other parsers if they recognize the tokens
|
||||
new_context = self.context.push(self.name)
|
||||
parsing_result = core.builtin_helpers.expect_one(new_context, self.sheerka.parse(new_context, tokens))
|
||||
new_context = self.context.push(self.name, desc=f"Parsing {keyword}")
|
||||
new_context.log_new(self.verbose_log)
|
||||
to_parse = self.sheerka.ret(
|
||||
new_context.who,
|
||||
True,
|
||||
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=tokens))
|
||||
steps = [BuiltinConcepts.PARSING]
|
||||
parsed = self.sheerka.execute(new_context, to_parse, steps, self.verbose_log)
|
||||
parsing_result = core.builtin_helpers.expect_one(new_context, parsed)
|
||||
if not parsing_result.status:
|
||||
self.add_error(parsing_result.value)
|
||||
continue
|
||||
|
||||
@@ -11,7 +11,7 @@ class EmptyStringParser(BaseParser):
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
BaseParser.__init__(self, "NullParser")
|
||||
BaseParser.__init__(self, "EmptyString")
|
||||
|
||||
def parse(self, context, text):
|
||||
sheerka = context.sheerka
|
||||
@@ -19,12 +19,13 @@ class EmptyStringParser(BaseParser):
|
||||
if isinstance(text, str) and text.strip() == "" or \
|
||||
isinstance(text, list) and text == [] or \
|
||||
text is None:
|
||||
log.debug(f"Recognized '{text}' as BuiltinConcepts.NOP.")
|
||||
return sheerka.ret(self.name, True, sheerka.new(
|
||||
ret = sheerka.ret(self.name, True, sheerka.new(
|
||||
BuiltinConcepts.PARSER_RESULT,
|
||||
parser=self,
|
||||
source="",
|
||||
body=sheerka.new(BuiltinConcepts.NOP)))
|
||||
else:
|
||||
ret = sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.NOT_FOR_ME))
|
||||
|
||||
log.debug(f"Failed to recognize '{text}'")
|
||||
return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.NOT_FOR_ME))
|
||||
self.log_result(context, text, ret)
|
||||
return ret
|
||||
|
||||
@@ -15,7 +15,7 @@ class ExactConceptParser(BaseParser):
|
||||
MAX_WORDS_SIZE = 10
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
BaseParser.__init__(self, "ConceptParser")
|
||||
BaseParser.__init__(self, "ExactConcept")
|
||||
|
||||
def parse(self, context, text):
|
||||
"""
|
||||
@@ -56,14 +56,19 @@ class ExactConceptParser(BaseParser):
|
||||
source=text if isinstance(text, str) else self.get_text_from_tokens(text),
|
||||
body=concept,
|
||||
try_parsed=concept)))
|
||||
log.debug(f"Recognized '{text}' as '{concept}'")
|
||||
recognized = True
|
||||
|
||||
if recognized:
|
||||
if len(res) == 1:
|
||||
self.log_result(context, text, res[0])
|
||||
else:
|
||||
self.log_multiple_results(context, text, res)
|
||||
return res
|
||||
return res
|
||||
|
||||
log.debug(f"Failed to recognize {words}")
|
||||
return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body=text))
|
||||
ret = sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body=text))
|
||||
self.log_result(context, text, ret)
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_words(text):
|
||||
|
||||
@@ -13,7 +13,7 @@ class PythonErrorNode(ErrorNode):
|
||||
exception: Exception
|
||||
|
||||
# def __post_init__(self):
|
||||
# log.debug("-> PythonErrorNode: " + str(self.exception))
|
||||
# self.log.debug("-> PythonErrorNode: " + str(self.exception))
|
||||
|
||||
|
||||
@dataclass()
|
||||
@@ -58,7 +58,7 @@ class PythonParser(BaseParser):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
BaseParser.__init__(self, "PythonParser")
|
||||
BaseParser.__init__(self, "Python")
|
||||
self.source = kwargs.get("source", "<undef>")
|
||||
|
||||
def parse(self, context, text):
|
||||
@@ -87,7 +87,7 @@ class PythonParser(BaseParser):
|
||||
body=self.error_sink if self.has_error else PythonNode(text, tree),
|
||||
try_parsed=None))
|
||||
|
||||
self.log_result(log, text, ret)
|
||||
self.log_result(context, text, ret)
|
||||
return ret
|
||||
|
||||
def try_parse_expression(self, text):
|
||||
@@ -110,8 +110,6 @@ class PythonGetNamesVisitor(ast.NodeVisitor):
|
||||
|
||||
def __init__(self):
|
||||
self.names = set()
|
||||
log.debug("Searching for names.")
|
||||
|
||||
def visit_Name(self, node):
|
||||
log.debug(f"Found name : {node.id}")
|
||||
self.names.add(node.id)
|
||||
|
||||
Reference in New Issue
Block a user