diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..cf7cea3 --- /dev/null +++ b/Readme.md @@ -0,0 +1,78 @@ +# JIPL : Just an Interpreted Programming Language + +## What is JIPL? + +JIPL is an interpreted programming language. It is written in Go and is very easy to use. +It is also very easy to learn. It is a very simple language and is not very powerful. It is not meant to be used for large projects + +## How to use JIPL (With go installed)? + +1. Clone the repo + + ```` + git clone``` + + ```` + +2. cd to the repo + + ```` + cd JIPL``` + + ```` + +3. Run the main.go file + + ```` + go run ./cmd/main.go``` + + ```` + +4. Now you can use JIPL in the terminal + +# JIPL Documentation + +1. Variables + + 1. supported data types + 1. integers + 2. booleans + 3. undefined + 2. defining variables + 1. integers + 1. syntax + 1. `def = ` + 2. example + 1. `def a = 10` + 2. booleans + 1. syntax + 1. `def = ` + 2. example + 1. `def a = true` + +2. Functions + + 1. syntax + 1. `function function_name(arguments) { function_body ;})` + 2. example + 1. `function add(a,b) { return a+b ;})` + 3. calling functions + 1. syntax + 1. `(arguments);` + 2. example + 1. `add(10,20);` + +3. If statements + + 1. syntax + 1. `if (condition) { body ;} else { else_body ;}` + 2. example + 1. `if (a == 10) { return true ;} else { return false ;}` + +4. Loops + 1. for loops + 1. syntax + 1. `for (initialization; condition; increment) { body ;}` + 2. example + 1. `for (def i = 0; i <= 10; i++) { if(i==3){return 3;}}` +5. diff --git a/cmd/REPL/repel.go b/cmd/REPL/repel.go index 402653a..f9c550b 100644 --- a/cmd/REPL/repel.go +++ b/cmd/REPL/repel.go @@ -12,9 +12,9 @@ import ( "github.com/houcine7/JIPL/internal/types" ) -// REPL :Read --> Evaluate --> Print --> loop -// the repl used to interact with users to read from console -// and send to interpreter to evaluate then prints back the result +// REPL +// the repl used to interact with users to read from it console +// send to interpreter to evaluate then prints back the result /* * Function as the start method of the repl @@ -23,7 +23,6 @@ import ( const PROMPT = "🟢>" -var GLOBAL_CONTEXT = types.NewContext() func Start(in io.Reader, out io.Writer) { scanner := bufio.NewScanner(in) @@ -64,6 +63,7 @@ func Start(in io.Reader, out io.Writer) { continue } + var GLOBAL_CONTEXT = types.NewContext() evaluated ,err:= runtime.Eval(pr,GLOBAL_CONTEXT) if err != debug.NOERROR { diff --git a/internal/runtime/eval.go b/internal/runtime/eval.go index b5a1f26..8955623 100644 --- a/internal/runtime/eval.go +++ b/internal/runtime/eval.go @@ -31,8 +31,26 @@ func Eval(node ast.Node, ctx *types.Context) (types.ObjectJIPL, *debug.Error) { return val,err case *ast.Identifier: return evalIdentifier(node,ctx) + case *ast.ForLoopExpression: + return evalForLoopExpression(node,ctx) case *ast.IfExpression: return evalIfExpression(node,ctx) + case *ast.FunctionExp : + ctx.Set(node.Name.Value,&types.Function{Name: node.Name.Value, Params: node.Parameters, + Body: node.FnBody, Ctx: ctx}) + return &types.Function{Name: node.Name.Value, Params: node.Parameters, + Body: node.FnBody, Ctx: ctx},debug.NOERROR + case *ast.FunctionCall: + function,err := Eval(node.Function,ctx) + if err != debug.NOERROR { + return nil,err + } + + args,err := evalExpressions(node.Arguments,ctx) + if err != debug.NOERROR { + return nil,err + } + return applyFunction(function,args) case *ast.BlockStm: return evalABlockStatements(node.Statements,ctx) case *ast.IntegerLiteral: @@ -54,6 +72,38 @@ func Eval(node ast.Node, ctx *types.Context) (types.ObjectJIPL, *debug.Error) { } } +func applyFunction(function types.ObjectJIPL, args []types.ObjectJIPL) (types.ObjectJIPL, *debug.Error) { + fn , ok := function.(*types.Function) + if !ok { + return nil,debug.NewError("not a function") + } + appendedCtx := appedCtx(fn,args) + eval,err := Eval(fn.Body,appendedCtx) + + if err != debug.NOERROR { + return nil,err + } + + if eval,ok := eval.(*types.Return); ok { + return eval.Val,debug.NOERROR + } + + return eval,debug.NOERROR +} + + +func appedCtx(fn *types.Function, args []types.ObjectJIPL) *types.Context { + + ctx := types.NewContextWithOuter(fn.Ctx) + + for i, param := range fn.Params { + ctx.Set(param.Value, args[i]) + } + + return ctx + +} + func Eval2(node ast.Node,ctx *types.Context) (types.ObjectJIPL,*debug.Error) { switch node := node.(type) { case *ast.Program: @@ -65,9 +115,20 @@ func Eval2(node ast.Node,ctx *types.Context) (types.ObjectJIPL,*debug.Error) { default: return nil,debug.NewError("unknown node type") } - } - +} +func evalExpressions(node []ast.Expression, ctx *types.Context) ([]types.ObjectJIPL, *debug.Error) { + var result []types.ObjectJIPL + for _, exp := range node { + evaluated, err := Eval(exp, ctx) + + if err != debug.NOERROR { + return nil, err + } + result = append(result, evaluated) + } + return result, debug.NOERROR +} func evalIdentifier(node *ast.Identifier,ctx *types.Context) (types.ObjectJIPL, *debug.Error) { @@ -79,7 +140,6 @@ func evalIdentifier(node *ast.Identifier,ctx *types.Context) (types.ObjectJIPL, 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 { @@ -91,11 +151,7 @@ func evalIfExpression(ifExp *ast.IfExpression,ctx *types.Context) (types.ObjectJ return nil, debug.NewError("if condition is not a met and no else body") } - -func evalInfixExpression(operator string, leftOperand, rightOperand types.ObjectJIPL) (types.ObjectJIPL , *debug.Error) { - - // fmt.Println(leftOperand.ToString(),operator,rightOperand.ToString()) - +func evalInfixExpression(operator string, leftOperand, rightOperand types.ObjectJIPL) (types.ObjectJIPL , *debug.Error) { if leftOperand.GetType() == types.T_INTEGER && rightOperand.GetType() ==types.T_INTEGER { return evalIntInfixExpression(operator,leftOperand,rightOperand) @@ -108,8 +164,7 @@ func evalInfixExpression(operator string, leftOperand, rightOperand types.Object } return nil,debug.NewError(fmt.Sprintf("type mismatch: %s %s %s", leftOperand.GetType(), operator, rightOperand.GetType())) -} - +} func evalBoolInfixExpression(operator string, left, right types.ObjectJIPL) (types.ObjectJIPL , *debug.Error){ boolObjRight := right.(*types.Boolean) @@ -159,21 +214,42 @@ func evalIntInfixExpression(operator string, left, right types.ObjectJIPL) (typ } } - func evalForLoopExpression(forLoop *ast.ForLoopExpression,ctx *types.Context)( types.ObjectJIPL, *debug.Error){ // the init statement - Eval(forLoop.InitStm,ctx) + + _, err :=Eval(forLoop.InitStm,ctx) + + if err != debug.NOERROR { + fmt.Println("error while evaluating the init statement",err) + return nil,err + } + // the condition condition,_ := Eval(forLoop.Condition,ctx) + for condition == types.TRUE { - Eval(forLoop.Body,ctx) - Eval(forLoop.PostIteration,ctx) - condition,_ = Eval(forLoop.Condition,ctx) + iterationEval ,_ := Eval(forLoop.Body,ctx) + returnEval,ok := iterationEval.(*types.Return) + if ok { + return returnEval.Val ,debug.NOERROR + } + + postEval ,_ := Eval(forLoop.PostIteration,ctx) + // update the value of the loop condition + postFix,ok:= forLoop.PostIteration.(*ast.PostfixExpression) + + if ok { + ctx.Set(postFix.Left.(*ast.Identifier).Value, + postEval) + condition,_ = Eval(forLoop.Condition,ctx) + } } return nil,debug.NOERROR } + + func evalPostfixExpression(operator string, operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Error){ switch operator { case "--": @@ -191,7 +267,6 @@ func evalIncrementPostfix(operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Er } intObj := operand.(*types.Integer) return &types.Integer{Val: intObj.Val+1},debug.NOERROR - } func evalDecrementPostfix(operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Error){ @@ -201,6 +276,7 @@ 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,ctx *types.Context)( types.ObjectJIPL , *debug.Error) { var result types.ObjectJIPL var err *debug.Error = debug.NOERROR @@ -234,7 +310,6 @@ func evalABlockStatements(stms []ast.Statement,ctx *types.Context) (types.Object return result,debug.NOERROR } - func evalPrefixExpression(operator string, operand types.ObjectJIPL) (types.ObjectJIPL, * debug.Error){ switch operator { case "!": @@ -264,6 +339,5 @@ func evalComplementPrefix(operand types.ObjectJIPL) ( types.ObjectJIPL, *debug.E } return types.TRUE,debug.NOERROR - } diff --git a/internal/types/types.go b/internal/types/types.go index 2e36998..750124f 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1,6 +1,11 @@ package types -import "fmt" +import ( + "bytes" + "fmt" + + ast "github.com/houcine7/JIPL/internal/AST" +) type TypeObj string @@ -23,14 +28,32 @@ type Return struct { Val ObjectJIPL } +type Function struct { + Name string + Params []*ast.Identifier + Body *ast.BlockStm + Ctx *Context +} + + type Context struct { Store map[string]ObjectJIPL Outer *Context // for nested scopes } + +func NewContextWithOuter(outer *Context) *Context { + ctx := NewContext() + ctx.Outer = outer + return ctx +} + + func (ctx *Context) Get(key string) (ObjectJIPL, bool) { val, ok := ctx.Store[key] if !ok && ctx.Outer != nil { + // recursively search for the key + // in the outer context return ctx.Outer.Get(key) } return val, ok @@ -43,10 +66,32 @@ func (ctx *Context) Set(key string, val ObjectJIPL) ObjectJIPL { func NewContext() *Context { - return &Context{Store: make(map[string]ObjectJIPL)} + return &Context{ + Store: make(map[string]ObjectJIPL), + Outer: nil, + } } // implementing OBjectJIPL interface by supported types +func (fn *Function) GetType() TypeObj { + return T_FUNCTION +} +func (fn *Function) ToString() string { + var bf bytes.Buffer + bf.WriteString("function ") + bf.WriteString(fn.Name) + bf.WriteString("(") + for idx, param := range fn.Params { + bf.WriteString(param.Value) + if idx != len(fn.Params)-1 { + bf.WriteString(",") + } + } + bf.WriteString(fn.Body.ToString()) + + return bf.String() +} + func (ret *Return) ToString() string { return ret.Val.ToString() } @@ -95,6 +140,7 @@ const ( T_BOOLEAN = "BOOLEAN" T_UNDEFINED = "UNDEFINED" T_RETURN = "RETURN" + T_FUNCTION = "FUNCTION" ) var (