Skip to content

Commit

Permalink
Fix env type
Browse files Browse the repository at this point in the history
  • Loading branch information
chidiwilliams committed Jul 11, 2022
1 parent ca4985a commit b4b68bd
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 29 deletions.
4 changes: 2 additions & 2 deletions env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ type Environment struct {
}

// New returns a new environment enclosed by the given environment
func New(enclosing *Environment) Environment {
return Environment{Enclosing: enclosing, values: make(map[string]interface{})}
func New(enclosing *Environment) *Environment {
return &Environment{Enclosing: enclosing, values: make(map[string]interface{})}
}

// Define stores a new key-value pair
Expand Down
2 changes: 1 addition & 1 deletion interpret/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (f function) bind(i *instance) function {
closureEnv.Define("this", i)
return function{
declaration: f.declaration,
closure: &closureEnv,
closure: closureEnv,
isInitializer: f.isInitializer,
}
}
Expand Down
13 changes: 6 additions & 7 deletions interpret/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Interpreter struct {
// current execution environment
environment *env.Environment
// global variables
globals env.Environment
globals *env.Environment
// standard output
stdOut io.Writer
// standard error
Expand All @@ -41,7 +41,7 @@ func NewInterpreter(stdOut io.Writer, stdErr io.Writer) *Interpreter {

return &Interpreter{
globals: globals,
environment: &globals,
environment: globals,
stdOut: stdOut,
stdErr: stdErr,
locals: make(map[string]int),
Expand Down Expand Up @@ -109,8 +109,7 @@ func (in *Interpreter) VisitClassStmt(stmt ast.ClassStmt) interface{} {
in.environment.Define(stmt.Name.Lexeme, nil)

if superclass != nil {
superEnv := env.New(in.environment)
in.environment = &superEnv
in.environment = env.New(in.environment)
in.environment.Define("super", superclass)
}

Expand Down Expand Up @@ -367,7 +366,7 @@ func (in *Interpreter) VisitBinaryExpr(expr ast.BinaryExpr) interface{} {
// current environment. The name of the function expression is defined within its block.
func (in *Interpreter) VisitFunctionExpr(expr ast.FunctionExpr) interface{} {
closureEnv := env.New(in.environment)
fn := functionExpr{declaration: expr, closure: &closureEnv}
fn := functionExpr{declaration: expr, closure: closureEnv}
if expr.Name != nil {
fn.closure.Define(expr.Name.Lexeme, fn)
}
Expand Down Expand Up @@ -435,14 +434,14 @@ func (in *Interpreter) VisitTernaryExpr(expr ast.TernaryExpr) interface{} {
return in.evaluate(expr.Alternate)
}

func (in *Interpreter) executeBlock(statements []ast.Stmt, env env.Environment) {
func (in *Interpreter) executeBlock(statements []ast.Stmt, env *env.Environment) {
// Restore the current environment after executing the block
previous := in.environment
defer func() {
in.environment = previous
}()

in.environment = &env
in.environment = env
for _, statement := range statements {
in.execute(statement)
}
Expand Down
77 changes: 63 additions & 14 deletions typechecker/typechecker.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func NewTypeChecker(interpreter *interpret.Interpreter) *TypeChecker {
"nil": typeNil,
}

return &TypeChecker{env: &globals, globals: &globals, interpreter: interpreter, types: types}
return &TypeChecker{env: globals, globals: globals, interpreter: interpreter, types: types}
}

type TypeChecker struct {
Expand Down Expand Up @@ -52,14 +52,14 @@ func (c *TypeChecker) VisitBlockStmt(stmt ast.BlockStmt) interface{} {
return nil
}

func (c *TypeChecker) checkBlock(stmts []ast.Stmt, env env.Environment) {
func (c *TypeChecker) checkBlock(stmts []ast.Stmt, env *env.Environment) {
// Restore the current environment after executing the block
previous := c.env
defer func() {
c.env = previous
}()

c.env = &env
c.env = env
for _, stmt := range stmts {
c.checkStmt(stmt)
}
Expand All @@ -83,19 +83,59 @@ func (c *TypeChecker) VisitClassStmt(stmt ast.ClassStmt) interface{} {
c.enclosingClass = previousClass
}()

c.env = classType.env
c.env = env.New(previous)
c.enclosingClass = classType

// What should the actual type of classType be? Shouldn't it be a function
// callable by its initializer's args to return the class type?
c.env.Define("this", classType)

for _, method := range stmt.Methods {
c.checkStmt(method)
c.checkMethod(method)
}

return nil
}

func (c *TypeChecker) checkMethod(stmt ast.FunctionStmt) functionType {
// Check the method within the current environment
// Save the name and return type in the class's properties

params := stmt.Params
parsedReturnType := stmt.ReturnType
name := &stmt.Name
enclosingEnv := c.env
body := stmt.Body

// Predefine function type from signature for recursive calls
paramTypes := make([]loxType, len(params))
for i, param := range params {
paramTypes[i] = c.typeFromParsed(param.Type)
}

var returnType loxType
if name != nil && (*name).Lexeme == "init" {
returnType = c.enclosingClass
} else {
returnType = c.typeFromParsed(parsedReturnType)
}

if name != nil {
c.enclosingClass.properties.Define(name.Lexeme, newFunctionType("", paramTypes, returnType))
}

fnEnv := env.New(enclosingEnv)
for i, param := range params {
fnEnv.Define(param.Token.Lexeme, paramTypes[i])
}

inferredReturnType := c.checkFunctionBody(body, fnEnv, returnType)

if parsedReturnType != nil && !inferredReturnType.equals(returnType) {
panic(typeError{message: fmt.Sprintf("expected function %s to return %s, but got %s", body, parsedReturnType, inferredReturnType)})
}

return functionType{paramTypes: paramTypes, returnType: inferredReturnType}
}

func (c *TypeChecker) VisitExpressionStmt(stmt ast.ExpressionStmt) interface{} {
c.check(stmt.Expr)
return nil
Expand Down Expand Up @@ -253,8 +293,7 @@ func (c *TypeChecker) checkFunctionCall(calleeType functionType, expr ast.CallEx
}

func (c *TypeChecker) VisitFunctionExpr(expr ast.FunctionExpr) interface{} {
fnDeclEnv := env.New(c.env)
return c.checkFunction(expr.Name, expr.Params, expr.Body, expr.ReturnType, &fnDeclEnv)
return c.checkFunction(expr.Name, expr.Params, expr.Body, expr.ReturnType, env.New(c.env))
}

func (c *TypeChecker) checkFunction(name *ast.Token, params []ast.Param, body []ast.Stmt, parsedReturnType ast.Type, enclosingEnv *env.Environment) functionType {
Expand Down Expand Up @@ -283,7 +322,7 @@ func (c *TypeChecker) checkFunction(name *ast.Token, params []ast.Param, body []
return functionType{paramTypes: paramTypes, returnType: inferredReturnType}
}

func (c *TypeChecker) checkFunctionBody(fnBody []ast.Stmt, fnEnv env.Environment, declaredReturnType loxType) loxType {
func (c *TypeChecker) checkFunctionBody(fnBody []ast.Stmt, fnEnv *env.Environment, declaredReturnType loxType) loxType {
previousEnclosingFnReturnType := c.declaredFnReturnType
defer func() { c.declaredFnReturnType = previousEnclosingFnReturnType }()

Expand Down Expand Up @@ -323,11 +362,21 @@ func (c *TypeChecker) VisitLogicalExpr(expr ast.LogicalExpr) interface{} {
}

func (c *TypeChecker) VisitSetExpr(expr ast.SetExpr) interface{} {
// TODO: check that the object is an instance of a class
// and that object[name] has the same type as value
c.check(expr.Object)
object := c.check(expr.Object)

objectAsClassType, ok := object.(classType)
if !ok {
panic(typeError{message: "cannot set properties on a non-class type"})
}

property, err := objectAsClassType.properties.Get(expr.Name.Lexeme)
if err != nil {
c.error("property does not exist on class")
}

return c.check(expr.Value)
valueType := c.check(expr.Value)
c.expect(valueType, property.(loxType), expr.Value, expr)
return valueType
}

func (c *TypeChecker) VisitSuperExpr(expr ast.SuperExpr) interface{} {
Expand Down
10 changes: 5 additions & 5 deletions typechecker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (t aliasType) equals(t2 loxType) bool {
type classType struct {
name string
superClass loxType
env *env.Environment
properties *env.Environment
}

func (t classType) String() string {
Expand All @@ -145,7 +145,7 @@ func (t classType) equals(t2 loxType) bool {
}

func (t classType) getField(name string) (loxType, error) {
fieldType, err := t.env.Get(name)
fieldType, err := t.properties.Get(name)
if err != nil {
return nil, err
}
Expand All @@ -165,13 +165,13 @@ func (t classType) getConstructor() (functionType, error) {
func newClassType(name string, superClass loxType) classType {
var enclosingEnv *env.Environment
if superClassAsClassType, ok := superClass.(classType); ok {
enclosingEnv = superClassAsClassType.env
enclosingEnv = superClassAsClassType.properties
}

environment := env.New(enclosingEnv)
properties := env.New(enclosingEnv)
return classType{
name: name,
superClass: superClass,
env: &environment,
properties: properties,
}
}

0 comments on commit b4b68bd

Please sign in to comment.