Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lox/Expr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Expr:
pass


class Binary(Expr):
def __init__(self, left, operator, right):
self.left = left
Expand All @@ -10,20 +11,23 @@ def __init__(self, left, operator, right):
def accept(self, visitor):
return visitor.visit_BinaryExpr(self)


class Grouping(Expr):
def __init__(self, expression):
self.expression = expression

def accept(self, visitor):
return visitor.visit_GroupingExpr(self)


class Literal(Expr):
def __init__(self, value):
self.value = value

def accept(self, visitor):
return visitor.visit_LiteralExpr(self)


class Unary(Expr):
def __init__(self, operator, right):
self.operator = operator
Expand All @@ -32,13 +36,15 @@ def __init__(self, operator, right):
def accept(self, visitor):
return visitor.visit_UnaryExpr(self)


class Variable(Expr):
def __init__(self, name):
self.name = name

def accept(self, visitor):
return visitor.visit_VariableExpr(self)


class Assign(Expr):
def __init__(self, name, value):
self.name = name
Expand All @@ -47,6 +53,7 @@ def __init__(self, name, value):
def accept(self, visitor):
return visitor.visit_AssignExpr(self)


class Logical(Expr):
def __init__(self, left, operator, right):
self.left = left
Expand All @@ -56,6 +63,7 @@ def __init__(self, left, operator, right):
def accept(self, visitor):
return visitor.visit_LogicalExpr(self)


class Call(Expr):
def __init__(self, callee, paren, arguments):
self.callee = callee
Expand Down
4 changes: 2 additions & 2 deletions lox/LoxFunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from lox.LoxCallable import LoxCallable
from lox.loxreturn import Return


class LoxFunction(LoxCallable):
def __init__(self, declaration, closure):
self.declaration = declaration
Expand All @@ -10,8 +11,7 @@ def __init__(self, declaration, closure):
def __call__(self, interpreter, arguments):
self.environment = Environment(self.closure)
for i in range(len(self.declaration.params)):
self.environment.define(
self.declaration.params[i].lexeme, arguments[i])
self.environment.define(self.declaration.params[i].lexeme, arguments[i])
try:
interpreter._execute_block(self.declaration.body, self.environment)
except Return as ret:
Expand Down
18 changes: 14 additions & 4 deletions lox/Stmt.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
class Stmt:
'''Program -> Decl* ";"
"""Program -> Decl* ";"
Decl -> VarDcl | Stmt
Stmt -> ExprStmt | PrintStmt
'''
"""

pass


class Expression(Stmt):
'''
"""
ExprStmt -> expression ";"
'''
"""

def __init__(self, expression):
self.expression = expression

def accept(self, visitor):
return visitor.visit_ExpressionStmt(self)


class Print(Stmt):
def __init__(self, expression):
self.expression = expression

def accept(self, visitor):
return visitor.visit_PrintStmt(self)


class Var(Stmt):
def __init__(self, name, initializer):
self.name = name
Expand All @@ -30,13 +35,15 @@ def __init__(self, name, initializer):
def accept(self, visitor):
return visitor.visit_VarStmt(self)


class Block(Stmt):
def __init__(self, statements):
self.statements = statements

def accept(self, visitor):
return visitor.visit_BlockStmt(self)


class If(Stmt):
def __init__(self, condition, then_branch, else_branch):
self.condition = condition
Expand All @@ -46,6 +53,7 @@ def __init__(self, condition, then_branch, else_branch):
def accept(self, visitor):
return visitor.visit_IfStmt(self)


class While(Stmt):
def __init__(self, condition, body):
self.condition = condition
Expand All @@ -54,6 +62,7 @@ def __init__(self, condition, body):
def accept(self, visitor):
return visitor.visit_WhileStmt(self)


class Function(Stmt):
def __init__(self, name, params, body):
self.name = name
Expand All @@ -63,6 +72,7 @@ def __init__(self, name, params, body):
def accept(self, visitor):
return visitor.visit_FunctionStmt(self)


class Return(Stmt):
def __init__(self, keyword, value):
self.keyword = keyword
Expand Down
32 changes: 14 additions & 18 deletions lox/ast_printer.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,41 @@
from lox import Expr
from lox.visitor import Visitor
from lox.token import Token
from lox.tokentype import TokenType
from lox.visitor import Visitor


class ASTPrinter(Visitor):
def pprint_ast(self, expr):
return self.visit(expr)

def parenthesize(self, name, *args):
out = ['(', name]
out = ["(", name]
for expr in args:
out.append(' ')
out.append(" ")
out.append(expr.accept(self))
out.append(')')
return ''.join(out)
out.append(")")
return "".join(out)

def visit_BinaryExpr(self, expr):
return self.parenthesize(expr.operator.lexeme, expr.left, expr.right)

def visit_GroupingExpr(self, expr):
return self.parenthesize('group', expr.expression)
return self.parenthesize("group", expr.expression)

def visit_LiteralExpr(self, expr):
if expr.value == 'nil':
return 'nil'
if expr.value == "nil":
return "nil"
return str(expr.value)

def visit_UnaryExpr(self, expr):
return self.parenthesize(expr.operator.lexeme, expr.right)


if __name__ == '__main__':

if __name__ == "__main__":
expression = Expr.Binary(
Expr.Unary(
Token(TokenType.MINUS, '-', None, 1),
Expr.Literal(123)
),
Token(TokenType.STAR, '*', None, 1),
Expr.Grouping(
Expr.Literal(45.67)
)
Expr.Unary(Token(TokenType.MINUS, "-", None, 1), Expr.Literal(123)),
Token(TokenType.STAR, "*", None, 1),
Expr.Grouping(Expr.Literal(45.67)),
)
ppast = ASTPrinter().pprint_ast(expression)
print(ppast)
13 changes: 7 additions & 6 deletions lox/environment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from lox.exceptions import RuntimeException


class Environment:
def __init__(self, enclosing=None):
self.values = {}
Expand All @@ -11,29 +12,29 @@ def ancestor(self, distance):
return self

def define(self, name, value):
''':param name: str
""":param name: str
:param value: Expr.Literal
'''
"""
self.values[name] = value

