Skip to content

Commit

Permalink
Merge pull request #11 from muktihari/dev
Browse files Browse the repository at this point in the history
Resolve issues: #8 #9 #10
  • Loading branch information
muktihari authored Oct 2, 2020
2 parents 2e52e37 + 35df377 commit fabd084
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 2 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: [+, -, *, /]

Expand All @@ -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"
Expand Down
7 changes: 7 additions & 0 deletions boolean/boolean.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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: [+, -, *, /]
Expand Down
20 changes: 20 additions & 0 deletions expr_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package expr_test

import (
"math"
"testing"

"github.com/muktihari/expr"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 23 additions & 0 deletions float/float.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
30 changes: 30 additions & 0 deletions integer/integer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit fabd084

Please sign in to comment.