rules: pass evaluation depth as a parameter
This commit is contained in:
parent
3d15b6dd63
commit
6e2391f973
1 changed files with 22 additions and 25 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue