├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── environment ├── environment.go └── environment_test.go ├── eval.go ├── eval_test.go ├── evaluator ├── datatype │ └── datatype.go ├── evaluator.go └── evaluator_test.go ├── function ├── default.go ├── default_test.go ├── function.go └── util.go ├── go.mod └── parser ├── ast ├── ast.go └── expr │ └── expr.go ├── lexer ├── lexer.go └── lexer_test.go ├── parser.go └── token └── token.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 1.x 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: ^1.13 20 | 21 | - name: Check out code into the Go module directory 22 | uses: actions/checkout@v2 23 | 24 | - name: Get dependencies 25 | run: | 26 | go get -v -t -d ./... 27 | if [ -f Gopkg.toml ]; then 28 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 29 | dep ensure 30 | fi 31 | 32 | - name: Build 33 | run: go build -v ./... 34 | 35 | - name: Test 36 | run: go test -v ./... 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | launch.json 2 | go.sum 3 | main.go -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Santhosh Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌶️ chili 2 | 3 | > Currently in development, Unstable (API may change in future) 4 | 5 | Simple expression evaluation engine. 6 | 7 | Expression is one liner that evalutes into single value 8 | 9 | ### Features 10 | 11 | - Number accuracy (using github.com/shopspring/decimal pkg) 12 | - Extensible 13 | - Simple grammer 14 | 15 | ### Installation 16 | 17 | ```shell 18 | $ go get github.com/5anthosh/chili 19 | ``` 20 | 21 | ### Examples 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | "github.com/5anthosh/chili" 29 | ) 30 | func main() { 31 | expression := "val > 50 ? 'Greater than or 50' : 'Smaller than 50'" 32 | values := map[string]interface{}{ 33 | "val": 60, 34 | } 35 | result, err := chili.Eval(expression, values) 36 | if err != nil { 37 | panic(err) 38 | } 39 | println(fmt.Sprintf("%v result", result)) 40 | } 41 | ``` 42 | 43 | ```go 44 | package main 45 | 46 | import ( 47 | "fmt" 48 | "github.com/5anthosh/chili/environment" 49 | "github.com/5anthosh/chili/evaluator" 50 | "github.com/5anthosh/chili/parser" 51 | "github.com/shopspring/decimal" 52 | ) 53 | 54 | func main() { 55 | source := "PI*R^2 + abs(45.345)" 56 | 57 | env := environment.New() 58 | env.SetDefaultFunctions() 59 | env.SetDefaultVariables() 60 | env.SetIntVariable("R", 2) 61 | 62 | chiliParser := parser.New(source) 63 | expression, err := chiliParser.Parse() 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | chiliEvaluator := evaluator.New(env) 69 | value, err := chiliEvaluator.Run(expression) 70 | if err != nil { 71 | panic(err) 72 | } 73 | 74 | println(fmt.Sprintf("%v result", value)) 75 | } 76 | ``` 77 | 78 | ## License 79 | 80 | [MIT](https://github.com/5anthosh/chili/blob/main/LICENSE) 81 | -------------------------------------------------------------------------------- /environment/environment.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/5anthosh/chili/function" 7 | "github.com/shopspring/decimal" 8 | ) 9 | 10 | //Type of variables 11 | const ( 12 | functionType = 1 + iota 13 | variableType 14 | ) 15 | 16 | //Environment of evaluator 17 | type Environment struct { 18 | symbolTable map[string]uint 19 | variables map[string]interface{} 20 | functions map[string]function.Function 21 | } 22 | 23 | //New Environment 24 | func New() *Environment { 25 | return &Environment{ 26 | symbolTable: make(map[string]uint), 27 | variables: make(map[string]interface{}), 28 | functions: make(map[string]function.Function), 29 | } 30 | } 31 | 32 | //SetFunction to Evaluator 33 | func (e *Environment) SetFunction(function function.Function) error { 34 | if e.CheckSymbolTable(function.Name) { 35 | return fmt.Errorf("%s is already declared", function.Name) 36 | } 37 | e.symbolTableEntry(function.Name, functionType) 38 | e.functions[function.Name] = function 39 | return nil 40 | } 41 | 42 | //SetDefaultFunctions to environment 43 | func (e *Environment) SetDefaultFunctions() error { 44 | funcs := function.DefaultFunctions 45 | for _, chiliFunc := range funcs { 46 | err := e.SetFunction(chiliFunc) 47 | if err != nil { 48 | return err 49 | } 50 | } 51 | return nil 52 | } 53 | 54 | //SetDefaultVariables to environment 55 | func (e *Environment) SetDefaultVariables() error { 56 | err := e.SetNumberVariable("PI", function.PI) 57 | if err != nil { 58 | return err 59 | } 60 | err = e.SetNumberVariable("E", function.E) 61 | return err 62 | } 63 | 64 | //SetNumberVariable in the environment 65 | func (e *Environment) SetNumberVariable(name string, value decimal.Decimal) error { 66 | return e.DeclareVariable(name, value) 67 | } 68 | 69 | //SetFloatVariable in the environment 70 | func (e *Environment) SetFloatVariable(name string, value float64) error { 71 | return e.SetNumberVariable(name, decimal.NewFromFloat(value)) 72 | } 73 | 74 | //SetFloat32Variable in the environment 75 | func (e *Environment) SetFloat32Variable(name string, value float32) error { 76 | return e.SetNumberVariable(name, decimal.NewFromFloat32(value)) 77 | } 78 | 79 | //SetIntVariable in the environment 80 | func (e *Environment) SetIntVariable(name string, value int64) error { 81 | return e.SetNumberVariable(name, decimal.NewFromInt(value)) 82 | } 83 | 84 | //SetInt32Variable in the environment 85 | func (e *Environment) SetInt32Variable(name string, value int32) error { 86 | return e.SetNumberVariable(name, decimal.NewFromInt32(value)) 87 | } 88 | 89 | //DeclareVariable in the environment 90 | func (e *Environment) DeclareVariable(name string, value interface{}) error { 91 | if e.CheckSymbolTable(name) { 92 | return fmt.Errorf("%s is already declared", name) 93 | } 94 | e.symbolTableEntry(name, variableType) 95 | e.variables[name] = value 96 | return nil 97 | } 98 | 99 | //GetVariable from the environment 100 | func (e *Environment) GetVariable(name string) (interface{}, bool) { 101 | value, ok := e.variables[name] 102 | return value, ok 103 | } 104 | 105 | //GetFunction from the environment 106 | func (e *Environment) GetFunction(name string) (function.Function, bool) { 107 | value, ok := e.functions[name] 108 | return value, ok 109 | } 110 | 111 | func (e *Environment) symbolTableEntry(name string, varType uint) { 112 | e.symbolTable[name] = varType 113 | } 114 | 115 | //CheckSymbolTable if variable is registered in symbol table 116 | func (e *Environment) CheckSymbolTable(name string) bool { 117 | _, ok := e.symbolTable[name] 118 | return ok 119 | } 120 | 121 | //IsVariable check if name is variable 122 | func (e *Environment) IsVariable(name string) bool { 123 | varType, _ := e.symbolTable[name] 124 | return varType == variableType 125 | } 126 | 127 | //IsFunction check if name is variable 128 | func (e *Environment) IsFunction(name string) bool { 129 | varType, _ := e.symbolTable[name] 130 | return varType == functionType 131 | } 132 | -------------------------------------------------------------------------------- /environment/environment_test.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/5anthosh/chili/function" 7 | ) 8 | 9 | type fields struct { 10 | symbolTable map[string]uint 11 | variables map[string]interface{} 12 | functions map[string]function.Function 13 | } 14 | 15 | func TestEnvironment_IsFunction(t *testing.T) { 16 | type args struct { 17 | name string 18 | } 19 | test1 := fields{} 20 | test1.symbolTable = map[string]uint{ 21 | "testfunc": functionType, 22 | } 23 | 24 | test2 := fields{} 25 | test2.symbolTable = map[string]uint{ 26 | "testfunc-1": functionType, 27 | } 28 | tests := []struct { 29 | name string 30 | fields fields 31 | args args 32 | want bool 33 | }{ 34 | { 35 | name: "Is function is true", 36 | fields: test1, 37 | args: args{ 38 | name: "testfunc", 39 | }, 40 | want: true, 41 | }, 42 | { 43 | name: "Is function is false", 44 | fields: test2, 45 | args: args{ 46 | name: "testfunc", 47 | }, 48 | want: false, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | e := &Environment{ 54 | symbolTable: tt.fields.symbolTable, 55 | variables: tt.fields.variables, 56 | functions: tt.fields.functions, 57 | } 58 | if got := e.IsFunction(tt.args.name); got != tt.want { 59 | t.Errorf("Environment.IsFunction() = %v, want %v", got, tt.want) 60 | } 61 | }) 62 | } 63 | } 64 | 65 | func TestEnvironment_SetFunction(t *testing.T) { 66 | type args struct { 67 | function function.Function 68 | } 69 | 70 | func1 := function.Function{ 71 | Name: "test-func", 72 | } 73 | 74 | func2 := function.Function{ 75 | Name: "test-func2", 76 | } 77 | 78 | fieldsTest := fields{} 79 | fieldsTest.functions = make(map[string]function.Function) 80 | fieldsTest.symbolTable = make(map[string]uint) 81 | fieldsTest.variables = make(map[string]interface{}) 82 | tests := []struct { 83 | name string 84 | fields fields 85 | args args 86 | wantErr bool 87 | }{ 88 | { 89 | name: "SetFunction success", 90 | fields: fieldsTest, 91 | args: args{ 92 | function: func1, 93 | }, 94 | wantErr: false, 95 | }, 96 | { 97 | name: "SetFunction success (2)", 98 | fields: fieldsTest, 99 | args: args{ 100 | function: func2, 101 | }, 102 | wantErr: false, 103 | }, 104 | { 105 | name: "SetFunction failure", 106 | fields: fieldsTest, 107 | args: args{ 108 | function: func1, 109 | }, 110 | wantErr: true, 111 | }, 112 | } 113 | for _, tt := range tests { 114 | t.Run(tt.name, func(t *testing.T) { 115 | e := &Environment{ 116 | symbolTable: tt.fields.symbolTable, 117 | variables: tt.fields.variables, 118 | functions: tt.fields.functions, 119 | } 120 | if err := e.SetFunction(tt.args.function); (err != nil) != tt.wantErr { 121 | t.Errorf("Environment.SetFunction() error = %v, wantErr %v", err, tt.wantErr) 122 | } 123 | }) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /eval.go: -------------------------------------------------------------------------------- 1 | package chili 2 | 3 | import ( 4 | "github.com/5anthosh/chili/environment" 5 | "github.com/5anthosh/chili/evaluator" 6 | "github.com/5anthosh/chili/evaluator/datatype" 7 | "github.com/5anthosh/chili/parser" 8 | ) 9 | 10 | //Eval the expression 11 | func Eval(expression string, data map[string]interface{}) (interface{}, error) { 12 | env := environment.New() 13 | env.SetDefaultFunctions() 14 | env.SetDefaultVariables() 15 | putDataToEnv(env, data) 16 | _parser := parser.New(expression) 17 | ast, err := _parser.Parse() 18 | if err != nil { 19 | return nil, err 20 | } 21 | _evaluator := evaluator.New(env) 22 | return _evaluator.Run(ast) 23 | } 24 | 25 | func putDataToEnv(env *environment.Environment, data map[string]interface{}) error { 26 | for k, v := range data { 27 | switch v.(type) { 28 | case int64: 29 | env.SetIntVariable(k, v.(int64)) 30 | case int32: 31 | env.SetInt32Variable(k, v.(int32)) 32 | case float64: 33 | env.SetFloatVariable(k, v.(float64)) 34 | case float32: 35 | env.SetFloat32Variable(k, v.(float32)) 36 | case string, bool: 37 | env.DeclareVariable(k, v) 38 | default: 39 | return datatype.ErrUnknownDataype 40 | } 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /eval_test.go: -------------------------------------------------------------------------------- 1 | package chili 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/5anthosh/chili/environment" 8 | "github.com/5anthosh/chili/evaluator" 9 | "github.com/5anthosh/chili/parser" 10 | ) 11 | 12 | func TestEvalNumberFunction(t *testing.T) { 13 | source := "PI*R^2 + abs(45.345)" 14 | env := environment.New() 15 | env.SetDefaultFunctions() 16 | env.SetDefaultVariables() 17 | env.SetIntVariable("R", 2) 18 | chiliParser := parser.New(source) 19 | expression, err := chiliParser.Parse() 20 | if err != nil { 21 | t.Error(err) 22 | } 23 | chiliEvaluator := evaluator.New(env) 24 | value, err := chiliEvaluator.Run(expression) 25 | if err != nil { 26 | t.Error(err) 27 | } 28 | if fmt.Sprintf("%v", value) != "57.91137061435917295385057353311801153678867759750042328389977836" { 29 | t.Errorf("Evaluation = %s but want %s", fmt.Sprintf("%v", value), "57.905") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /evaluator/datatype/datatype.go: -------------------------------------------------------------------------------- 1 | package datatype 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/shopspring/decimal" 7 | ) 8 | 9 | //datatypes 10 | const ( 11 | NumberType = 1 + iota 12 | StringType 13 | BooleanType 14 | GenerictType 15 | NoneType 16 | UnSupportedType 17 | ) 18 | 19 | //ErrUnknownDataype # 20 | var ErrUnknownDataype = errors.New("unknown datatype") 21 | 22 | var typeVsString = []string{ 23 | "NUMBER", "STRING", "BOOLEAN", "GENERIC", "NONE", "UNSUPPORTED", 24 | } 25 | 26 | //Checkdatatype of value is correct 27 | func Checkdatatype(value interface{}, datatype uint) bool { 28 | if datatype == GenerictType { 29 | return true 30 | } 31 | dtype, _ := GetType(value) 32 | return dtype == datatype 33 | } 34 | 35 | //IsSupported check if datatype is supported 36 | func IsSupported(value interface{}) bool { 37 | _, ok := GetType(value) 38 | return ok 39 | } 40 | 41 | //CheckNumber checks whether values are number type 42 | func CheckNumber(values ...interface{}) bool { 43 | for _, value := range values { 44 | if !Checkdatatype(value, NumberType) { 45 | return false 46 | } 47 | } 48 | return true 49 | } 50 | 51 | //CheckString checks whether values are string type 52 | func CheckString(values ...interface{}) bool { 53 | for _, value := range values { 54 | if !Checkdatatype(value, StringType) { 55 | return false 56 | } 57 | } 58 | return true 59 | } 60 | 61 | //CheckBoolean checks whether values are boolean type 62 | func CheckBoolean(values ...interface{}) bool { 63 | for _, value := range values { 64 | if !Checkdatatype(value, BooleanType) { 65 | return false 66 | } 67 | } 68 | return true 69 | } 70 | 71 | //GetType of value 72 | func GetType(value interface{}) (uint, bool) { 73 | switch value.(type) { 74 | case decimal.Decimal: 75 | return NumberType, true 76 | case string: 77 | return StringType, true 78 | case bool: 79 | return BooleanType, true 80 | case nil: 81 | return NoneType, true 82 | } 83 | return UnSupportedType, false 84 | } 85 | 86 | //GetTypeString # 87 | func GetTypeString(value interface{}) string { 88 | dtype, _ := GetType(value) 89 | return typeVsString[int(dtype)-1] 90 | } 91 | -------------------------------------------------------------------------------- /evaluator/evaluator.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/5anthosh/chili/environment" 8 | "github.com/5anthosh/chili/evaluator/datatype" 9 | "github.com/5anthosh/chili/parser/ast/expr" 10 | "github.com/5anthosh/chili/parser/token" 11 | "github.com/shopspring/decimal" 12 | ) 13 | 14 | //errors 15 | var ( 16 | ErrDivisionByZero = errors.New("decimal division by zero") 17 | ) 18 | 19 | //Evaluator # 20 | type Evaluator struct { 21 | Env *environment.Environment 22 | } 23 | 24 | //New Evaluator 25 | func New(env *environment.Environment) *Evaluator { 26 | if env == nil { 27 | env = environment.New() 28 | } 29 | return &Evaluator{ 30 | Env: env, 31 | } 32 | } 33 | 34 | //Run the evaluator 35 | func (eval *Evaluator) Run(expression expr.Expr) (interface{}, error) { 36 | return eval.accept(expression) 37 | } 38 | 39 | //VisitBinaryExpr # 40 | func (eval *Evaluator) VisitBinaryExpr(binaryExpr *expr.Binary) (interface{}, error) { 41 | left, err := eval.accept(binaryExpr.Left) 42 | if err != nil { 43 | return nil, err 44 | } 45 | right, err := eval.accept(binaryExpr.Right) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | switch binaryExpr.Operator.Type.Type() { 51 | case token.PlusType: 52 | if datatype.CheckNumber(left, right) { 53 | return left.(decimal.Decimal).Add(right.(decimal.Decimal)), nil 54 | } 55 | if datatype.CheckString(left) && datatype.CheckNumber(right) { 56 | return left.(string) + (right.(decimal.Decimal)).String(), nil 57 | } 58 | if datatype.CheckNumber(left) && datatype.CheckString(right) { 59 | return (left.(decimal.Decimal)).String() + right.(string), nil 60 | } 61 | return nil, generateUnsupportedOperationErr("+", left, right) 62 | case token.MinusType: 63 | if datatype.CheckNumber(left, right) { 64 | return left.(decimal.Decimal).Sub(right.(decimal.Decimal)), nil 65 | } 66 | return nil, generateUnsupportedOperationErr("-", left, right) 67 | case token.StarType: 68 | if datatype.CheckNumber(left, right) { 69 | return left.(decimal.Decimal).Mul(right.(decimal.Decimal)), nil 70 | } 71 | return nil, generateUnsupportedOperationErr("*", left, right) 72 | case token.CommonSlashType: 73 | if datatype.CheckNumber(left, right) { 74 | if decimal.Zero.Equals(right.(decimal.Decimal)) { 75 | return nil, ErrDivisionByZero 76 | } 77 | return left.(decimal.Decimal).Div(right.(decimal.Decimal)), nil 78 | } 79 | return nil, generateUnsupportedOperationErr("/", left, right) 80 | case token.CapType: 81 | if datatype.CheckNumber(left, right) { 82 | return left.(decimal.Decimal).Pow(right.(decimal.Decimal)), nil 83 | } 84 | return nil, generateUnsupportedOperationErr("^", left, right) 85 | case token.ModType: 86 | if datatype.CheckNumber(left, right) { 87 | if decimal.Zero.Equals(right.(decimal.Decimal)) { 88 | return nil, ErrDivisionByZero 89 | } 90 | return left.(decimal.Decimal).Mod(right.(decimal.Decimal)), nil 91 | } 92 | return nil, generateUnsupportedOperationErr("%", left, right) 93 | case token.EqualType: 94 | return logicalOperation("==", left, right) 95 | case token.NotEqualType: 96 | value, err := logicalOperation("!=", left, right) 97 | if err != nil { 98 | return nil, err 99 | } 100 | return !value, nil 101 | case token.GreaterType: 102 | if datatype.CheckNumber(left, right) { 103 | return left.(decimal.Decimal).GreaterThan(right.(decimal.Decimal)), nil 104 | } 105 | return nil, generateUnsupportedOperationErr(">", left, right) 106 | case token.GreaterEqualType: 107 | if datatype.CheckNumber(left, right) { 108 | return left.(decimal.Decimal).GreaterThanOrEqual(right.(decimal.Decimal)), nil 109 | } 110 | return nil, generateUnsupportedOperationErr(">=", left, right) 111 | case token.LesserType: 112 | if datatype.CheckNumber(left, right) { 113 | return left.(decimal.Decimal).LessThan(right.(decimal.Decimal)), nil 114 | } 115 | return nil, generateUnsupportedOperationErr("<", left, right) 116 | case token.LesserEqualType: 117 | if datatype.CheckNumber(left, right) { 118 | return left.(decimal.Decimal).LessThanOrEqual(right.(decimal.Decimal)), nil 119 | } 120 | return nil, generateUnsupportedOperationErr("<=", left, right) 121 | } 122 | 123 | return nil, fmt.Errorf("Unexpected binary operator %s", binaryExpr.Operator.Type.String()) 124 | } 125 | 126 | //VisitGroupExpr # 127 | func (eval *Evaluator) VisitGroupExpr(groupExpr *expr.Group) (interface{}, error) { 128 | return eval.accept(groupExpr.Expression) 129 | } 130 | 131 | //VisitLiteralExpr # 132 | func (eval *Evaluator) VisitLiteralExpr(literalExpression *expr.Literal) (interface{}, error) { 133 | return literalExpression.Value, nil 134 | } 135 | 136 | //VisitUnaryExpr # 137 | func (eval *Evaluator) VisitUnaryExpr(unaryExpr *expr.Unary) (interface{}, error) { 138 | right, err := eval.accept(unaryExpr.Right) 139 | if err != nil { 140 | return nil, err 141 | } 142 | 143 | if unaryExpr.Operator.Type.Type() == token.NotType { 144 | return !truthFullness(right), nil 145 | } 146 | 147 | if unaryExpr.Operator.Type.Type() == token.MinusType { 148 | return (right.(decimal.Decimal)).Neg(), nil 149 | } 150 | return right, nil 151 | } 152 | 153 | //VisitVariableExpr # 154 | func (eval *Evaluator) VisitVariableExpr(variableExpr *expr.Variable) (interface{}, error) { 155 | ok := eval.Env.CheckSymbolTable(variableExpr.Name) 156 | if !ok { 157 | return nil, fmt.Errorf("Unknown variable %s", variableExpr.Name) 158 | } 159 | 160 | ok = eval.Env.IsVariable(variableExpr.Name) 161 | if !ok { 162 | return nil, fmt.Errorf("%s is not variable", variableExpr.Name) 163 | } 164 | 165 | value, ok := eval.Env.GetVariable(variableExpr.Name) 166 | if ok { 167 | return value, nil 168 | } 169 | 170 | return nil, fmt.Errorf("Unknown variable %s", variableExpr.Name) 171 | } 172 | 173 | //VisitFunctionCall # 174 | func (eval *Evaluator) VisitFunctionCall(functionCall *expr.FunctionCall) (interface{}, error) { 175 | ok := eval.Env.CheckSymbolTable(functionCall.Name) 176 | if !ok { 177 | return nil, fmt.Errorf("Unknown function %s()", functionCall.Name) 178 | } 179 | 180 | ok = eval.Env.IsFunction(functionCall.Name) 181 | if !ok { 182 | return nil, fmt.Errorf("%s is not function", functionCall.Name) 183 | } 184 | _function, _ := eval.Env.GetFunction(functionCall.Name) 185 | 186 | var args []interface{} 187 | for _, arg := range functionCall.Args { 188 | value, err := eval.accept(arg) 189 | if err != nil { 190 | return nil, err 191 | } 192 | args = append(args, value) 193 | } 194 | 195 | err := _function.CheckNumberOfArgs(args) 196 | if err != nil { 197 | return nil, err 198 | } 199 | 200 | ok = _function.CheckTypeOfArgs(args) 201 | if !ok { 202 | return nil, fmt.Errorf("function %s() got wrong data type params", functionCall.Name) 203 | } 204 | 205 | if _function.VerifyArgs != nil { 206 | err = _function.VerifyArgs(args) 207 | if err != nil { 208 | return nil, err 209 | } 210 | } 211 | 212 | return _function.FunctionImpl(args) 213 | } 214 | 215 | //VisitTernary # 216 | func (eval *Evaluator) VisitTernary(ternaryExpr *expr.Ternary) (interface{}, error) { 217 | cond, err := eval.accept(ternaryExpr.Condition) 218 | if err != nil { 219 | return nil, err 220 | } 221 | ok := truthFullness(cond) 222 | if ok { 223 | return eval.accept(ternaryExpr.True) 224 | } 225 | return eval.accept(ternaryExpr.False) 226 | } 227 | 228 | //VisitLogicalExpr # 229 | func (eval *Evaluator) VisitLogicalExpr(logicalExpr *expr.Logical) (interface{}, error) { 230 | left, err := eval.accept(logicalExpr.Left) 231 | if err != nil { 232 | return nil, err 233 | } 234 | ok := truthFullness(left) 235 | switch logicalExpr.Operator.Type.Type() { 236 | case token.AndType: 237 | if !ok { 238 | return false, nil 239 | } 240 | right, err := eval.accept(logicalExpr.Right) 241 | if err != nil { 242 | return nil, err 243 | } 244 | return truthFullness(right), nil 245 | case token.OrType: 246 | if ok { 247 | return true, nil 248 | } 249 | right, err := eval.accept(logicalExpr.Right) 250 | if err != nil { 251 | return nil, err 252 | } 253 | return truthFullness(right), nil 254 | } 255 | return nil, fmt.Errorf("Unexpected logical operator %s", logicalExpr.Operator.Type.String()) 256 | 257 | } 258 | func (eval *Evaluator) accept(expr expr.Expr) (interface{}, error) { 259 | return expr.Accept(eval) 260 | } 261 | 262 | func generateUnsupportedOperationErr(op string, left interface{}, right interface{}) error { 263 | return fmt.Errorf("%s operation between (%s, %s) is not supported", op, datatype.GetTypeString(left), datatype.GetTypeString(right)) 264 | } 265 | 266 | func truthFullness(value interface{}) bool { 267 | if value == nil { 268 | return false 269 | } 270 | if datatype.CheckNumber(value) && value.(decimal.Decimal).Equals(decimal.Zero) { 271 | return false 272 | } 273 | if datatype.CheckString(value) && len(value.(string)) == 0 { 274 | return false 275 | } 276 | if datatype.CheckBoolean(value) { 277 | return value.(bool) 278 | } 279 | return true 280 | } 281 | 282 | func logicalOperation(op string, left interface{}, right interface{}) (bool, error) { 283 | if datatype.CheckNumber(left, right) { 284 | return left.(decimal.Decimal).Equals(right.(decimal.Decimal)), nil 285 | } 286 | if datatype.CheckString(left, right) { 287 | return left.(string) == right.(string), nil 288 | } 289 | if datatype.CheckBoolean(left, right) { 290 | return left.(bool) == right.(bool), nil 291 | } 292 | return false, generateUnsupportedOperationErr(op, left, right) 293 | } 294 | -------------------------------------------------------------------------------- /evaluator/evaluator_test.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | -------------------------------------------------------------------------------- /function/default.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "math" 5 | "strings" 6 | 7 | "github.com/5anthosh/chili/evaluator/datatype" 8 | "github.com/shopspring/decimal" 9 | ) 10 | 11 | var one = decimal.NewFromInt(1) 12 | var three = decimal.NewFromInt(3) 13 | var two = decimal.NewFromInt(2) 14 | 15 | //E Euler constant 16 | var E, _ = decimal.NewFromString("2.71828182845904523536028747135266249775724709369995957496696763") 17 | 18 | //PI constant 19 | var PI, _ = decimal.NewFromString("3.14159265358979323846264338327950288419716939937510582097494459") 20 | 21 | func absImpl(args []interface{}) (interface{}, error) { 22 | arg := args[0] 23 | return arg.(decimal.Decimal).Abs(), nil 24 | } 25 | 26 | func cbrtImpl(args []interface{}) (interface{}, error) { 27 | arg := args[0].(decimal.Decimal) 28 | result, _ := root(arg.BigFloat(), 3).Float64() 29 | return decimal.NewFromFloat(result), nil 30 | } 31 | 32 | func ceilImpl(args []interface{}) (interface{}, error) { 33 | arg := args[0] 34 | return arg.(decimal.Decimal).Ceil(), nil 35 | } 36 | 37 | func floorImpl(args []interface{}) (interface{}, error) { 38 | arg := args[0] 39 | return arg.(decimal.Decimal).Floor(), nil 40 | } 41 | 42 | func expImpl(args []interface{}) (interface{}, error) { 43 | arg := args[0] 44 | return E.Pow(arg.(decimal.Decimal)), nil 45 | } 46 | 47 | // TODO implement natural log using decimal.Decimal 48 | func lnImpl(args []interface{}) (interface{}, error) { 49 | arg := args[0].(decimal.Decimal) 50 | v, _ := arg.Float64() 51 | return decimal.NewFromFloat(math.Log(v)), nil 52 | } 53 | 54 | func log10Impl(args []interface{}) (interface{}, error) { 55 | arg := args[0].(decimal.Decimal) 56 | v, _ := arg.Float64() 57 | return decimal.NewFromFloat(math.Log10(v)), nil 58 | } 59 | 60 | func log2Impl(args []interface{}) (interface{}, error) { 61 | arg := args[0].(decimal.Decimal) 62 | v, _ := arg.Float64() 63 | return decimal.NewFromFloat(math.Log2(v)), nil 64 | } 65 | 66 | func maxImpl(args []interface{}) (interface{}, error) { 67 | max := args[0].(decimal.Decimal) 68 | for i := 1; i < len(args); i++ { 69 | v := args[i].(decimal.Decimal) 70 | if v.GreaterThan(max) { 71 | max = v 72 | } 73 | } 74 | return max, nil 75 | } 76 | 77 | func minImpl(args []interface{}) (interface{}, error) { 78 | min := args[0].(decimal.Decimal) 79 | for i := 1; i < len(args); i++ { 80 | v := args[i].(decimal.Decimal) 81 | if v.LessThan(min) { 82 | min = v 83 | } 84 | } 85 | return min, nil 86 | } 87 | 88 | func powImpl(args []interface{}) (interface{}, error) { 89 | base := args[0].(decimal.Decimal) 90 | power := args[1].(decimal.Decimal) 91 | return base.Pow(power), nil 92 | } 93 | 94 | func roundImpl(args []interface{}) (interface{}, error) { 95 | arg := args[0].(decimal.Decimal) 96 | v, _ := arg.Float64() 97 | return decimal.NewFromFloat(math.Round(v)), nil 98 | } 99 | 100 | func signImpl(args []interface{}) (interface{}, error) { 101 | arg := args[0].(decimal.Decimal) 102 | return decimal.NewFromInt(int64(arg.Sign())), nil 103 | } 104 | 105 | func sqrtImpl(args []interface{}) (interface{}, error) { 106 | arg := args[0].(decimal.Decimal) 107 | result, _ := root(arg.BigFloat(), 2).Float64() 108 | return decimal.NewFromFloat(result), nil 109 | } 110 | 111 | func toNumberImpl(args []interface{}) (interface{}, error) { 112 | arg := args[0].(string) 113 | return decimal.NewFromString(arg) 114 | } 115 | 116 | /**************************String function************************/ 117 | 118 | func concatImpl(args []interface{}) (interface{}, error) { 119 | var builder strings.Builder 120 | for _, arg := range args { 121 | _, err := builder.WriteString(arg.(string)) 122 | if err != nil { 123 | return "", nil 124 | } 125 | } 126 | return builder.String(), nil 127 | } 128 | 129 | func containsImpl(args []interface{}) (interface{}, error) { 130 | str1 := args[0].(string) 131 | str2 := args[1].(string) 132 | return strings.Contains(str1, str2), nil 133 | } 134 | 135 | func joinImpl(args []interface{}) (interface{}, error) { 136 | delimiter := args[0].(string) 137 | var builder strings.Builder 138 | _, err := builder.WriteString(args[1].(string)) 139 | if err != nil { 140 | return nil, err 141 | } 142 | for i := 2; i < len(args); i++ { 143 | _, err = builder.WriteString(delimiter) 144 | if err != nil { 145 | return nil, err 146 | } 147 | _, err = builder.WriteString(args[i].(string)) 148 | if err != nil { 149 | return nil, err 150 | } 151 | } 152 | return builder.String(), nil 153 | } 154 | 155 | func lengthImpl(args []interface{}) (interface{}, error) { 156 | return len(args[0].(string)), nil 157 | } 158 | 159 | func replaceImpl(args []interface{}) (interface{}, error) { 160 | str1 := args[0].(string) 161 | str2 := args[1].(string) 162 | str3 := args[2].(string) 163 | oc := args[3].(decimal.Decimal) 164 | return strings.Replace(str1, str2, str3, int(oc.IntPart())), nil 165 | } 166 | 167 | func replaceAllImpl(args []interface{}) (interface{}, error) { 168 | str1 := args[0].(string) 169 | str2 := args[1].(string) 170 | str3 := args[2].(string) 171 | return strings.ReplaceAll(str1, str2, str3), nil 172 | } 173 | 174 | func sliceImpl(args []interface{}) (interface{}, error) { 175 | str := args[0].(string) 176 | start := args[1].(decimal.Decimal).IntPart() 177 | stop := args[2].(decimal.Decimal).IntPart() 178 | if start < 0 { 179 | start = 0 180 | } 181 | if stop > int64(len(str)) { 182 | stop = int64(len(str)) 183 | } 184 | return str[start:stop], nil 185 | } 186 | 187 | // Functions 188 | var ( 189 | AbsFunction = Function{ 190 | Name: "abs", 191 | Arity: 1, 192 | FunctionImpl: absImpl, 193 | ParamsType: []uint{datatype.NumberType}, 194 | ReturnType: datatype.NumberType, 195 | Documentation: "Returns the absolute value of a number.\n Returns a number.", 196 | } 197 | CbrtFunction = Function{ 198 | Name: "cbrt", 199 | Arity: 1, 200 | FunctionImpl: cbrtImpl, 201 | ParamsType: []uint{datatype.NumberType}, 202 | ReturnType: datatype.NumberType, 203 | Documentation: "Returns the cube root of a number.\n Returns a number.", 204 | } 205 | CeilFunction = Function{ 206 | Name: "ceil", 207 | Arity: 1, 208 | FunctionImpl: ceilImpl, 209 | ParamsType: []uint{datatype.NumberType}, 210 | ReturnType: datatype.NumberType, 211 | Documentation: "Returns the smallest integer greater than or equal to a number.\n Returns a number.", 212 | } 213 | ExpFunction = Function{ 214 | Name: "exp", 215 | Arity: 1, 216 | FunctionImpl: expImpl, 217 | ParamsType: []uint{datatype.NumberType}, 218 | ReturnType: datatype.NumberType, 219 | Documentation: "Returns E^x, where x is the argument, and E is Euler's constant (2.718…), the base of the natural logarithm.\n Returns a number.", 220 | } 221 | FloorFunction = Function{ 222 | Name: "floor", 223 | Arity: 1, 224 | FunctionImpl: floorImpl, 225 | ParamsType: []uint{datatype.NumberType}, 226 | ReturnType: datatype.NumberType, 227 | Documentation: "Returns the largest integer less than or equal to a number.\n Returns a number.", 228 | } 229 | LnFunction = Function{ 230 | Name: "ln", 231 | Arity: 1, 232 | FunctionImpl: lnImpl, 233 | ParamsType: []uint{datatype.NumberType}, 234 | ReturnType: datatype.NumberType, 235 | Documentation: "Returns the natural logarithm of a number.\n Returns a number.", 236 | } 237 | Log10Function = Function{ 238 | Name: "log10", 239 | Arity: 1, 240 | FunctionImpl: log10Impl, 241 | ParamsType: []uint{datatype.NumberType}, 242 | ReturnType: datatype.NumberType, 243 | Documentation: "Returns the base 10 logarithm of a number.\n Returns a number.", 244 | } 245 | Log2Function = Function{ 246 | Name: "log2", 247 | Arity: 1, 248 | FunctionImpl: log2Impl, 249 | ParamsType: []uint{datatype.NumberType}, 250 | ReturnType: datatype.NumberType, 251 | Documentation: "Returns the base 2 logarithm of a number.\n Returns a number.", 252 | } 253 | MaxFunction = Function{ 254 | Name: "max", 255 | Arity: -1, 256 | MinArity: 1, 257 | MaxArity: MaximumNumberOfParamsLimit, 258 | FunctionImpl: maxImpl, 259 | ParamsType: []uint{datatype.NumberType}, 260 | ReturnType: datatype.NumberType, 261 | Documentation: "Returns the largest number from a list, where numbers are separated by commas.\n Returns a number", 262 | } 263 | MinFunction = Function{ 264 | Name: "min", 265 | Arity: -1, 266 | MinArity: 1, 267 | MaxArity: MaximumNumberOfParamsLimit, 268 | FunctionImpl: minImpl, 269 | ParamsType: []uint{datatype.NumberType}, 270 | ReturnType: datatype.NumberType, 271 | Documentation: "Returns the smallest number from a list, where numbers are separated by commas.\n Returns a number.", 272 | } 273 | PowFunction = Function{ 274 | Name: "pow", 275 | Arity: 2, 276 | FunctionImpl: powImpl, 277 | ParamsType: []uint{datatype.NumberType, datatype.NumberType}, 278 | ReturnType: datatype.NumberType, 279 | Documentation: "Returns base to the exponent power.\n Returns a number.", 280 | } 281 | RoundFunction = Function{ 282 | Name: "round", 283 | Arity: 1, 284 | FunctionImpl: roundImpl, 285 | ParamsType: []uint{datatype.NumberType}, 286 | ReturnType: datatype.NumberType, 287 | Documentation: "Rounds a number to the nearest integer.\n Returns a number.", 288 | } 289 | SignFunction = Function{ 290 | Name: "sign", 291 | Arity: 1, 292 | FunctionImpl: signImpl, 293 | ParamsType: []uint{datatype.NumberType}, 294 | ReturnType: datatype.NumberType, 295 | Documentation: "Returns the sign of a number, indicating whether it's positive (1), negative (-1) or zero (0).\n Returns a number.", 296 | } 297 | SqrtFunction = Function{ 298 | Name: "sqrt", 299 | Arity: 1, 300 | FunctionImpl: sqrtImpl, 301 | ParamsType: []uint{datatype.NumberType}, 302 | ReturnType: datatype.NumberType, 303 | Documentation: "Returns the square root of a number.\n Returns a number.", 304 | } 305 | TonumberFunction = Function{ 306 | Name: "toNumber", 307 | Arity: 1, 308 | FunctionImpl: toNumberImpl, 309 | ParamsType: []uint{datatype.StringType}, 310 | ReturnType: datatype.NumberType, 311 | Documentation: "Converts a text string to a number.\n Returns a number.", 312 | } 313 | 314 | ConcantFunction = Function{ 315 | Name: "concat", 316 | Arity: -1, 317 | MinArity: 1, 318 | MaxArity: MaximumNumberOfParamsLimit, 319 | FunctionImpl: concatImpl, 320 | ParamsType: []uint{datatype.StringType}, 321 | ReturnType: datatype.StringType, 322 | Documentation: "Concatenates (combines) text strings.\n Returns a text string.", 323 | } 324 | 325 | ContainsFunction = Function{ 326 | Name: "contains", 327 | Arity: 2, 328 | FunctionImpl: containsImpl, 329 | ParamsType: []uint{datatype.StringType, datatype.StringType}, 330 | ReturnType: datatype.BooleanType, 331 | Documentation: "Tests whether a text string contains another text string.\n Returns a boolean.", 332 | } 333 | 334 | JoinFunction = Function{ 335 | Name: "join", 336 | Arity: -1, 337 | MinArity: 2, 338 | MaxArity: MaximumNumberOfParamsLimit, 339 | FunctionImpl: joinImpl, 340 | ParamsType: []uint{datatype.StringType}, 341 | ReturnType: datatype.StringType, 342 | Documentation: "Combines text strings, with a specified delimiter.\n Returns a text string.", 343 | } 344 | 345 | LengthFunction = Function{ 346 | Name: "length", 347 | Arity: 1, 348 | FunctionImpl: lengthImpl, 349 | ParamsType: []uint{datatype.StringType}, 350 | ReturnType: datatype.NumberType, 351 | Documentation: "Returns the number of characters in a text string.\n Returns a number.", 352 | } 353 | 354 | ReplaceFunction = Function{ 355 | Name: "replace", 356 | Arity: 4, 357 | FunctionImpl: replaceImpl, 358 | ParamsType: []uint{datatype.StringType, datatype.StringType, datatype.StringType, datatype.NumberType}, 359 | ReturnType: datatype.StringType, 360 | Documentation: "Replaces the n match within a text string with a specified new text string.\n Returns a text string.", 361 | } 362 | ReplaceAllFunction = Function{ 363 | Name: "replaceAll", 364 | Arity: 3, 365 | FunctionImpl: replaceAllImpl, 366 | ParamsType: []uint{datatype.StringType, datatype.StringType, datatype.StringType}, 367 | ReturnType: datatype.StringType, 368 | Documentation: "Replaces all matches within a text string with a specified new text string.\n Returns a text string.", 369 | } 370 | 371 | SliceFunction = Function{ 372 | Name: "slice", 373 | Arity: 3, 374 | FunctionImpl: sliceImpl, 375 | ParamsType: []uint{datatype.StringType, datatype.NumberType, datatype.NumberType}, 376 | ReturnType: datatype.StringType, 377 | Documentation: "Extracts a substring from a text string, given a specified starting point and end point.\n Returns a text string", 378 | } 379 | DefaultFunctions = []Function{ 380 | AbsFunction, 381 | CbrtFunction, 382 | CeilFunction, 383 | ExpFunction, 384 | FloorFunction, 385 | LnFunction, 386 | Log2Function, 387 | Log10Function, 388 | MaxFunction, 389 | MinFunction, 390 | PowFunction, 391 | RoundFunction, 392 | SignFunction, 393 | SqrtFunction, 394 | TonumberFunction, 395 | ConcantFunction, 396 | ContainsFunction, 397 | JoinFunction, 398 | LengthFunction, 399 | ReplaceFunction, 400 | ReplaceAllFunction, 401 | SliceFunction, 402 | } 403 | ) 404 | -------------------------------------------------------------------------------- /function/default_test.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/shopspring/decimal" 8 | ) 9 | 10 | type args struct { 11 | args []interface{} 12 | } 13 | 14 | type testArgs struct { 15 | name string 16 | args args 17 | want interface{} 18 | wantErr bool 19 | } 20 | 21 | func Test_absImpl(t *testing.T) { 22 | tests := []testArgs{ 23 | { 24 | name: "Run abs for negative values", 25 | args: args{ 26 | args: []interface{}{decimal.NewFromInt(-234)}, 27 | }, 28 | want: decimal.NewFromInt(234), 29 | wantErr: false, 30 | }, 31 | { 32 | name: "Run abs for positive values", 33 | args: args{ 34 | args: []interface{}{decimal.NewFromFloat32(34.345)}, 35 | }, 36 | want: decimal.NewFromFloat32(34.345), 37 | wantErr: false, 38 | }, 39 | } 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | got, err := absImpl(tt.args.args) 43 | if (err != nil) != tt.wantErr { 44 | t.Errorf("absImpl() error = %v, wantErr %v", err, tt.wantErr) 45 | return 46 | } 47 | if !reflect.DeepEqual(got, tt.want) { 48 | t.Errorf("absImpl() = %v, want %v", got, tt.want) 49 | } 50 | }) 51 | } 52 | } 53 | 54 | func Test_cbrtImpl(t *testing.T) { 55 | tests := []testArgs{ 56 | { 57 | name: "cbrt for positive integer", 58 | args: args{ 59 | args: []interface{}{decimal.NewFromInt(27)}, 60 | }, 61 | want: three, 62 | wantErr: false, 63 | }, 64 | { 65 | name: "cbrt for negative integer", 66 | args: args{ 67 | args: []interface{}{decimal.NewFromInt(-27)}, 68 | }, 69 | want: three.Neg(), 70 | wantErr: false, 71 | }, 72 | { 73 | name: "cbrt for decimal number", 74 | args: args{ 75 | args: []interface{}{ 76 | decimal.NewFromFloat(0.45), 77 | }, 78 | }, 79 | want: decimal.NewFromFloat(0.7663094323935531), 80 | wantErr: false, 81 | }, 82 | } 83 | for _, tt := range tests { 84 | t.Run(tt.name, func(t *testing.T) { 85 | got, err := cbrtImpl(tt.args.args) 86 | if (err != nil) != tt.wantErr { 87 | t.Errorf("cbrtImpl() error = %v, wantErr %v", err, tt.wantErr) 88 | return 89 | } 90 | if !reflect.DeepEqual(got, tt.want) { 91 | t.Errorf("cbrtImpl() = %v, want %v", got, tt.want) 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func Test_ceilImpl(t *testing.T) { 98 | tests := []testArgs{ 99 | { 100 | name: "ceil for positive decimal", 101 | args: args{ 102 | args: []interface{}{decimal.NewFromFloat(2.2)}, 103 | }, 104 | want: three, 105 | wantErr: false, 106 | }, 107 | { 108 | name: "ceil for negative decimal", 109 | args: args{ 110 | args: []interface{}{decimal.NewFromFloat(2.4).Neg()}, 111 | }, 112 | want: two.Neg(), 113 | wantErr: false, 114 | }, 115 | { 116 | name: "ceil for positive integer", 117 | args: args{ 118 | args: []interface{}{three}, 119 | }, 120 | want: three, 121 | wantErr: false, 122 | }, 123 | { 124 | name: "ceil for negative integer", 125 | args: args{ 126 | args: []interface{}{three.Neg()}, 127 | }, 128 | want: three.Neg(), 129 | wantErr: false, 130 | }, 131 | } 132 | for _, tt := range tests { 133 | t.Run(tt.name, func(t *testing.T) { 134 | got, err := ceilImpl(tt.args.args) 135 | if (err != nil) != tt.wantErr { 136 | t.Errorf("ceilImpl() error = %v, wantErr %v", err, tt.wantErr) 137 | return 138 | } 139 | if !reflect.DeepEqual(got, tt.want) { 140 | t.Errorf("ceilImpl() = %v, want %v", got, tt.want) 141 | } 142 | }) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /function/function.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/5anthosh/chili/evaluator/datatype" 7 | ) 8 | 9 | //MaximumNumberOfParamsLimit for the function 10 | const MaximumNumberOfParamsLimit = 255 11 | 12 | //Function struct 13 | type Function struct { 14 | Name string 15 | Arity int 16 | MinArity uint 17 | MaxArity uint 18 | FunctionImpl func(args []interface{}) (interface{}, error) 19 | ParamsType []uint 20 | VerifyArgs func(arguments []interface{}) error 21 | ReturnType uint 22 | Documentation string 23 | ArgsDocumentation string 24 | ExampleDocumention string 25 | } 26 | 27 | //CheckNumberOfArgs in the function 28 | func (f *Function) CheckNumberOfArgs(arguments []interface{}) error { 29 | if f.Arity == -1 { 30 | if len(arguments) < int(f.MinArity) { 31 | return fmt.Errorf("%s() expecting minimum %d arguments", f.Name, f.MinArity) 32 | } 33 | if len(arguments) > int(f.MaxArity) { 34 | return fmt.Errorf("%s() expecting maximum %d arguments", f.Name, f.MaxArity) 35 | } 36 | return nil 37 | } 38 | if f.Arity != len(arguments) { 39 | return fmt.Errorf("%s() expecting %d arguments but got %d", f.Name, f.Arity, len(arguments)) 40 | } 41 | return nil 42 | } 43 | 44 | //CheckTypeOfArgs in the function 45 | func (f *Function) CheckTypeOfArgs(arguments []interface{}) bool { 46 | if f.Arity == -1 { 47 | dtype := f.ParamsType[0] 48 | for _, arg := range arguments { 49 | if !datatype.Checkdatatype(arg, dtype) { 50 | return false 51 | } 52 | } 53 | } 54 | 55 | paramsLen := len(f.ParamsType) 56 | argsLen := len(arguments) 57 | for i := 0; i < paramsLen && i < argsLen; i++ { 58 | if !datatype.Checkdatatype(arguments[i], f.ParamsType[i]) { 59 | return false 60 | } 61 | } 62 | return true 63 | } 64 | -------------------------------------------------------------------------------- /function/util.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import "math/big" 4 | 5 | var limit = pow(new(2), 256) 6 | 7 | func pow(a *big.Float, e uint64) *big.Float { 8 | result := zero().Copy(a) 9 | for i := uint64(0); i < e-1; i++ { 10 | result = mul(result, a) 11 | } 12 | return result 13 | } 14 | 15 | func root(a *big.Float, n uint64) *big.Float { 16 | mulvalue := new(1) 17 | if a.Sign() == -1 { 18 | mulvalue = new(-1) 19 | a = a.Mul(a, mulvalue) 20 | } 21 | 22 | n1 := n - 1 23 | n1f, rn := new(float64(n1)), div(new(1.0), new(float64(n))) 24 | x, x0 := new(1.0), zero() 25 | _ = x0 26 | for { 27 | potx, t2 := div(new(1.0), x), a 28 | for b := n1; b > 0; b >>= 1 { 29 | if b&1 == 1 { 30 | t2 = mul(t2, potx) 31 | } 32 | potx = mul(potx, potx) 33 | } 34 | x0, x = x, mul(rn, add(mul(n1f, x), t2)) 35 | if lesser(mul(abs(sub(x, x0)), limit), x) { 36 | break 37 | } 38 | } 39 | x = x.Mul(x, mulvalue) 40 | return x 41 | } 42 | 43 | func abs(a *big.Float) *big.Float { 44 | return zero().Abs(a) 45 | } 46 | 47 | func new(f float64) *big.Float { 48 | r := big.NewFloat(f) 49 | r.SetPrec(256) 50 | return r 51 | } 52 | 53 | func div(a, b *big.Float) *big.Float { 54 | return zero().Quo(a, b) 55 | } 56 | 57 | func zero() *big.Float { 58 | r := big.NewFloat(0.0) 59 | r.SetPrec(256) 60 | return r 61 | } 62 | 63 | func mul(a, b *big.Float) *big.Float { 64 | return zero().Mul(a, b) 65 | } 66 | 67 | func add(a, b *big.Float) *big.Float { 68 | return zero().Add(a, b) 69 | } 70 | 71 | func sub(a, b *big.Float) *big.Float { 72 | return zero().Sub(a, b) 73 | } 74 | 75 | func lesser(x, y *big.Float) bool { 76 | return x.Cmp(y) == -1 77 | } 78 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/5anthosh/chili 2 | 3 | go 1.15 4 | 5 | require github.com/shopspring/decimal v1.2.0 6 | -------------------------------------------------------------------------------- /parser/ast/ast.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/5anthosh/chili/parser/ast/expr" 8 | ) 9 | 10 | const ( 11 | tab uint = 2 12 | preFix = "+" 13 | ) 14 | 15 | func createPrefix(depth uint, expressionType string) string { 16 | return fmt.Sprintf("%s%s (%d)%s", preFix, strings.Repeat("-", int(depth*tab)), depth/tab, expressionType) 17 | } 18 | 19 | //Printer # 20 | type Printer struct { 21 | depth uint 22 | ast expr.Expr 23 | } 24 | 25 | //New Printer 26 | func New(ast expr.Expr) *Printer { 27 | return &Printer{ 28 | depth: 0, 29 | ast: ast, 30 | } 31 | } 32 | 33 | //Print ast structure 34 | func (ac *Printer) Print(expression expr.Expr) (string, error) { 35 | value, err := ac.accept(expression) 36 | if err != nil { 37 | return "", err 38 | } 39 | return value.(string), nil 40 | } 41 | 42 | //VisitBinaryExpr # 43 | func (ac *Printer) VisitBinaryExpr(binaryExpr *expr.Binary) (interface{}, error) { 44 | ac.depth += tab 45 | left, err := ac.accept(binaryExpr.Left) 46 | if err != nil { 47 | return nil, err 48 | } 49 | right, err := ac.accept(binaryExpr.Right) 50 | if err != nil { 51 | return nil, err 52 | } 53 | ac.depth -= tab 54 | return fmt.Sprintf("%s %s \n|\n%v%v", createPrefix(ac.depth, "BINARY"), binaryExpr.Operator.Type.String(), left, right), nil 55 | } 56 | 57 | //VisitGroupExpr # 58 | func (ac *Printer) VisitGroupExpr(groupExpr *expr.Group) (interface{}, error) { 59 | ac.depth += tab 60 | expression, err := ac.accept(groupExpr.Expression) 61 | if err != nil { 62 | return nil, err 63 | } 64 | ac.depth -= tab 65 | return fmt.Sprintf("%s \n|\n%v", createPrefix(ac.depth, "GROUP"), expression), nil 66 | } 67 | 68 | //VisitLiteralExpr # 69 | func (ac *Printer) VisitLiteralExpr(literalExpression *expr.Literal) (interface{}, error) { 70 | return fmt.Sprintf("%s %v\n|\n", createPrefix(ac.depth, "LITERAL"), literalExpression.Value), nil 71 | } 72 | 73 | //VisitUnaryExpr # 74 | func (ac *Printer) VisitUnaryExpr(unaryExpr *expr.Unary) (interface{}, error) { 75 | ac.depth += tab 76 | expression, err := ac.accept(unaryExpr.Right) 77 | if err != nil { 78 | return nil, err 79 | } 80 | ac.depth -= tab 81 | return fmt.Sprintf("%s %s \n|\n%v", createPrefix(ac.depth, "UNARY"), unaryExpr.Operator.Type.String(), expression), nil 82 | } 83 | 84 | //VisitVariableExpr # 85 | func (ac *Printer) VisitVariableExpr(variableExpression *expr.Variable) (interface{}, error) { 86 | return fmt.Sprintf("%s %s\n|\n", createPrefix(ac.depth, "VARIABLE"), variableExpression.Name), nil 87 | } 88 | 89 | //VisitFunctionCall # 90 | func (ac *Printer) VisitFunctionCall(functionCallExpr *expr.FunctionCall) (interface{}, error) { 91 | var builder strings.Builder 92 | builder.WriteString(fmt.Sprintf("%s %s\n|\n", createPrefix(ac.depth, "FUNCTION"), functionCallExpr.Name)) 93 | ac.depth += tab 94 | for _, arg := range functionCallExpr.Args { 95 | argStr, err := ac.accept(arg) 96 | if err != nil { 97 | return nil, err 98 | } 99 | builder.WriteString(fmt.Sprintf("%s", argStr)) 100 | } 101 | ac.depth -= tab 102 | return builder.String(), nil 103 | 104 | } 105 | 106 | //VisitTernary # 107 | func (ac *Printer) VisitTernary(ternaryExpr *expr.Ternary) (interface{}, error) { 108 | ac.depth += tab 109 | cond, err := ac.accept(ternaryExpr.Condition) 110 | if err != nil { 111 | return nil, err 112 | } 113 | trueExpr, err := ac.accept(ternaryExpr.True) 114 | if err != nil { 115 | return nil, err 116 | } 117 | falseExpr, err := ac.accept(ternaryExpr.False) 118 | if err != nil { 119 | return nil, err 120 | } 121 | ac.depth -= tab 122 | return fmt.Sprintf("%s \n|\n %v%v%v", createPrefix(ac.depth, "TERNARY"), cond, trueExpr, falseExpr), nil 123 | } 124 | 125 | //VisitLogicalExpr # 126 | func (ac *Printer) VisitLogicalExpr(logicalExpr *expr.Logical) (interface{}, error) { 127 | ac.depth += tab 128 | left, err := ac.accept(logicalExpr.Left) 129 | if err != nil { 130 | return nil, err 131 | } 132 | right, err := ac.accept(logicalExpr.Right) 133 | if err != nil { 134 | return nil, err 135 | } 136 | ac.depth -= tab 137 | return fmt.Sprintf("%s %s \n|\n%v%v", createPrefix(ac.depth, "LOGICAL"), logicalExpr.Operator.Type.String(), left, right), nil 138 | } 139 | 140 | func (ac *Printer) accept(expression expr.Expr) (interface{}, error) { 141 | return expression.Accept(ac) 142 | } 143 | 144 | func (ac *Printer) String() (string, error) { 145 | v, err := ac.accept(ac.ast) 146 | if err != nil { 147 | return "", err 148 | } 149 | return v.(string), err 150 | } 151 | -------------------------------------------------------------------------------- /parser/ast/expr/expr.go: -------------------------------------------------------------------------------- 1 | package expr 2 | 3 | import "github.com/5anthosh/chili/parser/token" 4 | 5 | //Expr interface 6 | type Expr interface { 7 | Accept(Visitor) (interface{}, error) 8 | } 9 | 10 | //Visitor interface 11 | type Visitor interface { 12 | VisitBinaryExpr(binaryExpression *Binary) (interface{}, error) 13 | VisitGroupExpr(groupExpression *Group) (interface{}, error) 14 | VisitLiteralExpr(LiteralExpression *Literal) (interface{}, error) 15 | VisitUnaryExpr(unaryExpr *Unary) (interface{}, error) 16 | VisitVariableExpr(variableExpr *Variable) (interface{}, error) 17 | VisitFunctionCall(functionCallExpr *FunctionCall) (interface{}, error) 18 | VisitTernary(ternaryExpr *Ternary) (interface{}, error) 19 | VisitLogicalExpr(logicalExpr *Logical) (interface{}, error) 20 | } 21 | 22 | //Binary # 23 | type Binary struct { 24 | Left Expr 25 | Right Expr 26 | Operator *token.Token 27 | } 28 | 29 | //Accept binary operation 30 | func (b *Binary) Accept(visitor Visitor) (interface{}, error) { 31 | return visitor.VisitBinaryExpr(b) 32 | } 33 | 34 | //Group # 35 | type Group struct { 36 | Expression Expr 37 | } 38 | 39 | //Accept Group exp 40 | func (g *Group) Accept(visitor Visitor) (interface{}, error) { 41 | return visitor.VisitGroupExpr(g) 42 | } 43 | 44 | //Literal # 45 | type Literal struct { 46 | Value interface{} 47 | } 48 | 49 | //Accept Literal expression 50 | func (l *Literal) Accept(visitor Visitor) (interface{}, error) { 51 | return visitor.VisitLiteralExpr(l) 52 | } 53 | 54 | //Unary # 55 | type Unary struct { 56 | Operator *token.Token 57 | Right Expr 58 | } 59 | 60 | //Accept Unary expr 61 | func (u *Unary) Accept(visitor Visitor) (interface{}, error) { 62 | return visitor.VisitUnaryExpr(u) 63 | } 64 | 65 | //Variable # 66 | type Variable struct { 67 | Name string 68 | } 69 | 70 | //Accept variable expression 71 | func (v *Variable) Accept(visitor Visitor) (interface{}, error) { 72 | return visitor.VisitVariableExpr(v) 73 | } 74 | 75 | //FunctionCall # 76 | type FunctionCall struct { 77 | Name string 78 | Args []Expr 79 | } 80 | 81 | //Accept # 82 | func (f *FunctionCall) Accept(visitor Visitor) (interface{}, error) { 83 | return visitor.VisitFunctionCall(f) 84 | } 85 | 86 | //Ternary # 87 | type Ternary struct { 88 | Condition Expr 89 | True Expr 90 | False Expr 91 | } 92 | 93 | //Accept # 94 | func (t *Ternary) Accept(visitor Visitor) (interface{}, error) { 95 | return visitor.VisitTernary(t) 96 | } 97 | 98 | //Logical operation 99 | type Logical struct { 100 | Left Expr 101 | Right Expr 102 | Operator *token.Token 103 | } 104 | 105 | //Accept # 106 | func (t *Logical) Accept(visitor Visitor) (interface{}, error) { 107 | return visitor.VisitLogicalExpr(t) 108 | } 109 | -------------------------------------------------------------------------------- /parser/lexer/lexer.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | 9 | "github.com/5anthosh/chili/parser/token" 10 | "github.com/shopspring/decimal" 11 | ) 12 | 13 | const nullTerminater = '\000' 14 | 15 | //ErrInvalidNumber # 16 | var ErrInvalidNumber = errors.New("Invalid number") 17 | 18 | //ErrEOF # 19 | var ErrEOF = errors.New("EOF") 20 | 21 | //Lexer struct 22 | type Lexer struct { 23 | // expression source 24 | source []byte 25 | // Length of expression source 26 | len uint 27 | // Scanned tokens 28 | tokens []token.Token 29 | // Current column 30 | column uint 31 | start uint 32 | current uint 33 | } 34 | 35 | //New creates new lexer 36 | func New(reader io.ReadCloser) (*Lexer, error) { 37 | source, err := ioutil.ReadAll(reader) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return FromBytes(source), err 42 | } 43 | 44 | //FromBytes # 45 | func FromBytes(source []byte) *Lexer { 46 | lex := new(Lexer) 47 | lex.source = source 48 | lex.len = uint(len(source)) 49 | return lex 50 | } 51 | 52 | //FromString # 53 | func FromString(source string) *Lexer { 54 | return FromBytes([]byte(source)) 55 | } 56 | 57 | //Next token 58 | func (l *Lexer) Next() (*token.Token, error) { 59 | if l.isEnd() { 60 | return l.nextToken(token.EOF{}, nil), nil 61 | } 62 | l.start = l.current 63 | t, err := l.scan() 64 | if err != nil { 65 | return nil, err 66 | } 67 | l.tokens = append(l.tokens, *t) 68 | return t, err 69 | } 70 | 71 | func (l *Lexer) scan() (*token.Token, error) { 72 | b := l.eat() 73 | b = l.space(b) 74 | switch b { 75 | case token.PlusChar: 76 | return l.nextToken(token.Plus{}, nil), nil 77 | case token.MinusChar: 78 | return l.nextToken(token.Minus{}, nil), nil 79 | case token.StarChar: 80 | return l.nextToken(token.Star{}, nil), nil 81 | case token.CommonSlashChar: 82 | return l.nextToken(token.CommonSlash{}, nil), nil 83 | case token.OpenParenChar: 84 | return l.nextToken(token.OpenParen{}, nil), nil 85 | case token.CloseParenChar: 86 | return l.nextToken(token.CloseParen{}, nil), nil 87 | case nullTerminater: 88 | return l.nextToken(token.EOF{}, nil), nil 89 | case token.CapChar: 90 | return l.nextToken(token.Cap{}, nil), nil 91 | case token.ModChar: 92 | return l.nextToken(token.Mod{}, nil), nil 93 | case token.CommaChar: 94 | return l.nextToken(token.Comma{}, nil), nil 95 | case token.QuoteChar: 96 | return l.stringLiteral(token.QuoteChar) 97 | case token.DoubleQuoteChar: 98 | return l.stringLiteral(token.DoubleQuoteChar) 99 | case token.QuestionChar: 100 | return l.nextToken(token.Question{}, nil), nil 101 | case token.ColonChar: 102 | return l.nextToken(token.Colon{}, nil), nil 103 | case token.PipeChar: 104 | if l.peek(0) == token.PipeChar { 105 | l.eat() 106 | return l.nextToken(token.Or{}, nil), nil 107 | } 108 | return nil, fmt.Errorf("Expecting '|' after |") 109 | case token.AndChar: 110 | if l.peek(0) == token.AndChar { 111 | l.eat() 112 | return l.nextToken(token.And{}, nil), nil 113 | } 114 | return nil, fmt.Errorf("Expecting '&' after &") 115 | case token.EqualChar: 116 | if l.peek(0) == token.EqualChar { 117 | l.eat() 118 | return l.nextToken(token.Equal{}, nil), nil 119 | } 120 | return nil, fmt.Errorf("Expecting '=' after =") 121 | case token.PunctuationChar: 122 | if l.peek(0) == token.EqualChar { 123 | l.eat() 124 | return l.nextToken(token.NotEqual{}, nil), nil 125 | } 126 | return l.nextToken(token.Not{}, nil), nil 127 | case token.GreaterChar: 128 | if l.peek(0) == token.EqualChar { 129 | l.eat() 130 | return l.nextToken(token.GreaterEqual{}, nil), nil 131 | } 132 | return l.nextToken(token.Greater{}, nil), nil 133 | case token.LesserChar: 134 | if l.peek(0) == token.EqualChar { 135 | l.eat() 136 | return l.nextToken(token.LesserEqual{}, nil), nil 137 | } 138 | return l.nextToken(token.LesserEqual{}, nil), nil 139 | } 140 | if isDigit(b) { 141 | return l.number() 142 | } 143 | 144 | if isChar(b) { 145 | return l.variable() 146 | } 147 | //ErrUnexpectedToken # 148 | var errUnexpectedToken = fmt.Errorf("Unexpected character %c", b) 149 | return nil, errUnexpectedToken 150 | } 151 | 152 | func (l *Lexer) number() (*token.Token, error) { 153 | l.digits() 154 | if l.match('.') { 155 | if !isDigit(l.peek(0)) { 156 | return nil, ErrInvalidNumber 157 | } 158 | l.eat() 159 | l.digits() 160 | } 161 | 162 | if l.match('e') || l.match('E') { 163 | if !l.match('-') { 164 | l.match('+') 165 | } 166 | if !isDigit(l.peek(0)) { 167 | return nil, ErrInvalidNumber 168 | } 169 | l.eat() 170 | l.digits() 171 | } 172 | value, err := decimal.NewFromString(string(l.source[l.start:l.current])) 173 | return l.nextToken(token.Number{}, value), err 174 | } 175 | 176 | func (l *Lexer) variable() (*token.Token, error) { 177 | l.characters() 178 | value := l.source[int(l.start):int(l.current)] 179 | if string(value) == "true" { 180 | return l.nextToken(token.Boolean{}, true), nil 181 | } 182 | if string(value) == "false" { 183 | return l.nextToken(token.Boolean{}, false), nil 184 | } 185 | return l.nextToken(token.Variable{}, nil), nil 186 | } 187 | 188 | func (l *Lexer) eat() byte { 189 | l.current++ 190 | l.column++ 191 | return l.source[l.current-1] 192 | } 193 | 194 | func (l *Lexer) match(expected byte) bool { 195 | if l.isEnd() { 196 | return false 197 | } 198 | if l.source[l.current] != expected { 199 | return false 200 | } 201 | l.current++ 202 | l.column++ 203 | return true 204 | } 205 | 206 | func (l *Lexer) space(b byte) byte { 207 | for isEmptySpace(b) { 208 | if !l.isEnd() { 209 | l.start = l.current 210 | b = l.eat() 211 | } else { 212 | return nullTerminater 213 | } 214 | } 215 | return b 216 | } 217 | 218 | func (l Lexer) peek(b uint) byte { 219 | if l.current+b >= l.len { 220 | return nullTerminater 221 | } 222 | return l.source[l.current+b] 223 | } 224 | 225 | func (l *Lexer) isEnd() bool { 226 | return l.current >= l.len 227 | } 228 | 229 | func isEmptySpace(b byte) bool { 230 | return b == ' ' || b == '\r' || b == '\t' || b == '\n' 231 | } 232 | func (l *Lexer) digits() { 233 | for !l.isEnd() && isDigit(l.peek(0)) { 234 | l.eat() 235 | } 236 | } 237 | func isDigit(b byte) bool { 238 | return b >= '0' && b <= '9' 239 | } 240 | 241 | func isChar(b byte) bool { 242 | return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z' || b == '_' 243 | } 244 | 245 | func (l *Lexer) characters() { 246 | for !l.isEnd() && (isChar(l.peek(0)) || isDigit(l.peek(0)) || l.peek(0) == '.') { 247 | l.eat() 248 | } 249 | } 250 | 251 | func (l *Lexer) nextToken(tokenType token.Type, literal interface{}) *token.Token { 252 | return &token.Token{ 253 | Type: tokenType, 254 | Literal: literal, 255 | Lexeme: string(l.source[l.start:l.current]), 256 | Column: l.column, 257 | } 258 | } 259 | 260 | func (l *Lexer) stringLiteral(s byte) (*token.Token, error) { 261 | for { 262 | if l.isEnd() { 263 | return nil, fmt.Errorf("Expecting %c but found EOF", s) 264 | } 265 | if l.peek(0) == s { 266 | l.eat() 267 | break 268 | } 269 | l.eat() 270 | } 271 | return l.nextToken(token.LiteralString{}, string(l.source[l.start+1:l.current-1])), nil 272 | } 273 | -------------------------------------------------------------------------------- /parser/lexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/5anthosh/chili/parser/token" 7 | "github.com/shopspring/decimal" 8 | ) 9 | 10 | func TestLexerNormalExpression(t *testing.T) { 11 | expression := "34534 + 345.34 - 222 / 43435 * 745.234 () () '3453453' \"apple is good$\" sum(8E-3,097e+34)" 12 | lex := FromString(expression) 13 | number1, _ := decimal.NewFromString("34534") 14 | number2, _ := decimal.NewFromString("345.34") 15 | number3, _ := decimal.NewFromString("222") 16 | number4, _ := decimal.NewFromString("43435") 17 | number5, _ := decimal.NewFromString("745.234") 18 | number6, _ := decimal.NewFromString("8E-3") 19 | number7, _ := decimal.NewFromString("097e+34") 20 | tokens := []token.Token{ 21 | {Type: token.Number{}, Literal: number1, Lexeme: "34534", Column: 5}, 22 | {Type: token.Plus{}, Literal: nil, Lexeme: "+", Column: 7}, 23 | {Type: token.Number{}, Literal: number2, Lexeme: "345.34", Column: 14}, 24 | {Type: token.Minus{}, Literal: nil, Lexeme: "-", Column: 16}, 25 | {Type: token.Number{}, Literal: number3, Lexeme: "222", Column: 20}, 26 | {Type: token.CommonSlash{}, Literal: nil, Lexeme: "/", Column: 22}, 27 | {Type: token.Number{}, Literal: number4, Lexeme: "43435", Column: 28}, 28 | {Type: token.Star{}, Literal: nil, Lexeme: "*", Column: 30}, 29 | {Type: token.Number{}, Literal: number5, Lexeme: "745.234", Column: 38}, 30 | {Type: token.OpenParen{}, Literal: nil, Lexeme: "(", Column: 40}, 31 | {Type: token.CloseParen{}, Literal: nil, Lexeme: ")", Column: 41}, 32 | {Type: token.OpenParen{}, Literal: nil, Lexeme: "(", Column: 43}, 33 | {Type: token.CloseParen{}, Literal: nil, Lexeme: ")", Column: 44}, 34 | {Type: token.LiteralString{}, Literal: "3453453", Lexeme: "'3453453'", Column: 54}, 35 | {Type: token.LiteralString{}, Literal: "apple is good$", Lexeme: "\"apple is good$\"", Column: 71}, 36 | {Type: token.Variable{}, Literal: nil, Lexeme: "sum", Column: 75}, 37 | {Type: token.OpenParen{}, Literal: nil, Lexeme: "(", Column: 76}, 38 | {Type: token.Number{}, Literal: number6, Lexeme: "8E-3", Column: 80}, 39 | {Type: token.Comma{}, Literal: nil, Lexeme: ",", Column: 81}, 40 | {Type: token.Number{}, Literal: number7, Lexeme: "097e+34", Column: 88}, 41 | {Type: token.CloseParen{}, Literal: nil, Lexeme: ")", Column: 89}, 42 | } 43 | for _, tt := range tokens { 44 | t.Run(tt.Lexeme, func(t *testing.T) { 45 | got, err := lex.Next() 46 | if err != nil { 47 | t.Errorf("Lexer.Next() error = %v, wantErr %v", err, false) 48 | return 49 | } 50 | testLocalToken(t, tt, *got) 51 | }) 52 | } 53 | } 54 | 55 | func testLocalToken(t *testing.T, tt token.Token, got token.Token) { 56 | if tt.Column != got.Column { 57 | t.Errorf("Lexer.Next().Column == %v, want %v", got.Column, tt.Column) 58 | } 59 | 60 | if tt.Type.Type() != got.Type.Type() { 61 | t.Errorf("Lexer.Next().Type == %v, want %v", got.Type.String(), tt.Type.String()) 62 | } 63 | 64 | switch tt.Literal.(type) { 65 | case decimal.Decimal: 66 | if !tt.Literal.(decimal.Decimal).Equal(got.Literal.(decimal.Decimal)) { 67 | t.Errorf("Lexer.Next().Literal == %v, want %v", got.Literal, tt.Literal) 68 | } 69 | } 70 | 71 | if tt.Lexeme != got.Lexeme { 72 | t.Errorf("Lexer.Next().Lexeme == %v, want %v", got.Lexeme, tt.Lexeme) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/5anthosh/chili/parser/ast/expr" 8 | "github.com/5anthosh/chili/parser/lexer" 9 | "github.com/5anthosh/chili/parser/token" 10 | ) 11 | 12 | //Parser struct 13 | type Parser struct { 14 | source string 15 | lex *lexer.Lexer 16 | n uint 17 | tokens []*token.Token 18 | } 19 | 20 | //New Parser 21 | func New(source string) *Parser { 22 | newParser := new(Parser) 23 | newParser.source = source 24 | newParser.lex = lexer.FromString(source) 25 | newParser.tokens = make([]*token.Token, 0) 26 | return newParser 27 | } 28 | 29 | //Parse the expression and returns AST 30 | func (p *Parser) Parse() (expr.Expr, error) { 31 | return p.expression() 32 | } 33 | 34 | func (p *Parser) expression() (expr.Expr, error) { 35 | return p.ternary() 36 | } 37 | 38 | func (p *Parser) ternary() (expr.Expr, error) { 39 | expression, err := p.logical() 40 | if err != nil { 41 | return nil, err 42 | } 43 | ok, err := p.match([]uint{token.QuestionType}) 44 | if err != nil { 45 | return nil, err 46 | } 47 | if !ok { 48 | return expression, nil 49 | } 50 | 51 | trueExpr, err := p.expression() 52 | if err != nil { 53 | return nil, err 54 | } 55 | ok, err = p.match([]uint{token.ColonType}) 56 | if err != nil { 57 | return nil, err 58 | } 59 | if !ok { 60 | return nil, fmt.Errorf("Expecting : in ternary operation") 61 | } 62 | falseExpr, err := p.expression() 63 | if err != nil { 64 | return nil, err 65 | } 66 | return &expr.Ternary{ 67 | Condition: expression, 68 | True: trueExpr, 69 | False: falseExpr, 70 | }, nil 71 | } 72 | 73 | func (p *Parser) logical() (expr.Expr, error) { 74 | expression, err := p.equality() 75 | if err != nil { 76 | return nil, err 77 | } 78 | for { 79 | ok, err := p.match([]uint{token.AndType, token.OrType}) 80 | if err != nil { 81 | return nil, err 82 | } 83 | if !ok { 84 | break 85 | } 86 | 87 | operator := p.previous() 88 | right, err := p.equality() 89 | if err != nil { 90 | return nil, err 91 | } 92 | expression = &expr.Logical{Left: expression, Right: right, Operator: operator} 93 | } 94 | return expression, nil 95 | } 96 | 97 | func (p *Parser) equality() (expr.Expr, error) { 98 | expression, err := p.addition() 99 | if err != nil { 100 | return nil, err 101 | } 102 | for { 103 | ok, err := p.match([]uint{ 104 | token.EqualType, 105 | token.NotEqualType, 106 | token.GreaterType, 107 | token.GreaterEqualType, 108 | token.LesserType, 109 | token.LesserEqualType, 110 | }) 111 | if err != nil { 112 | return nil, err 113 | } 114 | if !ok { 115 | break 116 | } 117 | 118 | operator := p.previous() 119 | right, err := p.addition() 120 | if err != nil { 121 | return nil, err 122 | } 123 | expression = &expr.Binary{Left: expression, Right: right, Operator: operator} 124 | } 125 | return expression, nil 126 | } 127 | 128 | func (p *Parser) addition() (expr.Expr, error) { 129 | expression, err := p.multiply() 130 | if err != nil { 131 | return nil, err 132 | } 133 | for { 134 | ok, err := p.match([]uint{token.PlusType, token.MinusType}) 135 | if err != nil { 136 | return nil, err 137 | } 138 | if !ok { 139 | break 140 | } 141 | 142 | operator := p.previous() 143 | right, err := p.multiply() 144 | if err != nil { 145 | return nil, err 146 | } 147 | expression = &expr.Binary{Left: expression, Right: right, Operator: operator} 148 | } 149 | return expression, nil 150 | } 151 | 152 | func (p *Parser) multiply() (expr.Expr, error) { 153 | expression, err := p.exponent() 154 | if err != nil { 155 | return nil, err 156 | } 157 | for { 158 | ok, err := p.match([]uint{token.StarType, token.CommonSlashType, token.CapType, token.ModType}) 159 | if err != nil { 160 | return nil, err 161 | } 162 | if !ok { 163 | break 164 | } 165 | 166 | operator := p.previous() 167 | right, err := p.exponent() 168 | if err != nil { 169 | return nil, err 170 | } 171 | expression = &expr.Binary{Left: expression, Right: right, Operator: operator} 172 | } 173 | return expression, nil 174 | } 175 | 176 | func (p *Parser) exponent() (expr.Expr, error) { 177 | expression, err := p.unary() 178 | if err != nil { 179 | return nil, err 180 | } 181 | for { 182 | ok, err := p.match([]uint{token.CapType}) 183 | if err != nil { 184 | return nil, err 185 | } 186 | if !ok { 187 | break 188 | } 189 | operator := p.previous() 190 | right, err := p.unary() 191 | if err != nil { 192 | return nil, err 193 | } 194 | expression = &expr.Binary{Left: expression, Right: right, Operator: operator} 195 | } 196 | return expression, nil 197 | } 198 | 199 | func (p *Parser) unary() (expr.Expr, error) { 200 | ok, err := p.match([]uint{token.PlusType, token.MinusType, token.NotType}) 201 | if err != nil { 202 | return nil, err 203 | } 204 | if !ok { 205 | return p.functionCall() 206 | } 207 | 208 | t := p.previous() 209 | unaryExpr, err := p.functionCall() 210 | if err != nil { 211 | return nil, err 212 | } 213 | return &expr.Unary{Operator: t, Right: unaryExpr}, nil 214 | 215 | } 216 | 217 | func (p *Parser) functionCall() (expr.Expr, error) { 218 | expression, err := p.term() 219 | if err != nil { 220 | return nil, err 221 | } 222 | ok, err := p.match([]uint{token.OpenParenType}) 223 | if err != nil { 224 | return nil, err 225 | } 226 | if !ok { 227 | return expression, nil 228 | } 229 | switch expression.(type) { 230 | case *expr.Variable: 231 | name := expression.(*expr.Variable).Name 232 | var args []expr.Expr 233 | ok, err := p.match([]uint{token.CloseParenType}) 234 | if err != nil { 235 | return nil, err 236 | } 237 | if ok { 238 | return &expr.FunctionCall{Name: name, Args: args}, nil 239 | } 240 | for { 241 | arg, err := p.expression() 242 | if err != nil { 243 | return nil, err 244 | } 245 | args = append(args, arg) 246 | ok, err := p.match([]uint{token.CommaType}) 247 | if err != nil { 248 | return nil, err 249 | } 250 | if !ok { 251 | break 252 | } 253 | } 254 | ok, err = p.match([]uint{token.CloseParenType}) 255 | if err != nil { 256 | return nil, err 257 | } 258 | if ok { 259 | return &expr.FunctionCall{Name: name, Args: args}, nil 260 | } 261 | return nil, errors.New("Expecting ')' after arguments") 262 | } 263 | return nil, errors.New("Expecting function before '('") 264 | } 265 | 266 | func (p *Parser) term() (expr.Expr, error) { 267 | ok, err := p.match([]uint{token.NumberType, token.StringType, token.BooleanType}) 268 | if err != nil { 269 | return nil, err 270 | } 271 | if ok { 272 | literal := p.previous() 273 | return &expr.Literal{Value: literal.Literal}, nil 274 | } 275 | 276 | ok, err = p.match([]uint{token.VariableType}) 277 | if err != nil { 278 | return nil, err 279 | } 280 | if ok { 281 | variableExpression := p.previous() 282 | return &expr.Variable{Name: variableExpression.Lexeme}, nil 283 | } 284 | 285 | ok, err = p.match([]uint{token.OpenParenType}) 286 | if err != nil { 287 | return nil, err 288 | } 289 | if ok { 290 | expression, err := p.expression() 291 | if err != nil { 292 | return nil, err 293 | } 294 | peekValue := "EOF" 295 | peekToken, err := p.peek() 296 | if err != nil { 297 | return nil, err 298 | } 299 | if peekToken.Type.Type() != token.EOFType { 300 | peekValue = peekToken.Lexeme 301 | } 302 | err = p.consume(token.CloseParenType, fmt.Sprintf("Expect ')' after expression but found %s", peekValue)) 303 | if err != nil { 304 | return nil, err 305 | } 306 | return &expr.Group{Expression: expression}, nil 307 | } 308 | t, err := p.peek() 309 | if err != nil { 310 | return nil, err 311 | } 312 | peekValue := "EOF" 313 | if t.Type.Type() != token.EOFType { 314 | peekValue = t.Lexeme 315 | } 316 | return nil, fmt.Errorf("Expect Expression but found %s", peekValue) 317 | } 318 | 319 | func (p *Parser) getToken() (*token.Token, error) { 320 | t, err := p.lex.Next() 321 | if err == nil { 322 | if t.Type.Type() != token.EOFType { 323 | p.tokens = append(p.tokens, t) 324 | } 325 | return t, nil 326 | } 327 | return nil, err 328 | } 329 | 330 | func (p *Parser) match(tokenTypes []uint) (bool, error) { 331 | for _, tokenType := range tokenTypes { 332 | ok, err := p.check(tokenType) 333 | if err != nil { 334 | return false, err 335 | } 336 | if ok { 337 | p.increment() 338 | return true, nil 339 | } 340 | } 341 | return false, nil 342 | } 343 | 344 | func (p *Parser) consume(tokenType uint, message string) error { 345 | ok, err := p.check(tokenType) 346 | if err != nil { 347 | return err 348 | } 349 | if ok { 350 | p.increment() 351 | return nil 352 | } 353 | return errors.New(message) 354 | } 355 | 356 | func (p *Parser) check(tokenType uint) (bool, error) { 357 | ok, err := p.isAtEnd() 358 | if err != nil { 359 | return false, err 360 | } 361 | if ok { 362 | return false, nil 363 | } 364 | t, err := p.peek() 365 | if err != nil { 366 | return false, err 367 | } 368 | return t.Type.Type() == tokenType, nil 369 | } 370 | 371 | func (p *Parser) peek() (*token.Token, error) { 372 | return p.nextToken() 373 | } 374 | func (p *Parser) isAtEnd() (bool, error) { 375 | t, err := p.nextToken() 376 | if err != nil { 377 | return false, err 378 | } 379 | return t.Type.Type() == token.EOFType, nil 380 | } 381 | 382 | func (p *Parser) previous() *token.Token { 383 | return p.tokens[p.n-1] 384 | } 385 | 386 | func (p *Parser) nextToken() (*token.Token, error) { 387 | if p.n < uint(len(p.tokens)) { 388 | return p.tokens[p.n], nil 389 | } 390 | return p.getToken() 391 | } 392 | 393 | func (p *Parser) increment() { 394 | p.n = p.n + 1 395 | } 396 | -------------------------------------------------------------------------------- /parser/token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import "fmt" 4 | 5 | // Token character 6 | const ( 7 | PlusChar = '+' 8 | MinusChar = '-' 9 | StarChar = '*' 10 | CommonSlashChar = '/' 11 | OpenParenChar = '(' 12 | CloseParenChar = ')' 13 | CapChar = '^' 14 | ModChar = '%' 15 | CommaChar = ',' 16 | QuoteChar = '\'' 17 | DoubleQuoteChar = '"' 18 | EqualChar = '=' 19 | PunctuationChar = '!' 20 | PipeChar = '|' 21 | AndChar = '&' 22 | QuestionChar = '?' 23 | ColonChar = ':' 24 | GreaterChar = '>' 25 | LesserChar = '<' 26 | ) 27 | 28 | // Type of tokens 29 | const ( 30 | PlusType = 1 + iota 31 | MinusType 32 | StarType 33 | CommonSlashType 34 | NumberType 35 | OpenParenType 36 | CloseParenType 37 | VariableType 38 | CapType 39 | ModType 40 | CommaType 41 | StringType 42 | EqualType 43 | NotEqualType 44 | GreaterType 45 | GreaterEqualType 46 | LesserType 47 | LesserEqualType 48 | OrType 49 | AndType 50 | NotType 51 | BooleanType 52 | QuestionType 53 | ColonType 54 | EOFType 55 | ) 56 | 57 | //Type of the token 58 | type Type interface { 59 | String() string 60 | Type() uint 61 | } 62 | 63 | //Plus symbol "+" 64 | type Plus struct{} 65 | 66 | func (Plus) String() string { 67 | return "Plus" 68 | } 69 | 70 | //Type of symbol 71 | func (Plus) Type() uint { 72 | return PlusType 73 | } 74 | 75 | //Star symbol "*"e 76 | type Star struct{} 77 | 78 | func (Star) String() string { 79 | return "Star" 80 | } 81 | 82 | //Type of symbol 83 | func (Star) Type() uint { 84 | return StarType 85 | } 86 | 87 | //Minus symbol "-" 88 | type Minus struct{} 89 | 90 | func (Minus) String() string { 91 | return "Minus" 92 | } 93 | 94 | //Type of symbol 95 | func (Minus) Type() uint { 96 | return MinusType 97 | } 98 | 99 | //CommonSlash symbol "/" 100 | type CommonSlash struct{} 101 | 102 | func (CommonSlash) String() string { 103 | return "Common Slash" 104 | } 105 | 106 | //Type of symbol 107 | func (CommonSlash) Type() uint { 108 | return CommonSlashType 109 | } 110 | 111 | //Number symbol 112 | type Number struct{} 113 | 114 | func (Number) String() string { 115 | return "Number" 116 | } 117 | 118 | //Type of symbol 119 | func (Number) Type() uint { 120 | return NumberType 121 | } 122 | 123 | //OpenParen symbol "(" 124 | type OpenParen struct{} 125 | 126 | func (OpenParen) String() string { 127 | return "Open Bracket" 128 | } 129 | 130 | //Type of symbol 131 | func (OpenParen) Type() uint { 132 | return OpenParenType 133 | } 134 | 135 | //CloseParen symbol ")" 136 | type CloseParen struct{} 137 | 138 | func (CloseParen) String() string { 139 | return "Close Bracket" 140 | } 141 | 142 | //Type of symbol 143 | func (CloseParen) Type() uint { 144 | return CloseParenType 145 | } 146 | 147 | //Variable symbol 148 | type Variable struct{} 149 | 150 | func (Variable) String() string { 151 | return "Variable" 152 | } 153 | 154 | //Type of symbol 155 | func (Variable) Type() uint { 156 | return VariableType 157 | } 158 | 159 | //Cap symbol 160 | type Cap struct{} 161 | 162 | func (Cap) String() string { 163 | return "Cap" 164 | } 165 | 166 | //Type of symbol 167 | func (Cap) Type() uint { 168 | return CapType 169 | } 170 | 171 | //Mod symbol 172 | type Mod struct{} 173 | 174 | func (Mod) String() string { 175 | return "Mod" 176 | } 177 | 178 | //Type of symbol 179 | func (Mod) Type() uint { 180 | return ModType 181 | } 182 | 183 | //Comma symbol 184 | type Comma struct{} 185 | 186 | func (Comma) String() string { 187 | return "Comma" 188 | } 189 | 190 | //Type of symbol 191 | func (Comma) Type() uint { 192 | return CommaType 193 | } 194 | 195 | //LiteralString value 196 | type LiteralString struct{} 197 | 198 | func (LiteralString) String() string { 199 | return "String" 200 | } 201 | 202 | //Type of symbol 203 | func (LiteralString) Type() uint { 204 | return StringType 205 | } 206 | 207 | //Equal == symbol 208 | type Equal struct{} 209 | 210 | func (Equal) String() string { 211 | return "Equal" 212 | } 213 | 214 | //Type of token 215 | func (Equal) Type() uint { 216 | return EqualType 217 | } 218 | 219 | //NotEqual != symbol 220 | type NotEqual struct{} 221 | 222 | func (NotEqual) String() string { 223 | return "Not Equal" 224 | } 225 | 226 | //Type of token 227 | func (NotEqual) Type() uint { 228 | return NotEqualType 229 | } 230 | 231 | //And && symbol 232 | type And struct{} 233 | 234 | func (And) String() string { 235 | return "And" 236 | } 237 | 238 | //Type of token 239 | func (And) Type() uint { 240 | return AndType 241 | } 242 | 243 | //Or || symbol 244 | type Or struct{} 245 | 246 | func (Or) String() string { 247 | return "Or" 248 | } 249 | 250 | //Type of token 251 | func (Or) Type() uint { 252 | return OrType 253 | } 254 | 255 | //Boolean || symbol 256 | type Boolean struct{} 257 | 258 | func (Boolean) String() string { 259 | return "Boolean" 260 | } 261 | 262 | //Type of token 263 | func (Boolean) Type() uint { 264 | return BooleanType 265 | } 266 | 267 | //Question ? symbol 268 | type Question struct{} 269 | 270 | func (Question) String() string { 271 | return "Question" 272 | } 273 | 274 | //Type of token 275 | func (Question) Type() uint { 276 | return QuestionType 277 | } 278 | 279 | //Colon : symbol 280 | type Colon struct{} 281 | 282 | func (Colon) String() string { 283 | return "Colon" 284 | } 285 | 286 | //Type of token 287 | func (Colon) Type() uint { 288 | return ColonType 289 | } 290 | 291 | //Not ! symbol 292 | type Not struct{} 293 | 294 | func (Not) String() string { 295 | return "Not" 296 | } 297 | 298 | //Type of token 299 | func (Not) Type() uint { 300 | return NotType 301 | } 302 | 303 | //Greater ! symbol 304 | type Greater struct{} 305 | 306 | func (Greater) String() string { 307 | return "Greater" 308 | } 309 | 310 | //Type of token 311 | func (Greater) Type() uint { 312 | return GreaterType 313 | } 314 | 315 | //GreaterEqual ! symbol 316 | type GreaterEqual struct{} 317 | 318 | func (GreaterEqual) String() string { 319 | return "GreaterEqual" 320 | } 321 | 322 | //Type of token 323 | func (GreaterEqual) Type() uint { 324 | return GreaterEqualType 325 | } 326 | 327 | //Lesser ! symbol 328 | type Lesser struct{} 329 | 330 | func (Lesser) String() string { 331 | return "Lesser" 332 | } 333 | 334 | //Type of token 335 | func (Lesser) Type() uint { 336 | return LesserType 337 | } 338 | 339 | //LesserEqual ! symbol 340 | type LesserEqual struct{} 341 | 342 | func (LesserEqual) String() string { 343 | return "LesserEqual" 344 | } 345 | 346 | //Type of token 347 | func (LesserEqual) Type() uint { 348 | return LesserEqualType 349 | } 350 | 351 | //EOF symbol 352 | type EOF struct{} 353 | 354 | func (e EOF) String() string { 355 | return "EOF" 356 | } 357 | 358 | //Type of symbol 359 | func (e EOF) Type() uint { 360 | return EOFType 361 | } 362 | 363 | //Token character stream of expression is token into token 364 | type Token struct { 365 | Type Type 366 | Literal interface{} 367 | Lexeme string 368 | Column uint 369 | } 370 | 371 | func (t Token) String() string { 372 | return fmt.Sprintf("< %s %s %v %d>", t.Type.String(), t.Lexeme, t.Literal, t.Column) 373 | } 374 | --------------------------------------------------------------------------------