def assign(self, name, value):
''':param name: Token
""":param name: Token
:param value: Expr.Literal
'''
"""
if name.lexeme in self.values.keys():
self.define(name.lexeme, value)
return
if self.enclosing != None:
self.enclosing.assign(name, value)
return
raise RuntimeException(name, f'Undefined variable {name.lexeme}.')
raise RuntimeException(name, f"Undefined variable {name.lexeme}.")

def get(self, name):
if name.lexeme in self.values.keys():
return self.values[name.lexeme]
if self.enclosing != None:
return self.enclosing.get(name)
raise RuntimeException(name, f'Undefined name {name.lexeme}.')
raise RuntimeException(name, f"Undefined name {name.lexeme}.")

def getat(self, distance, name):
return self.ancestor(distance).values[name.lexeme]
Expand Down
1 change: 1 addition & 0 deletions lox/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class ParseException(Exception):
pass


class RuntimeException(Exception):
def __init__(self, token, *args, **kwargs):
self.token = token
Expand Down
31 changes: 18 additions & 13 deletions lox/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@
from lox.exceptions import RuntimeException
from lox.LoxCallable import LoxCallable
from lox.LoxFunction import LoxFunction
from lox.native_functions import Clock
from lox.loxreturn import Return
from lox.native_functions import Clock
from lox.tokentype import TokenType
from lox.visitor import Visitor


def _is_true(obj):
'''Only `nil` and `false` are false. Everything else _evaluates to true.
'''
"""Only `nil` and `false` are false. Everything else _evaluates to true.
"""
if obj == None:
return False
elif isinstance(obj, bool):
return obj
return True


def _check_num_operand(op, *numbers):
for num in numbers:
if not isinstance(num, float):
raise RuntimeException(op, 'Operand must be a number.')
raise RuntimeException(op, "Operand must be a number.")


def stringify(obj):
if obj == None:
return 'nil'
return "nil"
if isinstance(obj, bool):
# Lox doesn't capitalize the starting 't' and 'f'.
if obj:
return 'true'
return 'false'
return "true"
return "false"
return str(obj)


class Interpreter(Visitor):
# self.environment points towards the current environment, whereas
# `global_env` always points to the outermost environment.
Expand Down Expand Up @@ -91,7 +95,8 @@ def visit_BinaryExpr(self, expr):
return left + right
except TypeError:
raise RuntimeException(
expr.operator, 'Operands must be two numbers or two strings.')
expr.operator, "Operands must be two numbers or two strings."
)
elif expr.operator.tokentype == TokenType.MINUS:
_check_num_operand(expr.operator, left, right)
return left - right
Expand Down Expand Up @@ -138,7 +143,7 @@ def visit_LogicalExpr(self, expr):
if expr.operator.tokentype == TokenType.OR:
if _is_true(left):
return left
else: # AND
else: # AND
if not _is_true(left):
return left
return self._evaluate(expr.right)
Expand All @@ -150,8 +155,7 @@ def visit_CallExpr(self, expr):
arguments.append(self._evaluate(argument))
# callee - what would be the type of callee?
if not isinstance(callee, LoxCallable):
raise RuntimeException(
expr.paren, "Can only call functions and classes.")
raise RuntimeException(expr.paren, "Can only call functions and classes.")

# The original author, while writing this compiler in Java typecasted
# the callee to LoxCallable, even after checking `isinstance(callee,
Expand All @@ -167,8 +171,9 @@ def visit_CallExpr(self, expr):

if len(arguments) != callee.arity:
raise RuntimeException(
expr.paren,
f"Expected {function.arity} arguments, but got {len(arguments)}.")
expr.paren,
f"Expected {function.arity} arguments, but got {len(arguments)}.",
)
return callee.__call__(self, arguments)

def visit_ExpressionStmt(self, stmt):
Expand Down
Loading