Skip to content

Commit

Permalink
ch3
Browse files Browse the repository at this point in the history
  • Loading branch information
0x-kys committed Nov 20, 2024
1 parent c2c7df3 commit 5effc26
Show file tree
Hide file tree
Showing 7 changed files with 551 additions and 17 deletions.
6 changes: 6 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ type CallExpression struct {
Arguments []Expression
}

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

func (ls *LetStatement) statementNode() {}
func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal }

Expand Down
243 changes: 234 additions & 9 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package evaluator
import (
"flare/ast"
"flare/object"
"fmt"
)

var (
Expand All @@ -11,38 +12,258 @@ var (
FALSE = &object.Boolean{Value: false}
)

func Eval(node ast.Node) object.Object {
func Eval(node ast.Node, env *object.Environment) object.Object {
switch node := node.(type) {
case *ast.Program:
return evalStatements(node.Statements)
return evalProgram(node, env)
case *ast.ExpressionStatement:
return Eval(node.Expression)
return Eval(node.Expression, env)
case *ast.Boolean:
return nativeBoolToBooleanObject(node.Value)
case *ast.IntegerLiteral:
return &object.Integer{Value: node.Value}
case *ast.PrefixExpression:
right := Eval(node.Right)
right := Eval(node.Right, env)
if isError(right) {
return right
}
return evalPrefixExpression(node.Operator, right)
case *ast.InfixExpression:
left := Eval(node.Left, env)
if isError(left) {
return left
}
right := Eval(node.Right, env)
if isError(right) {
return right
}
return evalInfixExpression(node.Operator, left, right)
case *ast.BlockStatement:
return evalBlockStatements(node, env)
case *ast.IfExpression:
return evalIfExpression(node, env)
case *ast.ReturnStatement:
val := Eval(node.ReturnValue, env)
if isError(val) {
return val
}
return &object.ReturnValue{Value: val}
case *ast.LetStatement:
val := Eval(node.Value, env)
if isError(val) {
return val
}
env.Set(node.Name.Value, val)
case *ast.Identifier:
return evalIdentifier(node, env)
case *ast.FunctionLiteral:
params := node.Parameters
body := node.Body
return &object.Function{Parameters: params, Env: env, Body: body}
case *ast.CallExpression:
function := Eval(node.Function, env)
if isError(function) {
return function
}
args := evalExpressions(node.Arguments, env)
if len(args) == 1 && isError(args[0]) {
return args[0]
}
return applyFunction(function, args)
}

return nil
}

func applyFunction(fn object.Object, args []object.Object) object.Object {
function, ok := fn.(*object.Function)
if !ok {
return newError("not a function: %s", fn.Type())
}

extendedEnv := extendedFunctionEnv(function, args)
evaluated := Eval(function.Body, extendedEnv)

return unwrapReturnValue(evaluated)
}

func extendedFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
env := object.NewEnclosedEnvironment(fn.Env)

for paramIndex, param := range fn.Parameters {
env.Set(param.Value, args[paramIndex])
}

return env
}

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

return obj
}

func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object {
var result []object.Object

for _, e := range exps {
evaluated := Eval(e, env)
if isError(evaluated) {
return []object.Object{evaluated}
}
result = append(result, evaluated)
}

return result
}

func isError(obj object.Object) bool {
if obj != nil {
return obj.Type() == object.ERROR_OBJ
}
return false
}

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)
}

return val
}

func newError(format string, a ...interface{}) *object.Error {
return &object.Error{Message: fmt.Sprintf(format, a...)}
}

func evalBlockStatements(block *ast.BlockStatement, env *object.Environment) object.Object {
var result object.Object

for _, statement := range block.Statements {
result = Eval(statement, env)

if result != nil {
rt := result.Type()
if rt == object.RETURN_VAL_OBJ || rt == object.ERROR_OBJ {
return result
}
}
}

return result
}

func evalProgram(program *ast.Program, env *object.Environment) object.Object {
var result object.Object

for _, statement := range program.Statements {
result = Eval(statement, env)

switch result := result.(type) {
case *object.ReturnValue:
return result.Value
case *object.Error:
return result
}
}

return result
}

func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object {
condition := Eval(ie.Condition, env)

if isError(condition) {
return condition
}

if isTruthy(condition) {
return Eval(ie.Consequence, env)
} else if ie.Alternative != nil {
return Eval(ie.Alternative, env)
} else {
return NULL
}
}

func isTruthy(obj object.Object) bool {
switch obj {
case NULL:
return false
case TRUE:
return true
case FALSE:
return false
default:
return true
}
}

func evalInfixExpression(operator string, left, right object.Object) object.Object {
if left.Type() != right.Type() {
return newError("type mismatch: %s %s %s", left.Type(), operator, right.Type())
}

switch left.Type() {
case object.INTEGER_OBJ:
return evalIntegerInfixExpression(operator, left, right)
case object.BOOLEAN_OBJ:
switch operator {
case "==":
return nativeBoolToBooleanObject(left == right)
case "!=":
return nativeBoolToBooleanObject(left != right)
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}

func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object {
leftVal := left.(*object.Integer).Value
rightVal := right.(*object.Integer).Value

switch operator {
case "+":
return &object.Integer{Value: leftVal + rightVal}
case "-":
return &object.Integer{Value: leftVal - rightVal}
case "*":
return &object.Integer{Value: leftVal * rightVal}
case "/":
return &object.Integer{Value: leftVal / rightVal}
case "<":
return nativeBoolToBooleanObject(leftVal < rightVal)
case ">":
return nativeBoolToBooleanObject(leftVal > rightVal)
case "==":
return nativeBoolToBooleanObject(leftVal == rightVal)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}

func evalPrefixExpression(operator string, right object.Object) object.Object {
switch operator {
case "!":
return evalBangOperatorExpression(right)
case "-":
return evalMinusPrefixOperatorExpression(right)
default:
return NULL
return newError("unknown operator: %s%s", operator, right.Type())
}
}

func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
if right.Type() != object.INTEGER_OBJ {
return NULL
return newError("unknown operator: -%s", right.Type())
}

value := right.(*object.Integer).Value
Expand Down Expand Up @@ -70,11 +291,15 @@ func nativeBoolToBooleanObject(input bool) *object.Boolean {
return FALSE
}

func evalStatements(stmts []ast.Statement) object.Object {
var result object.Object
func evalStatements(stmts []ast.Statement, env *object.Environment) object.Object {
var result object.Object = NULL

for _, statement := range stmts {
result = Eval(statement)
result = Eval(statement, env)

if returnValue, ok := result.(*object.ReturnValue); ok {
return returnValue.Value
}
}

return result
Expand Down
Loading

0 comments on commit 5effc26

Please sign in to comment.