diff --git a/compiler/rules.py b/compiler/rules.py index 1e7145d..883b232 100644 --- a/compiler/rules.py +++ b/compiler/rules.py @@ -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