Skip to content

Commit

Permalink
feat: add evaluation context
Browse files Browse the repository at this point in the history
  • Loading branch information
Houcine EL ADDALI committed Dec 20, 2023
1 parent ce2e7b4 commit ec81a40
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 39 deletions.
6 changes: 5 additions & 1 deletion cmd/REPL/repel.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/houcine7/JIPL/internal/lexer"
"github.com/houcine7/JIPL/internal/parser"
"github.com/houcine7/JIPL/internal/runtime"
"github.com/houcine7/JIPL/internal/types"
)

// REPL :Read --> Evaluate --> Print --> loop
Expand All @@ -22,6 +23,8 @@ import (

const PROMPT = "🟢>"

var GLOBAL_CONTEXT = types.NewContext()

func Start(in io.Reader, out io.Writer) {
scanner := bufio.NewScanner(in)

Expand Down Expand Up @@ -61,7 +64,8 @@ func Start(in io.Reader, out io.Writer) {
continue
}

evaluated ,err:= runtime.Eval(pr)

evaluated ,err:= runtime.Eval(pr,GLOBAL_CONTEXT)
if err != debug.NOERROR {
io.WriteString(out, fmt.Sprintf("error while evaluating your input: %s \n", err.Error()))
continue
Expand Down
84 changes: 54 additions & 30 deletions internal/runtime/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,85 @@ import (
"github.com/houcine7/JIPL/internal/types"
)

func Eval(node ast.Node) (types.ObjectJIPL, *debug.Error) {


func Eval(node ast.Node, ctx *types.Context) (types.ObjectJIPL, *debug.Error) {
switch node := node.(type) {
case *ast.Program:
return evalAllProgramStatements(node.Statements)
return evalAllProgramStatements(node.Statements,ctx)
case *ast.ExpressionStatement:
return Eval(node.Expression)
return Eval(node.Expression,ctx)
case *ast.ReturnStatement:
value,err := Eval(node.ReturnValue)
value,err := Eval(node.ReturnValue,ctx)
if err != debug.NOERROR {
return nil,err
}
return &types.Return{Val: value},err
case *ast.DefStatement:
val ,err := Eval(node.Value,ctx)
if err != debug.NOERROR {
return nil,err
}
ctx.Set(node.Name.Value,val)
return val,err
case *ast.Identifier:
return evalIdentifier(node,ctx)
case *ast.IfExpression:
return evalIfExpression(node)
return evalIfExpression(node,ctx)
case *ast.BlockStm:
return evalABlockStatements(node.Statements)
return evalABlockStatements(node.Statements,ctx)
case *ast.IntegerLiteral:
return &types.Integer{Val: node.Value},debug.NOERROR
case *ast.BooleanExp:
return types.BoolToObJIPL(node.Value),debug.NOERROR
case *ast.PrefixExpression:
operand,_ := Eval(node.Right)
operand,_ := Eval(node.Right,ctx)
return evalPrefixExpression(node.Operator, operand)
case *ast.PostfixExpression:
operand, _:= Eval(node.Left)
operand, _:= Eval(node.Left,ctx)
return evalPostfixExpression(node.Operator, operand)
case *ast.InfixExpression:
leftOperand,_ := Eval(node.Left)
rightOperand,_ := Eval(node.Right)
leftOperand,_ := Eval(node.Left,ctx)
rightOperand,_ := Eval(node.Right,ctx)
return evalInfixExpression(node.Operator,leftOperand,rightOperand)
default:
return nil,debug.NewError("unknown ast node type")
}
}

func Eval2(node ast.Node) (types.ObjectJIPL,*debug.Error) {
func Eval2(node ast.Node,ctx *types.Context) (types.ObjectJIPL,*debug.Error) {
switch node := node.(type) {
case *ast.Program:
return evalAllProgramStatements(node.Statements)
return evalAllProgramStatements(node.Statements,ctx )
case *ast.ExpressionStatement:
return Eval2(node.Expression)
return Eval2(node.Expression,ctx)
case *ast.IntegerLiteral:
return &types.Integer{Val: node.Value},debug.NOERROR
default:
return nil,debug.NewError("unknown node type")
}
}

func evalIfExpression(ifExp *ast.IfExpression) (types.ObjectJIPL , *debug.Error) {
condition , _ := Eval(ifExp.Condition)



func evalIdentifier(node *ast.Identifier,ctx *types.Context) (types.ObjectJIPL, *debug.Error) {
val,ok := ctx.Get(node.Value)
if !ok {
return nil, debug.NewError(fmt.Sprintf("identifier not found: %s",
node.Value))
}
return val,debug.NOERROR
}


func evalIfExpression(ifExp *ast.IfExpression,ctx *types.Context) (types.ObjectJIPL , *debug.Error) {
condition , _ := Eval(ifExp.Condition,ctx)
if condition == types.TRUE {
return Eval(ifExp.Body)
return Eval(ifExp.Body,ctx)
}
if ifExp.ElseBody != nil {
return Eval(ifExp.ElseBody)
return Eval(ifExp.ElseBody,ctx)
}
return nil, debug.NewError("if condition is not a met and no else body")
}
Expand Down Expand Up @@ -133,16 +160,15 @@ func evalIntInfixExpression(operator string, left, right types.ObjectJIPL) (typ
}



func evalForLoopExpression(forLoop *ast.ForLoopExpression)( types.ObjectJIPL, *debug.Error){
func evalForLoopExpression(forLoop *ast.ForLoopExpression,ctx *types.Context)( types.ObjectJIPL, *debug.Error){
// the init statement
Eval(forLoop.InitStm)
Eval(forLoop.InitStm,ctx)
// the condition
condition,_ := Eval(forLoop.Condition)
condition,_ := Eval(forLoop.Condition,ctx)
for condition == types.TRUE {
Eval(forLoop.Body)
Eval(forLoop.PostIteration)
condition,_ = Eval(forLoop.Condition)
Eval(forLoop.Body,ctx)
Eval(forLoop.PostIteration,ctx)
condition,_ = Eval(forLoop.Condition,ctx)
}
return nil,debug.NOERROR
}
Expand Down Expand Up @@ -175,12 +201,12 @@ func evalDecrementPostfix(operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Er
intObj := operand.(*types.Integer)
return &types.Integer{Val: intObj.Val-1},debug.NOERROR
}
func evalAllProgramStatements(stms []ast.Statement)( types.ObjectJIPL , *debug.Error) {
func evalAllProgramStatements(stms []ast.Statement,ctx *types.Context)( types.ObjectJIPL , *debug.Error) {
var result types.ObjectJIPL
var err *debug.Error = debug.NOERROR

for _, stm := range stms {
result,err = Eval(stm)
result,err = Eval(stm,ctx)
if err != debug.NOERROR {
return nil, err
}
Expand All @@ -192,12 +218,12 @@ func evalAllProgramStatements(stms []ast.Statement)( types.ObjectJIPL , *debug.E
return result,debug.NOERROR
}

func evalABlockStatements(stms []ast.Statement) (types.ObjectJIPL , *debug.Error) {
func evalABlockStatements(stms []ast.Statement,ctx *types.Context) (types.ObjectJIPL , *debug.Error) {
var result types.ObjectJIPL
var err *debug.Error = debug.NOERROR

for _, stm := range stms {
result, err= Eval(stm)
result, err= Eval(stm,ctx)
if err != debug.NOERROR {
return nil, err
}
Expand All @@ -209,8 +235,6 @@ func evalABlockStatements(stms []ast.Statement) (types.ObjectJIPL , *debug.Error
}




func evalPrefixExpression(operator string, operand types.ObjectJIPL) (types.ObjectJIPL, * debug.Error){
switch operator {
case "!":
Expand Down
30 changes: 22 additions & 8 deletions internal/runtime/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ import (
"github.com/houcine7/JIPL/internal/types"
)



func TestDefEval(t *testing.T) {
input := defEval
for _, test := range input {
evaluated := getEvaluated(test.input)
testIntegerObject(t, evaluated, test.expected)
}
}

func TestIntegerEval(t *testing.T) {
testData := intTestData
for _, test := range testData {
Expand All @@ -16,7 +26,6 @@ func TestIntegerEval(t *testing.T) {
}
}


func TestBooleanEval(t *testing.T) {
testData := boolInputData

Expand All @@ -41,7 +50,6 @@ func TestReturnEval(t *testing.T) {
}
}


func TestIfElseEval(t *testing.T) {
input := `
if (10 > 5) {
Expand All @@ -62,8 +70,6 @@ func TestIfElseEval(t *testing.T) {
}
}



///
func testBooleanObject(t *testing.T, evaluated types.ObjectJIPL, expected bool) {
boolObj, ok := evaluated.(*types.Boolean)
Expand All @@ -80,7 +86,8 @@ func getEvaluated(input string) types.ObjectJIPL {
l := lexer.InitLexer(input)
p := parser.InitParser(l)
program := p.Parse()
ev,_:=Eval(program)
ctx := types.NewContext()
ev,_:=Eval(program,ctx)
return ev
}

Expand All @@ -96,9 +103,6 @@ func testIntegerObject(t *testing.T, obj types.ObjectJIPL, expected int) {
}
}




// data
var (
boolInputData = []struct {
Expand All @@ -125,4 +129,14 @@ var (
{"7;", 7},
}

defEval = []struct {
input string
expected int
}{
{"def var1 = 0; var1;",0},
{"def var2 = 1; var2;",1},
{"def var3 = 5 * 4; var3;",20},
{"def var4 = 1;def var5 =2; var4+var5;",3},
{"def var6=1; def var7=7+var6; var7;",8},
}
)
21 changes: 21 additions & 0 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,28 @@ type Return struct {
Val ObjectJIPL
}

type Context struct {
Store map[string]ObjectJIPL
Outer *Context // for nested scopes
}

func (ctx *Context) Get(key string) (ObjectJIPL, bool) {
val, ok := ctx.Store[key]
if !ok && ctx.Outer != nil {
return ctx.Outer.Get(key)
}
return val, ok
}

func (ctx *Context) Set(key string, val ObjectJIPL) ObjectJIPL {
ctx.Store[key] = val
return val
}


func NewContext() *Context {
return &Context{Store: make(map[string]ObjectJIPL)}
}

// implementing OBjectJIPL interface by supported types
func (ret *Return) ToString() string {
Expand Down

0 comments on commit ec81a40

Please sign in to comment.