Skip to content

Commit

Permalink
feat: for loops evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
Houcine EL ADDALI committed Dec 20, 2023
1 parent ec81a40 commit cd50f08
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 24 deletions.
78 changes: 78 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -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 <variable name> = <value>`
2. example
1. `def a = 10`
2. booleans
1. syntax
1. `def <variable name> = <value>`
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. `<function_name>(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.
8 changes: 4 additions & 4 deletions cmd/REPL/repel.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,7 +23,6 @@ import (

const PROMPT = "🟢>"

var GLOBAL_CONTEXT = types.NewContext()

func Start(in io.Reader, out io.Writer) {
scanner := bufio.NewScanner(in)
Expand Down Expand Up @@ -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 {
Expand Down
110 changes: 92 additions & 18 deletions internal/runtime/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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) {
Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 "--":
Expand All @@ -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){
Expand All @@ -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
Expand Down Expand Up @@ -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 "!":
Expand Down Expand Up @@ -264,6 +339,5 @@ func evalComplementPrefix(operand types.ObjectJIPL) ( types.ObjectJIPL, *debug.E
}

return types.TRUE,debug.NOERROR

}

50 changes: 48 additions & 2 deletions internal/types/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package types

import "fmt"
import (
"bytes"
"fmt"

ast "github.com/houcine7/JIPL/internal/AST"
)

type TypeObj string

Expand All @@ -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
Expand All @@ -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()
}
Expand Down Expand Up @@ -95,6 +140,7 @@ const (
T_BOOLEAN = "BOOLEAN"
T_UNDEFINED = "UNDEFINED"
T_RETURN = "RETURN"
T_FUNCTION = "FUNCTION"
)

var (
Expand Down

0 comments on commit cd50f08

Please sign in to comment.