rules: pass evaluation depth as a parameter

This commit is contained in:
Antoine Viallon 2023-04-21 22:37:38 +02:00
parent 3d15b6dd63
commit 6e2391f973
Signed by: aviallon
GPG key ID: D126B13AB555E16F

View file

@ -38,7 +38,6 @@ RuleLike = Union[str, 'Rule']
class Rule(abc.ABC):
_named_rules: Dict[str, Rule] = dict()
_depth: int = 0
def __init__(self, *sub_rules: RuleLike):
self._prepared: bool = False
@ -55,7 +54,7 @@ class Rule(abc.ABC):
return rule
@abc.abstractmethod
def evaluate(self, tokens: List[Token]) -> EvalResult:
def evaluate(self, tokens: List[Token], *, depth: int = 0, parent: Optional[Rule] = None) -> EvalResult:
raise NotImplementedError()
def prepare(self):
@ -84,10 +83,10 @@ class Terminal(Rule):
super().__init__()
self.token_type = token_type
def evaluate(self, tokens: List[Token]) -> EvalResult:
def evaluate(self, tokens: List[Token], *, depth: int = 0, parent: Optional[Rule] = None) -> EvalResult:
assert len(tokens) > 0
result = EvalResult(name=self.name)
logger.debug(f"{Rule._depth}: Terminal: Evaluating terminal token with tokens: '{tokens}'")
logger.debug("%s", f"{depth}: Terminal: Evaluating terminal token with tokens: '{tokens}'")
if len(tokens) != 1:
result.errors = ParsingError(tokens[0].loc, message=f"Terminal rule must have exactly one token")
return result
@ -101,27 +100,26 @@ class Terminal(Rule):
result.result = tokens[0]
logger.debug(f"{Rule._depth}: Terminal: Found terminal node: {result}")
logger.debug(f"{depth}: Terminal: Found terminal node: {result}")
return result
def __repr__(self):
return "{}({})".format(self.__class__.__name__, self.token_type)
class Or(Rule):
def evaluate(self, tokens: List[Token]) -> (Optional[Exception], Dict[str, Any]):
def evaluate(self, tokens: List[Token], *, depth: int = 0, parent: Optional[Rule] = None) -> EvalResult:
result = EvalResult(errors=ParsingError(location=tokens[0].loc), name=self.name)
rule: Rule
for rule in self.rules:
logger.debug(f"{Rule._depth}: Or: trying rule: {rule}")
Rule._depth += 1
result = rule.evaluate(tokens)
Rule._depth -= 1
for i, rule in enumerate(self.rules):
logger.debug(f"{depth}: Or: Rule {i + 1}/{len(self.rules)} : trying rule: {rule}")
result = rule.evaluate(tokens, depth=depth + 1)
if result.errors is None:
logger.debug(f"{Rule._depth}: Or: Rule '{rule}' matched, result: {result}")
logger.debug(f"{depth}: Or: Rule {i + 1}/{len(self.rules)} '{rule}' matched, result: {result}")
break
logger.debug(f"{Rule._depth}: Or: Finished with errors: {result.errors}")
logger.debug(f"{depth}: Or: Finished with errors: {result.errors}")
result.name = self.name
@ -129,27 +127,25 @@ class Or(Rule):
class And(Rule):
def evaluate(self, tokens: List[Token]) -> (Optional[Exception], Dict[str, Any]):
def evaluate(self, tokens: List[Token], *, depth: int = 0, parent: Optional[Rule] = None) -> EvalResult:
result = EvalResult(errors=ParsingError(tokens[0].loc), name=self.name)
result.result = []
begin = 0
end = 0
for i, rule in enumerate(self.rules):
logger.debug("%s", f"{Rule._depth}: And: Trying rule '{rule}'")
logger.debug("%s", f"{depth}: And: Trying rule '{rule}'")
if end == len(tokens):
logger.error("%s", f"{Rule._depth}: And: Oops, reached the end of the tokens")
logger.error("%s", f"{depth}: And: Oops, reached the end of the tokens")
best_r: Optional[EvalResult] = None
while end < len(tokens):
tokens_ = tokens[begin:end+1]
Rule._depth += 1
r = rule.evaluate(tokens_)
Rule._depth -= 1
tokens_ = tokens[begin:end + 1]
r = rule.evaluate(tokens_, depth=depth + 1)
# No previous match, but we found one
if best_r is None and r.errors is None:
logger.debug(f"{Rule._depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' matched, result: {r}")
logger.debug(f"{depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' matched, result: {r}")
best_r = r
# No result at all. That's an error.
@ -159,12 +155,13 @@ class And(Rule):
# We had a result, but we still matched with more tokens
elif best_r is not None and r.errors is None:
logger.debug(f"{Rule._depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' **improved**, result: {r}")
logger.debug(f"{depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' **improved**, result: {r}")
best_r = r
# We already have a match, and we can't improve it. Finish this rule.
elif best_r is not None and r.errors is not None:
logger.debug(f"{Rule._depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' has FINAL match, result: {best_r}")
logger.debug(
f"{depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' has FINAL match, result: {best_r}")
result.result += [best_r]
# Matching rule ended at 'end - 1', meaning next rule will begin at end
begin = end
@ -174,10 +171,10 @@ class And(Rule):
else:
logger.debug(
f"{Rule._depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' has FINAL match, finishing rule, result: {best_r}")
f"{depth}: And: Rule {i + 1}/{len(self.rules)} '{rule}' has FINAL match, finishing rule, result: {best_r}")
result.result += [best_r]
result.errors = None
if end != len(tokens):
logger.debug(f"{Rule._depth}: And: Didn't consume all tokens")
logger.debug(f"{depth}: And: Didn't consume all tokens")
return result