From be9f38915944ff68fe57643323a72ad1232efd7f Mon Sep 17 00:00:00 2001 From: Antoine Viallon Date: Mon, 8 May 2023 17:43:34 +0200 Subject: [PATCH] nodes+parser: refactor parsing of terms and factors Make a distinction between Summation, Subtraction, Product and Division. Also distinguish Integers and Floats --- compiler/nodes.py | 48 ++++++++++++++++++++++++++--- compiler/parser.py | 75 ++++++++++++++++++++++++---------------------- 2 files changed, 83 insertions(+), 40 deletions(-) diff --git a/compiler/nodes.py b/compiler/nodes.py index ef70583..e1d2625 100644 --- a/compiler/nodes.py +++ b/compiler/nodes.py @@ -1,5 +1,7 @@ from __future__ import annotations +from beartype import beartype + class Node: pass @@ -10,7 +12,15 @@ class Operator(Node): class Sum(Node): - def __init__(self, *values: Expression): + def __init__(self, *values: Value): + self.values = values + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join(repr(v) for v in self.values)})" + + +class Sub(Node): + def __init__(self, *values: Value): self.values = values def __repr__(self): @@ -18,14 +28,35 @@ class Sum(Node): class Product(Node): - def __init__(self, *values: Expression): + def __init__(self, *values: Value): self.values = values def __repr__(self): return f"{self.__class__.__name__}({', '.join(repr(v) for v in self.values)})" -class Number(Node): +class Division(Node): + def __init__(self, *values: Value): + self.values = values + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join(repr(v) for v in self.values)})" + + +BinaryOperation = Sum | Sub | Product | Division + + +@beartype +class Float(Node): + def __init__(self, value: float): + self.value = value + + def __repr__(self): + return f"{self.__class__.__name__}({self.value})" + + +@beartype +class Integer(Node): def __init__(self, value: int): self.value = value @@ -33,4 +64,13 @@ class Number(Node): return f"{self.__class__.__name__}({self.value})" -Expression = Sum | Product | Number +class Expression(Node): + def __init__(self, node: Node): + self.node = node + + def __repr__(self): + return f"{self.__class__.__name__}({self.node})" + + +Number = Float | Integer +Value = BinaryOperation | Number diff --git a/compiler/parser.py b/compiler/parser.py index 94fdcda..bb091bb 100644 --- a/compiler/parser.py +++ b/compiler/parser.py @@ -1,6 +1,5 @@ from __future__ import annotations - from beartype.typing import List, Dict, Callable from .logger import Logger @@ -21,6 +20,7 @@ class Parser: def __init__(self, tokens: List[Token]): self.tokens = tokens self.pos = 0 + self._last_accepted_token: Tokens | None = None @property def token(self) -> Token: @@ -34,11 +34,12 @@ class Parser: def next_symbol(self): self.pos += 1 - logger.debug("%s", f"Advancing to token {self.pos} {self.token}") + logger.debug(f"Advancing to token {self.pos} {self.token}") def accept(self, *token_types: Tokens) -> False | Token: tok = self.token if self.token.kind in token_types: + self._last_accepted_token = self.token self.next_symbol() return tok return False @@ -51,56 +52,58 @@ class Parser: def expect(self, token_type: Tokens) -> Token: r = self.accept(token_type) - logger.debug("%s", f"Expecting {token_type}, got {r}") + logger.debug(f"Expecting {token_type}, got {r}") if r is False: raise ParsingError(self.token.loc, f"Unexpected token '{self.token}', wanted {token_type}") return r - def factor(self) -> Expression: + def number(self, mandatory: bool = False): + if tok := self.accept(Tokens.Float): + logger.debug(f"Found float {tok}") + return Float(value=float(tok.value)) + elif tok := self.accept(Tokens.Integer): + logger.debug(f"Found integer {tok}") + return Integer(value=int(tok.value)) + elif mandatory: + raise ParsingError(self.token.loc, f"Unexpected token '{self.token}', wanted integer or float") + + def binary_op(self, operand_func: Callable[[], Value], operators: Dict[Tokens, Value]): + operand = operand_func() + + while operator := self.accept(*list(operators.keys())): + node_type = operators[operator.kind] + operand2 = operand_func() + operand = node_type(operand, operand2) + logger.debug(f"{node_type.__name__} of the following operands: {operand} and {operand2}") + + return operand + + def factor(self) -> Value: if self.accept(Tokens.Parens_Left): v = self.expression() self.expect(Tokens.Parens_Right) return v - elif tok := self.accept(Tokens.Number): - logger.debug("%s", f"Found number {self.prev_token}") - return Number(value=int(tok.value)) + elif num := self.number(): + return num else: raise ParsingError(self.token.loc, f"Unexpected token '{self.token}', wanted parenthesized expression or " f"number") - def term(self) -> Expression: - operations = [] - operand = self.factor() - operations += [operand] - - while operator := self.accept(Tokens.Op_Multiply, Tokens.Op_Divide): - operand = self.factor() - operations += [operand] - - if len(operations) == 1: - return operations[0] - - logger.debug("%s", f"Product of the following terms: {operations}") - return Product(*operations) + def term(self) -> Value: + return self.binary_op(self.factor, operators={ + Tokens.Op_Multiply: Product, + Tokens.Op_Divide: Division, + }) def summation(self) -> Sum: - operations = [] - operand = self.term() - operations += [operand] + return self.binary_op(self.term, operators={ + Tokens.Op_Plus: Sum, + Tokens.Op_Minus: Sub, + }) - while operator := self.accept(Tokens.Op_Plus, Tokens.Op_Minus): - operand = self.term() - operations += [operand] - - if len(operations) == 1: - return operations[0] - - logger.debug("%s", f"Sum of the following terms: {operations}") - return Sum(*operations) - - def expression(self) -> Expression: + def expression(self) -> Value: summation = self.summation() - return summation + return Expression(summation) def root(self): blocks = [