From 51eb540b3c225c5bc8b064212c761d109e00020a Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Thu, 5 Feb 2026 13:55:07 +0100 Subject: [PATCH] feat(ast): add basic AST expressions and printer --- main.py | 16 ++++++++++- src/ast/__init__.py | 0 src/ast/expr.py | 70 +++++++++++++++++++++++++++++++++++++++++++++ src/ast/printer.py | 21 ++++++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/ast/__init__.py create mode 100644 src/ast/expr.py create mode 100644 src/ast/printer.py diff --git a/main.py b/main.py index 205570f..16a14aa 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,7 @@ +from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr +from src.ast.printer import AstPrinter from src.lexer import Lexer -from src.token import Token +from src.token import Token, TokenType def main(): @@ -17,5 +19,17 @@ def main(): tokens: list[Token] = lexer.process(source, path) print(tokens) + printer: AstPrinter = AstPrinter() + ast: Expr = BinaryExpr( + UnaryExpr( + Token(TokenType.MINUS, "-", None, None), + LiteralExpr(123) + ), + Token(TokenType.STAR, "*", None, None), + GroupingExpr(LiteralExpr(45.67)) + ) + print(printer.print(ast)) + + if __name__ == '__main__': main() diff --git a/src/ast/__init__.py b/src/ast/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ast/expr.py b/src/ast/expr.py new file mode 100644 index 0000000..b3f0f25 --- /dev/null +++ b/src/ast/expr.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Any, TypeVar, Generic + +from src.token import Token + + +T = TypeVar("T") + + +@dataclass(frozen=True) +class Expr(ABC): + @abstractmethod + def accept(self, visitor: Visitor[T]) -> T: + ... + + +class Visitor(ABC, Generic[T]): + @abstractmethod + def visit_binary_expr(self, expr: BinaryExpr) -> T: + ... + + @abstractmethod + def visit_unary_expr(self, expr: UnaryExpr) -> T: + ... + + @abstractmethod + def visit_grouping_expr(self, expr: GroupingExpr) -> T: + ... + + @abstractmethod + def visit_literal_expr(self, expr: LiteralExpr) -> T: + ... + + +@dataclass(frozen=True) +class BinaryExpr(Expr): + left: Expr + operator: Token + right: Expr + + def accept(self, visitor: Visitor[T]) -> T: + return visitor.visit_binary_expr(self) + + +@dataclass(frozen=True) +class UnaryExpr(Expr): + operator: Token + right: Expr + + def accept(self, visitor: Visitor[T]) -> T: + return visitor.visit_unary_expr(self) + + +@dataclass(frozen=True) +class GroupingExpr(Expr): + expression: Expr + + def accept(self, visitor: Visitor[T]) -> T: + return visitor.visit_grouping_expr(self) + + +@dataclass(frozen=True) +class LiteralExpr(Expr): + value: Any + + def accept(self, visitor: Visitor[T]) -> T: + return visitor.visit_literal_expr(self) diff --git a/src/ast/printer.py b/src/ast/printer.py new file mode 100644 index 0000000..792021c --- /dev/null +++ b/src/ast/printer.py @@ -0,0 +1,21 @@ +from src.ast.expr import Visitor, Expr, LiteralExpr, T, GroupingExpr, UnaryExpr, BinaryExpr + + +class AstPrinter(Visitor[str]): + def print(self, expr: Expr): + return expr.accept(self) + + def parenthesize(self, name: str, *expressions: Expr): + return f"({name} {' '.join(expr.accept(self) for expr in expressions)})" + + def visit_binary_expr(self, expr: BinaryExpr) -> str: + return self.parenthesize(expr.operator.lexeme, expr.left, expr.right) + + def visit_unary_expr(self, expr: UnaryExpr) -> str: + return self.parenthesize(expr.operator.lexeme, expr.right) + + def visit_grouping_expr(self, expr: GroupingExpr) -> str: + return self.parenthesize("group", expr.expression) + + def visit_literal_expr(self, expr: LiteralExpr) -> str: + return str(expr.value)