diff --git a/README.md b/README.md index 1c48c9d..f53d353 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Expr is a string expression parser in go. Not a fancy eval, just a simple and li - Float64 parses the given expr string into float64 as a result. e.g: - "2 + 2" -> 4 - "2.2 + 2" -> 4.2 + - "10 * -5 + (-5.5)" -> -55.5 - Supported operators: - Arithmetic: [+, -, *, /] @@ -56,12 +57,14 @@ Expr is a string expression parser in go. Not a fancy eval, just a simple and li - Int parses the given expr string into int as a result. e.g: - "2 + 2" -> 4 - "2.2 + 2" -> 4 + - "10 + ((-5 * -10) / -10) - 2" -> 3 - Supported operators: - Arithmetic: [+, -, *, /, %] - Bitwise: [&, |, ^, &^, <<, >>] (signed integer) - Notes: - << and >> operators are not permitted to be used in signed integer for go version less than 1.13.x. - Reference: [https://golang.org/doc/go1.13#language](https://golang.org/doc/go1.13#language) + - Even if bitwise is supported, the priority operation is not granted, any bit operation is advised to be put in parentheses. ```go str := "((2 * 2) * (8 + 2) * 2) + 2.56789" diff --git a/boolean/boolean.go b/boolean/boolean.go index be17d6c..2b6a2cf 100644 --- a/boolean/boolean.go +++ b/boolean/boolean.go @@ -15,6 +15,9 @@ var ErrUnsupportedOperator = errors.New("unsupported operator") // ErrInvalidOperationOnFloat is error invalid operation on float var ErrInvalidOperationOnFloat = errors.New("invalid operation on float") +// ErrIntegerDividedByZero occurs when x/y and y equals to 0, Go does not allow integer to be devided by zero +var ErrIntegerDividedByZero = errors.New("integer divide by zero") + // Visitor is boolean visitor interface type Visitor interface { Visit(node ast.Node) ast.Visitor @@ -89,6 +92,10 @@ func (v *visitor) arithmetic(xVisitor, yVisitor *visitor, op token.Token) { case token.MUL: v.res, v.kind = fmt.Sprintf("%d", x*y), token.INT case token.QUO: + if y == 0 { + v.err = ErrIntegerDividedByZero + return + } v.res, v.kind = fmt.Sprintf("%d", x/y), token.INT case token.REM: v.res, v.kind = fmt.Sprintf("%d", x%y), token.INT diff --git a/expr.go b/expr.go index 337518b..872bb93 100644 --- a/expr.go +++ b/expr.go @@ -11,12 +11,17 @@ import ( // Int parses the given expr string into int as a result. // - e.g: -// -"2 + 2" -> 4 -// -"2.2 + 2" -> 4 +// - "2 + 2" -> 4 +// - "2.2 + 2" -> 4 +// - "10 + ((-5 * -10) / -10) - 2" -> 3 // // - Supported operators: // - Arithmetic: [+, -, *, /, %] // - Bitwise: [&, |, ^, &^, <<, >>] +// - Notes: +// - << and >> operators are not permitted to be used in signed integer for go version less than 1.13.x. +// - Reference: golang.org/doc/go1.13#language +// - Even if bitwise is supported, the priority operation is not granted, any bit operation is advised to be put in parentheses. func Int(str string) (int, error) { expr, err := parser.ParseExpr(str) if err != nil { @@ -32,6 +37,7 @@ func Int(str string) (int, error) { // - e.g: // - "2 + 2" -> 4 // - "2.2 + 2" -> 4.2 +// - "10 * -5 + (-5.5)" -> -55.5 // // - Supported operators: // - Arithmetic: [+, -, *, /] diff --git a/expr_test.go b/expr_test.go index 80c0d91..3b50f94 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1,6 +1,7 @@ package expr_test import ( + "math" "testing" "github.com/muktihari/expr" @@ -34,6 +35,17 @@ func TestInt(t *testing.T) { {In: "((2 + 2) * 4 / 4) * 10 + 4.234567", Eq: 44}, {In: "((2 + 2) * 4 / 4) * 10.5 + 4.234567", Eq: 44}, {In: "((2 + 2) * 4 / 4) * 10.7 + 4.234567 * (50 + 50)", Eq: 440}, + {In: "10 * -5", Eq: -50}, + {In: "10 * (-5-5)", Eq: -100}, + {In: "10 * -5 + (-5)", Eq: -55}, + {In: "10 + (10 * -10)", Eq: -90}, + {In: "10 + ((-5 * -10) * 10)", Eq: 510}, + {In: "10 + ((-5 * -10) / -10) - 2", Eq: 3}, + {In: "10 / 0", Eq: 0, Err: integer.ErrIntegerDividedByZero}, + {In: "1100 | 0100", Eq: 12}, // = 1111 + {In: "1100 ^ 0100", Eq: 8}, // = 1011 + {In: "1100 & 0100", Eq: 4}, // = 0100 + {In: "1100 &^ 0100", Eq: 8}, // = 1011 } for _, tc := range tt { @@ -75,6 +87,14 @@ func TestFloat64(t *testing.T) { {In: "((2 + 2) * 4 / 4) * 10 + 4.234567", Eq: 44.234567}, {In: "((2 + 2) * 4 / 4) * 10.5 + 4.234567", Eq: 46.234567}, {In: "((2 + 2) * 4 / 4) * 10.7 + 4.234567 * (50 + 50)", Eq: 466.2567}, + {In: "10 * -5", Eq: -50}, + {In: "10 * (-5-5)", Eq: -100}, + {In: "10 * -5 + (-5.5)", Eq: -55.5}, + {In: "10 + (10 * -10)", Eq: -90}, + {In: "10 + ((-5 * -10) * 10)", Eq: 510}, + {In: "10 + ((-5 * -10) / -10) - 2", Eq: 3}, + {In: "10 / 0", Eq: math.Inf(+1)}, + {In: "0 / 10", Eq: 0}, } for _, tc := range tt { diff --git a/float/float.go b/float/float.go index e38d20c..d014f40 100644 --- a/float/float.go +++ b/float/float.go @@ -26,6 +26,27 @@ type visitor struct { err error } +func (v *visitor) visitUnary(unaryExpr *ast.UnaryExpr) ast.Visitor { + switch unaryExpr.Op { + case token.ADD, token.SUB: + xVisitor := &visitor{} + ast.Walk(xVisitor, unaryExpr.X) + if xVisitor.err != nil { + v.err = xVisitor.err + return nil + } + switch unaryExpr.Op { + case token.ADD: + v.res = xVisitor.res + case token.SUB: + v.res = xVisitor.res * -1 + } + default: + v.err = ErrUnsupportedOperator + } + return nil +} + func (v *visitor) visitBinary(binaryExpr *ast.BinaryExpr) ast.Visitor { switch binaryExpr.Op { case token.ADD, token.SUB, token.MUL, token.QUO: @@ -70,6 +91,8 @@ func (v *visitor) Visit(node ast.Node) ast.Visitor { switch d := node.(type) { case *ast.ParenExpr: return v.Visit(d.X) + case *ast.UnaryExpr: + return v.visitUnary(d) case *ast.BinaryExpr: return v.visitBinary(d) case *ast.BasicLit: diff --git a/integer/integer.go b/integer/integer.go index 7417df5..19a822b 100644 --- a/integer/integer.go +++ b/integer/integer.go @@ -10,6 +10,9 @@ import ( // ErrUnsupportedOperator is error unsupported operator var ErrUnsupportedOperator = errors.New("unsupported operator") +// ErrIntegerDividedByZero occurs when x/y and y equals to 0, Go does not allow integer to be devided by zero +var ErrIntegerDividedByZero = errors.New("integer divide by zero") + // Visitor is integer visitor interface type Visitor interface { Visit(node ast.Node) ast.Visitor @@ -27,6 +30,27 @@ type visitor struct { err error } +func (v *visitor) visitUnary(unaryExpr *ast.UnaryExpr) ast.Visitor { + switch unaryExpr.Op { + case token.ADD, token.SUB: + xVisitor := &visitor{} + ast.Walk(xVisitor, unaryExpr.X) + if xVisitor.err != nil { + v.err = xVisitor.err + return nil + } + switch unaryExpr.Op { + case token.ADD: + v.res = xVisitor.res + case token.SUB: + v.res = xVisitor.res * -1 + } + default: + v.err = ErrUnsupportedOperator + } + return nil +} + func (v *visitor) arithmetic(binaryExpr *ast.BinaryExpr) { x := &visitor{} ast.Walk(x, binaryExpr.X) @@ -49,6 +73,10 @@ func (v *visitor) arithmetic(binaryExpr *ast.BinaryExpr) { case token.MUL: v.res = x.res * y.res case token.QUO: + if y.res == 0 { + v.err = ErrIntegerDividedByZero + return + } v.res = x.res / y.res case token.REM: v.res = x.res % y.res @@ -106,6 +134,8 @@ func (v *visitor) Visit(node ast.Node) ast.Visitor { switch d := node.(type) { case *ast.ParenExpr: return v.Visit(d.X) + case *ast.UnaryExpr: + return v.visitUnary(d) case *ast.BinaryExpr: return v.visitBinary(d) case *ast.BasicLit: