diff --git a/cmd/REPL/repel.go b/cmd/REPL/repel.go index a943b85..402653a 100644 --- a/cmd/REPL/repel.go +++ b/cmd/REPL/repel.go @@ -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 @@ -22,6 +23,8 @@ import ( const PROMPT = "🟢>" +var GLOBAL_CONTEXT = types.NewContext() + func Start(in io.Reader, out io.Writer) { scanner := bufio.NewScanner(in) @@ -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 diff --git a/internal/runtime/eval.go b/internal/runtime/eval.go index 6fc37d7..b5a1f26 100644 --- a/internal/runtime/eval.go +++ b/internal/runtime/eval.go @@ -8,44 +8,58 @@ 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: @@ -53,13 +67,26 @@ func Eval2(node ast.Node) (types.ObjectJIPL,*debug.Error) { } } -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") } @@ -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 } @@ -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 } @@ -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 } @@ -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 "!": diff --git a/internal/runtime/eval_test.go b/internal/runtime/eval_test.go index aae932b..f628e27 100644 --- a/internal/runtime/eval_test.go +++ b/internal/runtime/eval_test.go @@ -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 { @@ -16,7 +26,6 @@ func TestIntegerEval(t *testing.T) { } } - func TestBooleanEval(t *testing.T) { testData := boolInputData @@ -41,7 +50,6 @@ func TestReturnEval(t *testing.T) { } } - func TestIfElseEval(t *testing.T) { input := ` if (10 > 5) { @@ -62,8 +70,6 @@ func TestIfElseEval(t *testing.T) { } } - - /// func testBooleanObject(t *testing.T, evaluated types.ObjectJIPL, expected bool) { boolObj, ok := evaluated.(*types.Boolean) @@ -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 } @@ -96,9 +103,6 @@ func testIntegerObject(t *testing.T, obj types.ObjectJIPL, expected int) { } } - - - // data var ( boolInputData = []struct { @@ -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}, + } ) \ No newline at end of file diff --git a/internal/types/types.go b/internal/types/types.go index e297def..2e36998 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -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 {