Skip to content

Commit

Permalink
Merge pull request #62 from JunNishimura/feature/add_return_break_con…
Browse files Browse the repository at this point in the history
…tinue

add break, continue and return expression
  • Loading branch information
JunNishimura authored Oct 14, 2024
2 parents 8d955c6 + 151dbf5 commit 947eab1
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 15 deletions.
51 changes: 46 additions & 5 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
)

var (
Null = &object.Null{}
True = &object.Boolean{Value: true}
False = &object.Boolean{Value: false}
Null = &object.Null{}
Break = &object.Break{}
Continue = &object.Continue{}
True = &object.Boolean{Value: true}
False = &object.Boolean{Value: false}
)

const identEmbedPattern = `\{\s*\$\w+\s*\}`
Expand Down Expand Up @@ -52,7 +54,7 @@ func Eval(exp ast.Expression, env *object.Environment) object.Object {
}
}

func evalArray(array *ast.Array, env *object.Environment) *object.Array {
func evalArray(array *ast.Array, env *object.Environment) object.Object {
result := &object.Array{
Elements: []object.Object{},
}
Expand All @@ -62,6 +64,9 @@ func evalArray(array *ast.Array, env *object.Environment) *object.Array {
if isError(evaluated) {
return &object.Array{Elements: []object.Object{evaluated}}
}
if returnValue, ok := evaluated.(*object.ReturnValue); ok {
return returnValue
}
result.Elements = append(result.Elements, evaluated)
}

Expand Down Expand Up @@ -131,6 +136,16 @@ func evalKeyValueObject(kv *ast.KeyValueObject, env *object.Environment) object.
return evalLoopExpression(value, env)
case "lambda":
return evalLambdaExpression(value, env)
case "break":
return Break
case "continue":
return Continue
case "return":
evaluated := Eval(value, env)
if isError(evaluated) {
return evaluated
}
return &object.ReturnValue{Value: evaluated}
}
}

Expand Down Expand Up @@ -225,12 +240,22 @@ func applyFunction(function object.Object, args object.Object) object.Object {
return newError("failed to apply function: %s", err)
}

return Eval(funcType.Body, extendedEnv)
evaluated := Eval(funcType.Body, extendedEnv)
return unwrapReturnValue(evaluated)
default:
return newError("not a function: %s", function.Type())
}
}

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

return obj
}

func evalIfExpression(exp ast.Expression, env *object.Environment) object.Object {
keyValueObj, ok := exp.(*ast.KeyValueObject)
if !ok {
Expand Down Expand Up @@ -384,6 +409,14 @@ func evalFromUntilLoop(keyValueObj *ast.KeyValueObject, env *object.Environment)
return evaluated
}

if evaluated == Break {
break
} else if evaluated == Continue {
continue
} else if returnValue, ok := evaluated.(*object.ReturnValue); ok {
return returnValue
}

result = evaluated
}

Expand Down Expand Up @@ -462,6 +495,14 @@ func evalInLoop(keyValueObj *ast.KeyValueObject, env *object.Environment) object
return evaluated
}

if evaluated == Break {
break
} else if evaluated == Continue {
continue
} else if returnValue, ok := evaluated.(*object.ReturnValue); ok {
return returnValue
}

result = evaluated
}

Expand Down
126 changes: 126 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,72 @@ func TestLoopExpression(t *testing.T) {
]`,
expected: 60,
},
{
name: "break and continue in loop",
input: `
[
{
"set": {
"var": "$sum",
"val": 0
}
},
{
"loop": {
"for": "$i",
"from": 1,
"until": 15,
"do": {
"if": {
"cond": {
"command": {
"symbol": ">",
"args": ["$i", 10]
}
},
"conseq": {
"break": {}
},
"alt": {
"if": {
"cond": {
"command": {
"symbol": "==",
"args": [
{
"command": {
"symbol": "%",
"args": ["$i", 2]
}
},
0
]
}
},
"conseq": {
"set": {
"var": "$sum",
"val": {
"command": {
"symbol": "+",
"args": ["$sum", "$i"]
}
}
}
},
"alt": {
"continue": {}
}
}
}
}
}
}
},
"$sum"
]`,
expected: 30,
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -713,6 +779,66 @@ func TestLambdaExpression(t *testing.T) {
]`,
expected: 6,
},
{
name: "lambda expression with return statement",
input: `
[
{
"set": {
"var": "$f",
"val": {
"lambda": {
"body": [
{
"set": {
"var": "$sum",
"val": 0
}
},
{
"loop": {
"for": "$i",
"from": 1,
"until": 11,
"do": {
"if": {
"cond": {
"command": {
"symbol": ">",
"args": ["$i", 5]
}
},
"conseq": {
"return": "$sum"
},
"alt": {
"set": {
"var": "$sum",
"val": {
"command": {
"symbol": "+",
"args": ["$sum", "$i"]
}
}
}
}
}
}
}
}
]
}
}
}
},
{
"command": {
"symbol": "$f"
}
}
]`,
expected: 15,
},
}

for _, tt := range tests {
Expand Down
55 changes: 55 additions & 0 deletions examples/func_return.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[
{
"set": {
"var": "$f",
"val": {
"lambda": {
"body": [
{
"set": {
"var": "$sum",
"val": 0
}
},
{
"loop": {
"for": "$i",
"from": 1,
"until": 11,
"do": {
"if": {
"cond": {
"command": {
"symbol": ">",
"args": ["$i", 5]
}
},
"conseq": {
"return": "$sum"
},
"alt": {
"set": {
"var": "$sum",
"val": {
"command": {
"symbol": "+",
"args": ["$sum", "$i"]
}
}
}
}
}
}
}
}
]
}
}
}
},
{
"command": {
"symbol": "$f"
}
}
]
61 changes: 61 additions & 0 deletions examples/loop_break_continue.jsop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
[
{
"set": {
"var": "$sum",
"val": 0
}
},
{
"loop": {
"for": "$i",
"from": 1,
"until": 15,
"do": {
"if": {
"cond": {
"command": {
"symbol": ">",
"args": ["$i", 10]
}
},
"conseq": {
"break": {}
},
"alt": {
"if": {
"cond": {
"command": {
"symbol": "==",
"args": [
{
"command": {
"symbol": "%",
"args": ["$i", 2]
}
},
0
]
}
},
"conseq": {
"set": {
"var": "$sum",
"val": {
"command": {
"symbol": "+",
"args": ["$sum", "$i"]
}
}
}
},
"alt": {
"continue": {}
}
}
}
}
}
}
},
"$sum"
]
18 changes: 18 additions & 0 deletions object/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@ func (e *Environment) Get(name string) (Object, bool) {
}

func (e *Environment) Set(name string, val Object) Object {
// check if the variable already exists in the current environment
// current environment has higher priority than outer environment
if _, ok := e.store[name]; ok {
// if it does, update the value
e.store[name] = val
return val
}

// check if the variable exists in the outer environment
if e.outer != nil {
if _, ok := e.outer.Get(name); ok {
// if it does, update the value
e.outer.Set(name, val)
return val
}
}

// create a new variable in the current environment
e.store[name] = val
return val
}
Loading

0 comments on commit 947eab1

Please sign in to comment.