Skip to content

Commit

Permalink
Interpret variables assignment and evaluation.
Browse files Browse the repository at this point in the history
  • Loading branch information
iamsayantan committed Apr 12, 2022
1 parent 174c23d commit 4175b71
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 42 deletions.
71 changes: 33 additions & 38 deletions ast_printer.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
package glox

import (
"fmt"
"strings"
)

type AstPrinter struct{}

func (ap *AstPrinter) Print(expr Expr) (string, error) {
val, err := expr.Accept(ap)
if err != nil {
return "", err
}
// func (ap *AstPrinter) Print(expr Expr) (string, error) {
// val, err := expr.Accept(ap)
// if err != nil {
// return "", err
// }

return val.(string), nil
}
// return val.(string), nil
// }

func (ap *AstPrinter) VisitBinaryExpr(expr *Binary) (interface{}, error) {
return ap.parenthesize(expr.Operator.Lexeme, expr.Left, expr.Right), nil
}
// func (ap *AstPrinter) VisitBinaryExpr(expr *Binary) (interface{}, error) {
// return ap.parenthesize(expr.Operator.Lexeme, expr.Left, expr.Right), nil
// }

func (ap *AstPrinter) VisitGroupingExpr(expr *Grouping) (interface{}, error) {
return ap.parenthesize("group", expr.Expression), nil
}
// func (ap *AstPrinter) VisitGroupingExpr(expr *Grouping) (interface{}, error) {
// return ap.parenthesize("group", expr.Expression), nil
// }

func (ap *AstPrinter) VisitLiteralExpr(expr *Literal) (interface{}, error) {
if expr.Value == nil {
return "nil", nil
}
// func (ap *AstPrinter) VisitLiteralExpr(expr *Literal) (interface{}, error) {
// if expr.Value == nil {
// return "nil", nil
// }

return fmt.Sprintf("%v", expr.Value), nil
}
// return fmt.Sprintf("%v", expr.Value), nil
// }

func (ap *AstPrinter) VisitUnaryExpr(expr *Unary) (interface{}, error) {
return ap.parenthesize(expr.Operator.Lexeme, expr.Right), nil
}
// func (ap *AstPrinter) VisitUnaryExpr(expr *Unary) (interface{}, error) {
// return ap.parenthesize(expr.Operator.Lexeme, expr.Right), nil
// }

func (ap *AstPrinter) parenthesize(name string, exprs ...Expr) string {
s := strings.Builder{}
s.WriteString("(" + name)
// func (ap *AstPrinter) parenthesize(name string, exprs ...Expr) string {
// s := strings.Builder{}
// s.WriteString("(" + name)

for _, expr := range exprs {
s.WriteString(" ")
val,_ := expr.Accept(ap)
s.WriteString(val.(string))
}
// for _, expr := range exprs {
// s.WriteString(" ")
// val,_ := expr.Accept(ap)
// s.WriteString(val.(string))
// }

s.WriteString(")")
return s.String()
}
// s.WriteString(")")
// return s.String()
// }
26 changes: 26 additions & 0 deletions environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package glox

type Environment struct {
// values uses string for the keys and not Token because token represents
// a unit of code at a specific place in the source text, but when it comes
// to variables, all identifier tokens using the same name should refer to
// the same variable (ignorig scope for now).
values map[string]interface{}
}

func NewEnvironment() *Environment {
return &Environment{values: make(map[string]interface{}, 0)}
}

func (e *Environment) Define(name string, value interface{}) {
e.values[name] = value
}

func (e *Environment) Get(name Token) (interface{}, error) {
val, ok := e.values[name.Lexeme]
if !ok {
return nil, NewRuntimeError(name, "Undefined variable '" + name.Lexeme + "'")
}

return val, nil
}
33 changes: 31 additions & 2 deletions interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
)

type Interpreter struct {
runtime *Runtime
runtime *Runtime
environment *Environment
}

func NewInterpreter(runtime *Runtime) *Interpreter {
return &Interpreter{runtime: runtime}
return &Interpreter{runtime: runtime, environment: NewEnvironment()}
}

type RuntimeError struct {
Expand Down Expand Up @@ -47,6 +48,34 @@ func (i *Interpreter) execute(stmt Stmt) error {
return nil
}

// VisitVarStmt interprets an variable declaration. If the variable has an
// initialization part, we first evaluate it, otherwise we store the default
// nil value for it. Thus it allows us to define an uninitialized variable.
// Like other dynamically typed languages, we just assign nil if the variable
// is not initialized.
func (i *Interpreter) VisitVarStmt(expr *VarStmt) error {
var val interface{}
var err error
if expr.Initializer != nil {
val, err = i.evaluate(expr.Initializer)
if err != nil {
return err
}
}

i.environment.Define(expr.Name.Lexeme, val)
return nil
}

func (i *Interpreter) VisitVarExpr(expr *VarExpr) (interface{}, error) {
val, err := i.environment.Get(expr.Name)
if err != nil {
return nil, err
}

return val, nil
}

// VisitExpressionExpr interprets expression statements. As statements do not
// produce any value, we are discarding the expression generated from evaluating
// the statement's expression.
Expand Down
4 changes: 2 additions & 2 deletions stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Stmt interface {
type StmtVisitor interface {
VisitExpressionExpr(expr *Expression) error
VisitPrintExpr(expr *Print) error
VisitVariableExpr(expr *VarStmt) error
VisitVarStmt(expr *VarStmt) error
}

type Expression struct {
Expand All @@ -37,5 +37,5 @@ type VarStmt struct {
}

func (v *VarStmt) Accept(visitor StmtVisitor) error {
return visitor.VisitVariableExpr(v)
return visitor.VisitVarStmt(v)
}

0 comments on commit 4175b71

Please sign in to comment.