Skip to content

Commit

Permalink
Add everything related to arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
dcrodman committed Jun 2, 2020
1 parent 3d19a5d commit 61b1173
Show file tree
Hide file tree
Showing 11 changed files with 447 additions and 60 deletions.
50 changes: 45 additions & 5 deletions ast/literals.go
Expand Up @@ -11,36 +11,36 @@ type Integer struct {
Value int64
}

func (e Integer) String() string { return e.Token.Value }
func (e *Integer) String() string { return e.Token.Value }

type Boolean struct {
Token token.Token
Value bool
}

func (e Boolean) String() string { return e.Token.Value }
func (e *Boolean) String() string { return e.Token.Value }

type String struct {
Token token.Token
Value string
}

func (e String) String() string { return e.Token.Value }
func (e *String) String() string { return e.Token.Value }

type Identifier struct {
Token token.Token
Value string
}

func (e Identifier) String() string { return e.Value }
func (e *Identifier) String() string { return e.Value }

type Function struct {
Token token.Token
Parameters []*Identifier
Body *BlockStatement
}

func (e Function) String() string {
func (e *Function) String() string {
var str strings.Builder

str.WriteString("func ")
Expand All @@ -58,6 +58,27 @@ func (e Function) String() string {
return str.String()
}

type Array struct {
Token token.Token
Elements []Expression
}

func (a *Array) String() string {
var str strings.Builder

str.WriteString("[")

var params []string
for _, e := range a.Elements {
params = append(params, e.String())
}

str.WriteString(strings.Join(params, ", "))
str.WriteString("]")

return str.String()
}

type PrefixExpression struct {
Token token.Token
Operator string
Expand Down Expand Up @@ -124,3 +145,22 @@ func (e CallExpression) String() string {

return str.String()
}

type IndexExpression struct {
Token token.Token
Left Expression
Index Expression
}

func (ie *IndexExpression) String() string {
var str strings.Builder

str.WriteString("(")
str.WriteString(ie.Left.String())
str.WriteString("[")
str.WriteString(ie.Index.String())
str.WriteString("]")
str.WriteString(")")

return str.String()
}
103 changes: 103 additions & 0 deletions evaluator/builtins.go
@@ -0,0 +1,103 @@
package evaluator

import "monkey-interpreter/object"

var builtins = map[string]*object.Builtin{
"len": {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}

var size int

switch arg := args[0].(type) {
case *object.String:
size = len(arg.Value)
case *object.Array:
size = len(arg.Elements)
default:
return newError("argument to `len` not supported, got %s", args[0].Type())
}

return &object.Integer{Value: int64(size)}
},
},
"first": {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}

switch arg := args[0].(type) {
case *object.Array:
if len(arg.Elements) == 0 {
return NULL
}
return arg.Elements[0]
default:
return newError("argument to `first` not supported, got %s", args[0].Type())
}
},
},
"last": {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}

switch arg := args[0].(type) {
case *object.Array:
if len(arg.Elements) == 0 {
return NULL
}
return arg.Elements[len(arg.Elements)-1]
default:
return newError("argument to `last` not supported, got %s", args[0].Type())
}
},
},
"tail": {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}

switch arg := args[0].(type) {
case *object.Array:
tailArray := make([]object.Object, len(arg.Elements)-1)

if len(arg.Elements) > 1 {
copy(tailArray, arg.Elements[1:])
}

return &object.Array{Elements: tailArray}
default:
return newError("argument to `tail` not supported, got %s", args[0].Type())
}
},
},
"push": {
Fn: func(args ...object.Object) object.Object {
if len(args) < 2 {
return newError("wrong number of arguments. got=%d, wanted at least 2", len(args))
}

switch arg := args[0].(type) {
case *object.Array:
newArray := make([]object.Object, len(arg.Elements)-1)
if len(arg.Elements) > 1 {
copy(newArray, arg.Elements[1:])
}

for _, element := range args[1:] {
newArray = append(newArray, element)
}

return &object.Array{Elements: newArray}
default:
return newError("argument to `push` not supported, got %s", args[0].Type())
}
},
},
}
73 changes: 59 additions & 14 deletions evaluator/evaluator.go
Expand Up @@ -17,14 +17,21 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
switch node := node.(type) {
case *ast.AST:
return evalProgram(node.Statements, env)
case ast.ExpressionStatement:
case *ast.ExpressionStatement:
return Eval(node.Expression, env)
case *ast.Integer:
return &object.Integer{Value: node.Value}
case *ast.Boolean:
return boolToBooleanObject(node.Value)
case *ast.String:
return &object.String{Value: node.Value}
case *ast.Array:
expressions := evalExpressions(node.Elements, env)

if len(expressions) > 0 && isError(expressions[0]) {
return expressions[0]
}
return &object.Array{Elements: expressions}
case *ast.PrefixExpression:
right := Eval(node.Right, env)
if isError(right) {
Expand Down Expand Up @@ -78,6 +85,18 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
}

return applyFunction(fn, args)
case *ast.IndexExpression:
left := Eval(node.Left, env)
if isError(left) {
return left
}

index := Eval(node.Index, env)
if isError(index) {
return index
}

return evalIndexExpression(left, index)
}
return nil
}
Expand Down Expand Up @@ -246,11 +265,15 @@ func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) obje
}

func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object {
val, ok := env.Get(node.Value)
if !ok {
return newError("identifier not found: " + node.Value)
if val, ok := env.Get(node.Value); ok {
return val
}

if builtin, ok := builtins[node.Value]; ok {
return builtin
}
return val

return newError("identifier not found: " + node.Value)
}

func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object {
Expand All @@ -269,14 +292,24 @@ func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Ob
}

func applyFunction(fn object.Object, args []object.Object) object.Object {
function, ok := fn.(*object.Function)
if !ok {
switch fn := fn.(type) {
case *object.Function:
extendedEnv := extendFunctionEnv(fn, args)
evaluated := Eval(fn.Body, extendedEnv)
return unwrapReturnValue(evaluated)
case *object.Builtin:
return fn.Fn(args...)
default:
return newError("not a function: %s", fn.Type())
}
}

func unwrapReturnValue(obj object.Object) object.Object {
if returnValue, ok := obj.(*object.Return); ok {
return returnValue.Value
}

extendedEnv := extendFunctionEnv(function, args)
evaluated := Eval(function.Body, extendedEnv)
return unwrapReturnValue(evaluated)
return obj
}

func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
Expand All @@ -289,12 +322,24 @@ func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Enviro
return env
}

func unwrapReturnValue(obj object.Object) object.Object {
if returnValue, ok := obj.(*object.Return); ok {
return returnValue.Value
func evalIndexExpression(left, index object.Object) object.Object {
switch {
case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
return evalArrayIndexExpression(left, index)
default:
return newError("index operator not supported: %s", left.Type())
}
}

return obj
func evalArrayIndexExpression(array, index object.Object) object.Object {
idx := index.(*object.Integer).Value
elements := array.(*object.Array).Elements

if idx < 0 || idx > int64(len(elements))-1 {
return newError(fmt.Sprintf("index %d exceeds bounds of array of length %d", idx, len(elements)))
}

return elements[idx]
}

func newError(format string, a ...interface{}) *object.Error {
Expand Down

0 comments on commit 61b1173

Please sign in to comment.