diff --git a/examples/01_variables.peb b/examples/01_variables.peb index 250b216..3c31fe5 100644 --- a/examples/01_variables.peb +++ b/examples/01_variables.peb @@ -3,4 +3,8 @@ let var2 = "Hello" let var3 = false let var4 = var3 var4 = var2 -var3 = null \ No newline at end of file +var3 = null +print(var1) +print(var2) +print(var3) +print(var4) \ No newline at end of file diff --git a/main.py b/main.py index 344d80b..4413987 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,7 @@ def main(): 123 "This is another string" """ - path: str = "examples/07_math.peb" + path: str = "examples/01_variables.peb" with open(path, "r") as f: source = f.read() lexer: Lexer = Lexer() diff --git a/src/ast/expr.py b/src/ast/expr.py index f7ba222..e655394 100644 --- a/src/ast/expr.py +++ b/src/ast/expr.py @@ -17,6 +17,10 @@ class Expr(ABC): ... class Visitor(ABC, Generic[T]): + @abstractmethod + def visit_assign_expr(self, expr: AssignExpr) -> T: + ... + @abstractmethod def visit_binary_expr(self, expr: BinaryExpr) -> T: ... @@ -38,6 +42,15 @@ class Expr(ABC): ... +@dataclass(frozen=True) +class AssignExpr(Expr): + name: Token + value: Expr + + def accept(self, visitor: Expr.Visitor[T]) -> T: + return visitor.visit_assign_expr(self) + + @dataclass(frozen=True) class BinaryExpr(Expr): left: Expr diff --git a/src/interpreter/interpreter.py b/src/interpreter/interpreter.py index d49f03e..3c24fa5 100644 --- a/src/interpreter/interpreter.py +++ b/src/interpreter/interpreter.py @@ -1,6 +1,6 @@ from typing import Any -from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr +from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr from src.ast.stmt import Stmt, PrintStmt, T, ExpressionStmt, LetStmt from src.interpreter.error import PebbleRuntimeError from src.pebble import Pebble @@ -21,6 +21,11 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): except IndexError: raise PebbleRuntimeError(name, f"Undefined variable '{name.lexeme}'.") + def assign(self, name: Token, value: Any): + if name.lexeme not in self.values: + raise PebbleRuntimeError(name, f"Undefined variable '{name.lexeme}'.") + self.values[name.lexeme] = value + def clear(self): self.values = {} @@ -41,6 +46,11 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): def execute(self, stmt: Stmt) -> None: stmt.accept(self) + def visit_assign_expr(self, expr: AssignExpr) -> Any: + value: Any = self.evaluate(expr.value) + self.env.assign(expr.name, value) + return value + def visit_binary_expr(self, expr: BinaryExpr) -> Any: left: Any = self.evaluate(expr.left) right: Any = self.evaluate(expr.right) diff --git a/src/parser/parser.py b/src/parser/parser.py index ab0bda0..a107958 100644 --- a/src/parser/parser.py +++ b/src/parser/parser.py @@ -1,6 +1,6 @@ from typing import Optional -from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr +from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt from src.parser.error import ParsingError from src.pebble import Pebble @@ -124,7 +124,18 @@ class Parser: return ExpressionStmt(value) def expression(self) -> Expr: - return self.equality() + return self.assignment() + + def assignment(self) -> Expr: + expr: Expr = self.equality() + if self.match(TokenType.EQUAL): + equals: Token = self.previous() + value: Expr = self.assignment() + if isinstance(expr, VariableExpr): + name: Token = expr.name + return AssignExpr(name, value) + self.error(equals, "Invalid assignment target.") + return expr def equality(self) -> Expr: expr: Expr = self.comparison()