Skip to content

Commit

Permalink
enhance eval step && eval error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Houcine EL ADDALI committed Dec 20, 2023
1 parent 4f4ae9c commit 3485757
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 48 deletions.
8 changes: 7 additions & 1 deletion cmd/REPL/repel.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"

"github.com/houcine7/JIPL/internal/debug"
"github.com/houcine7/JIPL/internal/lexer"
"github.com/houcine7/JIPL/internal/parser"
"github.com/houcine7/JIPL/internal/runtime"
Expand Down Expand Up @@ -60,7 +61,12 @@ func Start(in io.Reader, out io.Writer) {
continue
}

evaluated := runtime.Eval(pr)
evaluated ,err:= runtime.Eval(pr)
if err != debug.NOERROR {
io.WriteString(out, fmt.Sprintf("error while evaluating your input: %s \n", err.Error()))
continue
}

if evaluated != nil {
io.WriteString(out, evaluated.ToString())
io.WriteString(out, "\n")
Expand Down
17 changes: 17 additions & 0 deletions internal/debug/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package debug

type Error struct {
Msg string
}

func (err *Error) Error() string {
return err.Msg
}

func NewError(msg string) *Error {
return &Error{Msg: msg}
}

var (
NOERROR = &Error{Msg: ""}
)
195 changes: 151 additions & 44 deletions internal/runtime/eval.go
Original file line number Diff line number Diff line change
@@ -1,130 +1,237 @@
package runtime

import (
"fmt"

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

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

func evalInfixExpression(operator string, leftOperand, rightOperand types.ObjectJIPL) types.ObjectJIPL {
if leftOperand.GetType() != types.T_INTEGER ||
rightOperand.GetType() !=types.T_INTEGER {
return nil
func Eval2(node ast.Node) (types.ObjectJIPL,*debug.Error) {
switch node := node.(type) {
case *ast.Program:
return evalAllProgramStatements(node.Statements)
case *ast.ExpressionStatement:
return Eval2(node.Expression)
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)
if condition == types.TRUE {
return Eval(ifExp.Body)
}
if ifExp.ElseBody != nil {
return Eval(ifExp.ElseBody)
}
return evalIntInfixExpression(operator,leftOperand,rightOperand)
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) {
if leftOperand.GetType() == types.T_INTEGER &&
rightOperand.GetType() ==types.T_INTEGER {
return evalIntInfixExpression(operator,leftOperand,rightOperand)
}

if leftOperand.GetType() == types.T_BOOLEAN &&
rightOperand.GetType() ==types.T_BOOLEAN {
return evalBoolInfixExpression(operator,leftOperand,rightOperand)
}

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)
boolObjLeft := left.(*types.Boolean)
switch operator {
case "==":
return types.BoolToObJIPL(boolObjLeft.Val == boolObjRight.Val),debug.NOERROR
case "!=":
return types.BoolToObJIPL(boolObjLeft.Val != boolObjRight.Val),debug.NOERROR
default:
return nil, debug.NewError("unknown operator")
}
}

func evalIntInfixExpression(operator string, left, right types.ObjectJIPL) types.ObjectJIPL {
func evalIntInfixExpression(operator string, left, right types.ObjectJIPL) (types.ObjectJIPL , *debug.Error){
intObjRight := right.(*types.Integer)
intObjLeft := left.(*types.Integer)
switch operator {
case "+":
return &types.Integer{Val: intObjLeft.Val + intObjRight.Val}
return &types.Integer{Val: intObjLeft.Val + intObjRight.Val},debug.NOERROR
case "-":
return &types.Integer{Val: intObjLeft.Val - intObjRight.Val}
return &types.Integer{Val: intObjLeft.Val - intObjRight.Val},debug.NOERROR
case "*":
return &types.Integer{Val: intObjLeft.Val * intObjRight.Val}
return &types.Integer{Val: intObjLeft.Val * intObjRight.Val},debug.NOERROR
case "/":
return &types.Integer{Val: intObjLeft.Val / intObjRight.Val}
return &types.Integer{Val: intObjLeft.Val / intObjRight.Val},debug.NOERROR
case "%":
return &types.Integer{Val: intObjLeft.Val % intObjRight.Val}
return &types.Integer{Val: intObjLeft.Val % intObjRight.Val},debug.NOERROR
case "==":
return types.BoolToObJIPL(intObjLeft.Val == intObjRight.Val),debug.NOERROR
case "!=":
return types.BoolToObJIPL( intObjLeft.Val != intObjRight.Val), debug.NOERROR
case "<":
return types.BoolToObJIPL(intObjLeft.Val < intObjRight.Val),debug.NOERROR
case "<=":
return types.BoolToObJIPL(intObjLeft.Val <= intObjRight.Val),debug.NOERROR
case ">":
return types.BoolToObJIPL(intObjLeft.Val > intObjRight.Val),debug.NOERROR
case ">=":
return types.BoolToObJIPL(intObjLeft.Val >= intObjRight.Val),debug.NOERROR
default:
return nil
return nil, debug.NewError("unknown operator")
}
}



func evalForLoopExpression(forLoop *ast.ForLoopExpression)( types.ObjectJIPL, *debug.Error){
// the init statement
Eval(forLoop.InitStm)
// the condition
condition,_ := Eval(forLoop.Condition)
for condition == types.TRUE {
Eval(forLoop.Body)
Eval(forLoop.PostIteration)
condition,_ = Eval(forLoop.Condition)
}
return nil,debug.NOERROR
}

func evalPostfixExpression(operator string, operand types.ObjectJIPL) types.ObjectJIPL{

func evalPostfixExpression(operator string, operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Error){
switch operator {
case "--":
return evalDecrementPostfix(operand)
case "++":
return evalIncrementPostfix(operand)
default:
return nil
return types.UNDEFIEND,debug.NewError("unknown operator")
}
}

func evalIncrementPostfix(operand types.ObjectJIPL) types.ObjectJIPL {
func evalIncrementPostfix(operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Error) {
if operand.GetType() != types.T_INTEGER {
return nil
return nil,debug.NewError("operand is not an integer")
}
intObj := operand.(*types.Integer)
return &types.Integer{Val: intObj.Val+1}
return &types.Integer{Val: intObj.Val+1},debug.NOERROR

}

func evalDecrementPostfix(operand types.ObjectJIPL) types.ObjectJIPL{
func evalDecrementPostfix(operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Error){
if operand.GetType() != types.T_INTEGER{
return nil
return nil,debug.NewError("operand is not an integer")
}
intObj := operand.(*types.Integer)
return &types.Integer{Val: intObj.Val-1}
return &types.Integer{Val: intObj.Val-1},debug.NOERROR
}
func evalAllStatements(stms []ast.Statement) types.ObjectJIPL {
var resrult types.ObjectJIPL
func evalAllProgramStatements(stms []ast.Statement)( types.ObjectJIPL , *debug.Error) {
var result types.ObjectJIPL
var err *debug.Error = debug.NOERROR

for _, stm := range stms {
resrult = Eval(stm)
for _, stm := range stms {
result,err = Eval(stm)
if err != debug.NOERROR {
return nil, err
}

if result != nil && result.GetType() == types.T_RETURN {
return result.(*types.Return).Val,debug.NOERROR
}
}
return result,debug.NOERROR
}

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

return resrult
for _, stm := range stms {
result, err= Eval(stm)
if err != debug.NOERROR {
return nil, err
}
if result != nil && result.GetType() == types.T_RETURN {
return result,debug.NOERROR
}
}
return result,debug.NOERROR
}


func evalPrefixExpression(operator string, operand types.ObjectJIPL) types.ObjectJIPL{



func evalPrefixExpression(operator string, operand types.ObjectJIPL) (types.ObjectJIPL, * debug.Error){
switch operator {
case "!":
return evalComplementPrefix(operand)
case "-":
return evalMinusPrefix(operand)
default:
return nil
return nil,debug.NewError("unknown operator")
}
}

func evalMinusPrefix(operand types.ObjectJIPL) types.ObjectJIPL {
func evalMinusPrefix(operand types.ObjectJIPL) (types.ObjectJIPL, *debug.Error) {
if operand.GetType() != types.T_INTEGER {
return nil
return nil,debug.NewError("operand is not an integer")
}
intObj := operand.(*types.Integer)
return &types.Integer{Val: -intObj.Val}
return &types.Integer{Val: -intObj.Val},debug.NOERROR
}

func evalComplementPrefix(operand types.ObjectJIPL) types.ObjectJIPL {
func evalComplementPrefix(operand types.ObjectJIPL) ( types.ObjectJIPL, *debug.Error) {
if operand.GetType() != types.T_BOOLEAN {
return nil
return nil,debug.NewError("operand is not a boolean")
}
boolObj := operand.(*types.Boolean)
if boolObj.Val {
return types.FALSE
return types.FALSE,debug.NOERROR
}

return types.TRUE
return types.TRUE,debug.NOERROR

}

3 changes: 2 additions & 1 deletion internal/runtime/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ func getEvaluated(input string) types.ObjectJIPL {
l := lexer.InitLexer(input)
p := parser.InitParser(l)
program := p.Parse()
return Eval(program)
ev,_:=Eval(program)
return ev
}

func testIntegerObject(t *testing.T, obj types.ObjectJIPL, expected int) {
Expand Down
27 changes: 26 additions & 1 deletion internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,20 @@ type Boolean struct {

type Undefined struct{}

// implementing OBjectJIPL interface by supporeted types
type Return struct {
Val ObjectJIPL
}



// implementing OBjectJIPL interface by supported types
func (ret *Return) ToString() string {
return ret.Val.ToString()
}
func (ret *Return) GetType() TypeObj {
return T_RETURN
}

func (und *Undefined) ToString() string {
return "undefined"
}
Expand All @@ -44,11 +57,23 @@ func (intObj *Integer) GetType() TypeObj {
return T_INTEGER
}



func BoolToObJIPL(bl bool) ObjectJIPL{
if bl {
return TRUE
}else{
return FALSE
}
}


// cte of types
const (
T_INTEGER = "INTEGER"
T_BOOLEAN = "BOOLEAN"
T_UNDEFINED = "UNDEFINED"
T_RETURN = "RETURN"
)

var (
Expand Down
2 changes: 1 addition & 1 deletion pkg/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package utils

// check if the a given character is letter
// support for _ in the name of identifiers
// accept _ in the name of identifiers
func IsLetter(char rune) bool{
if (char >= 'a' && char<='z') || (char >='A' && char <='Z') || char=='_'{
return true
Expand Down

0 comments on commit 3485757

Please sign in to comment.