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 123
"This is "This is
another string" """ another string" """
path: str = "examples/04_if_else.peb" path: str = "examples/09_logical.peb"
with open(path, "r") as f: with open(path, "r") as f:
source = f.read() source = f.read()
lexer: Lexer = Lexer() lexer: Lexer = Lexer()

View File

@@ -41,6 +41,10 @@ class Expr(ABC):
def visit_variable_expr(self, expr: VariableExpr) -> T: def visit_variable_expr(self, expr: VariableExpr) -> T:
... ...
@abstractmethod
def visit_logical_expr(self, expr: LogicalExpr) -> T:
...
@dataclass(frozen=True) @dataclass(frozen=True)
class AssignExpr(Expr): class AssignExpr(Expr):
@@ -92,3 +96,13 @@ class VariableExpr(Expr):
def accept(self, visitor: Expr.Visitor[T]) -> T: def accept(self, visitor: Expr.Visitor[T]) -> T:
return visitor.visit_variable_expr(self) 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 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.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt
from src.interpreter.environment import Environment from src.interpreter.environment import Environment
from src.interpreter.error import PebbleRuntimeError from src.interpreter.error import PebbleRuntimeError
@@ -40,6 +40,21 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
self.env.assign(expr.name, value) self.env.assign(expr.name, value)
return 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: def visit_binary_expr(self, expr: BinaryExpr) -> Any:
left: Any = self.evaluate(expr.left) left: Any = self.evaluate(expr.left)
right: Any = self.evaluate(expr.right) right: Any = self.evaluate(expr.right)

View File

@@ -1,6 +1,6 @@
from typing import Optional 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.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt
from src.parser.error import ParsingError from src.parser.error import ParsingError
from src.pebble import Pebble from src.pebble import Pebble
@@ -149,7 +149,7 @@ class Parser:
return self.assignment() return self.assignment()
def assignment(self) -> Expr: 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): if self.match(TokenType.EQUAL, TokenType.PLUS_EQUAL, TokenType.MINUS_EQUAL, TokenType.STAR_EQUAL, TokenType.SLASH_EQUAL):
operator: Token = self.previous() operator: Token = self.previous()
value: Expr = self.assignment() value: Expr = self.assignment()
@@ -169,6 +169,22 @@ class Parser:
self.error(operator, "Invalid assignment target.") self.error(operator, "Invalid assignment target.")
return expr 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: def equality(self) -> Expr:
expr: Expr = self.comparison() expr: Expr = self.comparison()
while self.match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL): while self.match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL):