feat: add logical expressions

This commit is contained in:
2026-02-06 01:52:17 +01:00
parent 40c6e65acc
commit 57eecb8a65
5 changed files with 51 additions and 4 deletions

2
examples/09_logical.peb Normal file
View File

@@ -0,0 +1,2 @@
print("hi" or 2) // "hi".
print(null or "yes") // "yes".

View File

@@ -13,7 +13,7 @@ def main():
123
"This is
another string" """
path: str = "examples/04_if_else.peb"
path: str = "examples/09_logical.peb"
with open(path, "r") as f:
source = f.read()
lexer: Lexer = Lexer()

View File

@@ -41,6 +41,10 @@ class Expr(ABC):
def visit_variable_expr(self, expr: VariableExpr) -> T:
...
@abstractmethod
def visit_logical_expr(self, expr: LogicalExpr) -> T:
...
@dataclass(frozen=True)
class AssignExpr(Expr):
@@ -92,3 +96,13 @@ class VariableExpr(Expr):
def accept(self, visitor: Expr.Visitor[T]) -> T:
return visitor.visit_variable_expr(self)
@dataclass(frozen=True)
class LogicalExpr(Expr):
left: Expr
operator: Token
right: Expr
def accept(self, visitor: Expr.Visitor[T]) -> T:
return visitor.visit_logical_expr(self)

View File

@@ -1,6 +1,6 @@
from typing import Any
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt
from src.interpreter.environment import Environment
from src.interpreter.error import PebbleRuntimeError
@@ -40,6 +40,21 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
self.env.assign(expr.name, value)
return value
def visit_logical_expr(self, expr: LogicalExpr) -> Any:
left: Any = self.evaluate(expr.left)
match expr.operator.type:
case TokenType.OR:
if self.is_truthy(left):
return left
case TokenType.AND:
if not self.is_truthy(left):
return left
case _:
# Unreachable
raise PebbleRuntimeError(expr.operator, f"Unknown logical operator")
return self.evaluate(expr.right)
def visit_binary_expr(self, expr: BinaryExpr) -> Any:
left: Any = self.evaluate(expr.left)
right: Any = self.evaluate(expr.right)

View File

@@ -1,6 +1,6 @@
from typing import Optional
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt
from src.parser.error import ParsingError
from src.pebble import Pebble
@@ -149,7 +149,7 @@ class Parser:
return self.assignment()
def assignment(self) -> Expr:
expr: Expr = self.equality()
expr: Expr = self.or_()
if self.match(TokenType.EQUAL, TokenType.PLUS_EQUAL, TokenType.MINUS_EQUAL, TokenType.STAR_EQUAL, TokenType.SLASH_EQUAL):
operator: Token = self.previous()
value: Expr = self.assignment()
@@ -169,6 +169,22 @@ class Parser:
self.error(operator, "Invalid assignment target.")
return expr
def or_(self) -> Expr:
expr: Expr = self.and_()
while self.match(TokenType.OR):
operator: Token = self.previous()
right: Expr = self.and_()
expr = LogicalExpr(expr, operator, right)
return expr
def and_(self) -> Expr:
expr: Expr = self.equality()
while self.match(TokenType.AND):
operator: Token = self.previous()
right: Expr = self.equality()
expr = LogicalExpr(expr, operator, right)
return expr
def equality(self) -> Expr:
expr: Expr = self.comparison()
while self.match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL):