Skip to content

Commit

Permalink
Clean up packages and error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
chidiwilliams committed Mar 5, 2022
1 parent 2f850fe commit 37333ee
Show file tree
Hide file tree
Showing 13 changed files with 821 additions and 1,034 deletions.
89 changes: 89 additions & 0 deletions ast/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package ast

import (
"fmt"
)

type astPrinter struct{}

func (a astPrinter) VisitSuperExpr(expr SuperExpr) interface{} {
// TODO implement me
panic("implement me")
}

func (a astPrinter) VisitThisExpr(expr ThisExpr) interface{} {
return expr.Keyword.Lexeme
}

func (a astPrinter) VisitGetExpr(expr GetExpr) interface{} {
// TODO implement me
panic("implement me")
}

func (a astPrinter) VisitSetExpr(expr SetExpr) interface{} {
// TODO implement me
panic("implement me")
}

func (a astPrinter) VisitFunctionExpr(expr FunctionExpr) interface{} {
// TODO implement me
panic("implement me")
}

func (a astPrinter) VisitCallExpr(expr CallExpr) interface{} {
// TODO implement me
panic("implement me")
}

// print returns a string representation of an Expr node
func (a astPrinter) print(expr Expr) string {
return expr.Accept(a).(string)
}

func (a astPrinter) VisitAssignExpr(expr AssignExpr) interface{} {
return a.parenthesize("= "+expr.Name.Lexeme, expr.Value)
}

func (a astPrinter) VisitVariableExpr(expr VariableExpr) interface{} {
return expr.Name.Lexeme
}

func (a astPrinter) VisitTernaryExpr(expr TernaryExpr) interface{} {
return a.parenthesize("?:", expr.Cond, expr.Left, expr.Right)
}

func (a astPrinter) VisitBinaryExpr(expr BinaryExpr) interface{} {
return a.parenthesize(expr.Operator.Lexeme, expr.Left, expr.Right)
}

func (a astPrinter) VisitGroupingExpr(expr GroupingExpr) interface{} {
return a.parenthesize("group", expr.Expression)
}

func (a astPrinter) VisitLiteralExpr(expr LiteralExpr) interface{} {
if expr.Value == nil {
return "nil"
}

return fmt.Sprint(expr.Value)
}

func (a astPrinter) VisitUnaryExpr(expr UnaryExpr) interface{} {
return a.parenthesize(expr.Operator.Lexeme, expr.Right)
}

func (a astPrinter) VisitLogicalExpr(expr LogicalExpr) interface{} {
return a.parenthesize(expr.Operator.Lexeme, expr.Left, expr.Right)
}

func (a astPrinter) parenthesize(name string, exprs ...Expr) string {
var str string

str += "(" + name
for _, expr := range exprs {
str += " " + a.print(expr)
}
str += ")"

return str
}
91 changes: 0 additions & 91 deletions ast_printer.go

This file was deleted.

44 changes: 0 additions & 44 deletions error.go

This file was deleted.

2 changes: 1 addition & 1 deletion class.go → interpret/class.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package interpret

import (
"fmt"
Expand Down
6 changes: 4 additions & 2 deletions clock.go → interpret/clock.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main
package interpret

import "time"
import (
"time"
)

type clock struct{}

Expand Down
2 changes: 1 addition & 1 deletion env.go → interpret/env.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package interpret

import (
"fmt"
Expand Down
10 changes: 4 additions & 6 deletions callable.go → interpret/function.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main
package interpret

import "glox/ast"
import (
"glox/ast"
)

type callable interface {
arity() int
Expand Down Expand Up @@ -46,10 +48,6 @@ func (f function) call(interpreter *Interpreter, args []interface{}) (returnVal
return nil
}

func (f function) String() string {
return "<fn " + f.declaration.Name.Lexeme + ">"
}

func (f function) bind(i *instance) function {
env := environment{enclosing: f.closure}
env.define("this", i)
Expand Down
44 changes: 29 additions & 15 deletions interpreter.go → interpret/interpreter.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package interpret

import (
"fmt"
Expand All @@ -7,6 +7,15 @@ import (
"glox/ast"
)

type runtimeError struct {
token ast.Token
msg string
}

func (r runtimeError) Error() string {
return fmt.Sprintf("%s\n[line %d]", r.msg, r.token.Line)
}

// Interpreter holds the globals and current execution
// environment for a program to be executed
type Interpreter struct {
Expand Down Expand Up @@ -37,31 +46,35 @@ func NewInterpreter(stdOut io.Writer, stdErr io.Writer) *Interpreter {
}

// Interpret interprets a list of statements within the interpreter's environment
func (in *Interpreter) Interpret(stmts []ast.Stmt) interface{} {
func (in *Interpreter) Interpret(stmts []ast.Stmt) (result interface{}, hadRuntimeError bool) {
defer func() {
if err := recover(); err != nil {
if e, ok := err.(runtimeError); ok {
reportRuntimeErr(in.stdErr, e)
_, _ = in.stdErr.Write([]byte(e.Error()))
hadRuntimeError = true
} else {
fmt.Printf("Error: %s\n", err)
}
}
}()

var result interface{}
for _, statement := range stmts {
result = in.execute(statement)
}

return result
return
}

func (in Interpreter) error(token ast.Token, message string) {
panic(runtimeError{token: token, msg: message})
}

func (in *Interpreter) execute(stmt ast.Stmt) interface{} {
return stmt.Accept(in)
}

// resolve sets the depth of a local variable access
func (in *Interpreter) resolve(expr ast.Expr, depth int) {
// Resolve sets the depth of a local variable access
func (in *Interpreter) Resolve(expr ast.Expr, depth int) {
in.locals[expr] = depth
}

Expand All @@ -79,7 +92,7 @@ func (in *Interpreter) VisitClassStmt(stmt ast.ClassStmt) interface{} {
if stmt.Superclass != nil {
superclassValue, ok := in.evaluate(stmt.Superclass).(Class)
if !ok {
panic(runtimeError{token: stmt.Superclass.Name, msg: "Superclass must be a class."})
in.error(stmt.Superclass.Name, "Superclass must be a class.")
}
superclass = &superclassValue
}
Expand Down Expand Up @@ -246,12 +259,12 @@ func (in *Interpreter) VisitCallExpr(expr ast.CallExpr) interface{} {

fn, ok := (callee).(callable)
if !ok {
panic(runtimeError{token: expr.Paren, msg: "Can only call functions and classes."})
in.error(expr.Paren, "Can only call functions and classes.")
}

if len(args) != fn.arity() {
panic(runtimeError{token: expr.Paren,
msg: fmt.Sprintf("Expected %d arguments but got %d.", fn.arity(), len(args))})
in.error(expr.Paren,
fmt.Sprintf("Expected %d arguments but got %d.", fn.arity(), len(args)))
}

return fn.call(in, args)
Expand All @@ -266,7 +279,8 @@ func (in *Interpreter) VisitGetExpr(expr ast.GetExpr) interface{} {
}
return val
}
panic(runtimeError{token: expr.Name, msg: "Only instances have properties."})
in.error(expr.Name, "Only instances have properties.")
return nil
}

func (in *Interpreter) VisitVariableExpr(expr ast.VariableExpr) interface{} {
Expand Down Expand Up @@ -302,7 +316,7 @@ func (in *Interpreter) VisitBinaryExpr(expr ast.BinaryExpr) interface{} {
if leftIsString && rightIsString {
return left.(string) + right.(string)
}
panic(runtimeError{expr.Operator, "Operands must be two numbers or two strings"})
in.error(expr.Operator, "Operands must be two numbers or two strings")
case ast.TokenMinus:
if err := in.checkNumberOperands(expr.Operator, left, right); err != nil {
panic(err)
Expand Down Expand Up @@ -373,7 +387,7 @@ func (in *Interpreter) VisitSetExpr(expr ast.SetExpr) interface{} {

inst, ok := object.(*instance)
if !ok {
panic(runtimeError{token: expr.Name, msg: "Only instances have fields."})
in.error(expr.Name, "Only instances have fields.")
}

value := in.evaluate(expr.Value)
Expand All @@ -387,7 +401,7 @@ func (in *Interpreter) VisitSuperExpr(expr ast.SuperExpr) interface{} {
object := in.environment.getAt(distance-1, "this").(*instance)
method, ok := superclass.findMethod(expr.Method.Lexeme)
if !ok {
panic(runtimeError{token: expr.Method, msg: fmt.Sprintf("Undefined property '%s'.", expr.Method.Lexeme)})
in.error(expr.Method, fmt.Sprintf("Undefined property '%s'.", expr.Method.Lexeme))
}
return method.bind(object)
}
Expand Down
Loading

0 comments on commit 37333ee

Please sign in to comment.