作者 yangfu

fix: data append error

  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "fmt"
  6 + "strconv"
  7 + "strings"
  8 +)
  9 +
  10 +type AST struct {
  11 + Tokens []*Token
  12 +
  13 + source string
  14 + currTok *Token
  15 + currIndex int
  16 + depth int
  17 +
  18 + Err error
  19 +}
  20 +
  21 +func NewAST(toks []*Token, s string) *AST {
  22 + a := &AST{
  23 + Tokens: toks,
  24 + source: s,
  25 + }
  26 + if a.Tokens == nil || len(a.Tokens) == 0 {
  27 + a.Err = errors.New("empty token")
  28 + } else {
  29 + a.currIndex = 0
  30 + a.currTok = a.Tokens[0]
  31 + }
  32 + return a
  33 +}
  34 +
  35 +// ParseExpression 解析表达式
  36 +func (a *AST) ParseExpression() ExprAST {
  37 + a.depth++ // called depth
  38 + lhs := a.parsePrimary()
  39 + r := a.parseBinOpRHS(0, lhs)
  40 + a.depth--
  41 + if a.depth == 0 && a.currIndex != len(a.Tokens) && a.Err == nil {
  42 + a.Err = errors.New(
  43 + fmt.Sprintf("bad expression, reaching the end or missing the operator\n%s",
  44 + ErrPos(a.source, a.currTok.Offset)))
  45 + }
  46 + return r
  47 +}
  48 +
  49 +func (a *AST) getNextToken() *Token {
  50 + a.currIndex++
  51 + if a.currIndex < len(a.Tokens) {
  52 + a.currTok = a.Tokens[a.currIndex]
  53 + return a.currTok
  54 + }
  55 + return nil
  56 +}
  57 +
  58 +func (a *AST) getTokPrecedence() int {
  59 + if p, ok := precedence[a.currTok.Tok]; ok {
  60 + return p
  61 + }
  62 + return -1
  63 +}
  64 +
  65 +func (a *AST) parseNumber() NumberExprAST {
  66 + f64, err := strconv.ParseFloat(a.currTok.Tok, 64)
  67 + if err != nil {
  68 + a.Err = errors.New(
  69 + fmt.Sprintf("%v\nwant '(' or '0-9' but get '%s'\n%s",
  70 + err.Error(),
  71 + a.currTok.Tok,
  72 + ErrPos(a.source, a.currTok.Offset)))
  73 + return NumberExprAST{}
  74 + }
  75 + n := NumberExprAST{
  76 + Val: f64,
  77 + Str: a.currTok.Tok,
  78 + }
  79 + a.getNextToken()
  80 + return n
  81 +}
  82 +
  83 +func (a *AST) parseFunCallerOrConst() ExprAST {
  84 + name := a.currTok.Tok
  85 + a.getNextToken()
  86 + // call func
  87 + if a.currTok.Tok == "(" {
  88 + f := FunCallerExprAST{}
  89 + if _, ok := defFunc[name]; !ok {
  90 + a.Err = errors.New(
  91 + fmt.Sprintf("function `%s` is undefined\n%s",
  92 + name,
  93 + ErrPos(a.source, a.currTok.Offset)))
  94 + return f
  95 + }
  96 + a.getNextToken()
  97 + exprs := make([]ExprAST, 0)
  98 + if a.currTok.Tok == ")" {
  99 + // function call without parameters
  100 + // ignore the process of parameter resolution
  101 + } else {
  102 + exprs = append(exprs, a.ParseExpression())
  103 + for a.currTok.Tok != ")" && a.getNextToken() != nil {
  104 + if a.currTok.Type == COMMA {
  105 + continue
  106 + }
  107 + exprs = append(exprs, a.ParseExpression())
  108 + }
  109 + }
  110 + def := defFunc[name]
  111 + if def.argc >= 0 && len(exprs) != def.argc {
  112 + a.Err = errors.New(
  113 + fmt.Sprintf("wrong way calling function `%s`, parameters want %d but get %d\n%s",
  114 + name,
  115 + def.argc,
  116 + len(exprs),
  117 + ErrPos(a.source, a.currTok.Offset)))
  118 + }
  119 + a.getNextToken()
  120 + f.Name = name
  121 + f.Args = exprs
  122 + return f
  123 + }
  124 + // call const
  125 + if v, ok := defConst[name]; ok {
  126 + return NumberExprAST{
  127 + Val: v,
  128 + Str: strconv.FormatFloat(v, 'f', 0, 64),
  129 + }
  130 + } else {
  131 + if strings.Contains(name, ".") {
  132 + return FieldExprAST{
  133 + Str: name,
  134 + }
  135 + }
  136 + a.Err = errors.New(
  137 + fmt.Sprintf("const `%s` is undefined\n%s",
  138 + name,
  139 + ErrPos(a.source, a.currTok.Offset)))
  140 + return NumberExprAST{}
  141 + }
  142 +}
  143 +
  144 +func (a *AST) parsePrimary() ExprAST {
  145 + switch a.currTok.Type {
  146 + case Identifier:
  147 + return a.parseFunCallerOrConst()
  148 + case Literal:
  149 + return a.parseNumber()
  150 + case StringArgs:
  151 + e := ValueExprAST{
  152 + Str: a.currTok.Tok,
  153 + }
  154 + a.getNextToken()
  155 + return e
  156 + case Operator:
  157 + if a.currTok.Tok == "(" {
  158 + t := a.getNextToken()
  159 + if t == nil {
  160 + a.Err = errors.New(
  161 + fmt.Sprintf("want '(' or '0-9' but get EOF\n%s",
  162 + ErrPos(a.source, a.currTok.Offset)))
  163 + return nil
  164 + }
  165 + e := a.ParseExpression()
  166 + if e == nil {
  167 + return nil
  168 + }
  169 + if a.currTok.Tok != ")" {
  170 + a.Err = errors.New(
  171 + fmt.Sprintf("want ')' but get %s\n%s",
  172 + a.currTok.Tok,
  173 + ErrPos(a.source, a.currTok.Offset)))
  174 + return nil
  175 + }
  176 + a.getNextToken()
  177 + return e
  178 + } else if a.currTok.Tok == "-" {
  179 + if a.getNextToken() == nil {
  180 + a.Err = errors.New(
  181 + fmt.Sprintf("want '0-9' but get '-'\n%s",
  182 + ErrPos(a.source, a.currTok.Offset)))
  183 + return nil
  184 + }
  185 + bin := BinaryExprAST{
  186 + Op: "-",
  187 + Lhs: NumberExprAST{},
  188 + Rhs: a.parsePrimary(),
  189 + }
  190 + return bin
  191 + } else {
  192 + return a.parseNumber()
  193 + }
  194 + case COMMA:
  195 + a.Err = errors.New(
  196 + fmt.Sprintf("want '(' or '0-9' but get %s\n%s",
  197 + a.currTok.Tok,
  198 + ErrPos(a.source, a.currTok.Offset)))
  199 + return nil
  200 + default:
  201 + return nil
  202 + }
  203 +}
  204 +
  205 +func (a *AST) parseBinOpRHS(execPrec int, lhs ExprAST) ExprAST {
  206 + for {
  207 + tokPrec := a.getTokPrecedence()
  208 + if tokPrec < execPrec {
  209 + return lhs
  210 + }
  211 + binOp := a.currTok.Tok
  212 + if a.getNextToken() == nil {
  213 + a.Err = errors.New(
  214 + fmt.Sprintf("want '(' or '0-9' but get EOF\n%s",
  215 + ErrPos(a.source, a.currTok.Offset)))
  216 + return nil
  217 + }
  218 + rhs := a.parsePrimary()
  219 + if rhs == nil {
  220 + return nil
  221 + }
  222 + nextPrec := a.getTokPrecedence()
  223 + if tokPrec < nextPrec {
  224 + rhs = a.parseBinOpRHS(tokPrec+1, rhs)
  225 + if rhs == nil {
  226 + return nil
  227 + }
  228 + }
  229 + lhs = BinaryExprAST{
  230 + Op: binOp,
  231 + Lhs: lhs,
  232 + Rhs: rhs,
  233 + }
  234 + }
  235 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "math"
  6 +)
  7 +
  8 +const (
  9 + RadianMode = iota
  10 + AngleMode
  11 +)
  12 +
  13 +type defS struct {
  14 + argc int
  15 + fun func(expr ...ExprAST) float64
  16 +}
  17 +
  18 +// TrigonometricMode enum "RadianMode", "AngleMode"
  19 +var TrigonometricMode = RadianMode
  20 +
  21 +var defConst = map[string]float64{
  22 + "pi": math.Pi,
  23 +}
  24 +
  25 +var defFunc map[string]defS
  26 +
  27 +func init() {
  28 + defFunc = map[string]defS{
  29 + "sin": {1, defSin},
  30 + "cos": {1, defCos},
  31 + "tan": {1, defTan},
  32 + "cot": {1, defCot},
  33 + "sec": {1, defSec},
  34 + "csc": {1, defCsc},
  35 +
  36 + "abs": {1, defAbs},
  37 + "ceil": {1, defCeil},
  38 + "floor": {1, defFloor},
  39 + "round": {1, defRound},
  40 + "sqrt": {1, defSqrt},
  41 + "cbrt": {1, defCbrt},
  42 +
  43 + "noerr": {1, defNoErr},
  44 +
  45 + "max": {-1, defMax},
  46 + "min": {-1, defMin},
  47 +
  48 + // excel support
  49 + "sum": {-1, defNone},
  50 + "if": {-1, defNone},
  51 + "sumif": {-1, defNone},
  52 + "and": {-1, defNone},
  53 + "or": {-1, defNone},
  54 + "month": {-1, defNone},
  55 + "year": {-1, defNone},
  56 + //"round": {-1, defNone},
  57 + "rounddown": {-1, defNone},
  58 + "roundup": {-1, defNone},
  59 + "count": {-1, defNone},
  60 + "countif": {-1, defNone},
  61 + //"&": {-1, defNone},
  62 + "concat": {-1, defNone},
  63 + }
  64 +}
  65 +
  66 +// sin(pi/2) = 1
  67 +func defSin(expr ...ExprAST) float64 {
  68 + return math.Sin(expr2Radian(expr[0]))
  69 +}
  70 +
  71 +// cos(0) = 1
  72 +func defCos(expr ...ExprAST) float64 {
  73 + return math.Cos(expr2Radian(expr[0]))
  74 +}
  75 +
  76 +// tan(pi/4) = 1
  77 +func defTan(expr ...ExprAST) float64 {
  78 + return math.Tan(expr2Radian(expr[0]))
  79 +}
  80 +
  81 +// cot(pi/4) = 1
  82 +func defCot(expr ...ExprAST) float64 {
  83 + return 1 / defTan(expr...)
  84 +}
  85 +
  86 +// sec(0) = 1
  87 +func defSec(expr ...ExprAST) float64 {
  88 + return 1 / defCos(expr...)
  89 +}
  90 +
  91 +// csc(pi/2) = 1
  92 +func defCsc(expr ...ExprAST) float64 {
  93 + return 1 / defSin(expr...)
  94 +}
  95 +
  96 +// abs(-2) = 2
  97 +func defAbs(expr ...ExprAST) float64 {
  98 + return math.Abs(ExprASTResult(expr[0]))
  99 +}
  100 +
  101 +// ceil(4.2) = ceil(4.8) = 5
  102 +func defCeil(expr ...ExprAST) float64 {
  103 + return math.Ceil(ExprASTResult(expr[0]))
  104 +}
  105 +
  106 +// floor(4.2) = floor(4.8) = 4
  107 +func defFloor(expr ...ExprAST) float64 {
  108 + return math.Floor(ExprASTResult(expr[0]))
  109 +}
  110 +
  111 +// round(4.2) = 4
  112 +// round(4.6) = 5
  113 +func defRound(expr ...ExprAST) float64 {
  114 + return math.Round(ExprASTResult(expr[0]))
  115 +}
  116 +
  117 +// sqrt(4) = 2
  118 +// sqrt(4) = abs(sqrt(4))
  119 +// returns only the absolute value of the result
  120 +func defSqrt(expr ...ExprAST) float64 {
  121 + return math.Sqrt(ExprASTResult(expr[0]))
  122 +}
  123 +
  124 +// cbrt(27) = 3
  125 +func defCbrt(expr ...ExprAST) float64 {
  126 + return math.Cbrt(ExprASTResult(expr[0]))
  127 +}
  128 +
  129 +// max(2) = 2
  130 +// max(2, 3) = 3
  131 +// max(2, 3, 1) = 3
  132 +func defMax(expr ...ExprAST) float64 {
  133 + if len(expr) == 0 {
  134 + panic(errors.New("calling function `max` must have at least one parameter."))
  135 + }
  136 + if len(expr) == 1 {
  137 + return ExprASTResult(expr[0])
  138 + }
  139 + maxV := ExprASTResult(expr[0])
  140 + for i := 1; i < len(expr); i++ {
  141 + v := ExprASTResult(expr[i])
  142 + maxV = math.Max(maxV, v)
  143 + }
  144 + return maxV
  145 +}
  146 +
  147 +// min(2) = 2
  148 +// min(2, 3) = 2
  149 +// min(2, 3, 1) = 1
  150 +func defMin(expr ...ExprAST) float64 {
  151 + if len(expr) == 0 {
  152 + panic(errors.New("calling function `min` must have at least one parameter."))
  153 + }
  154 + if len(expr) == 1 {
  155 + return ExprASTResult(expr[0])
  156 + }
  157 + maxV := ExprASTResult(expr[0])
  158 + for i := 1; i < len(expr); i++ {
  159 + v := ExprASTResult(expr[i])
  160 + maxV = math.Min(maxV, v)
  161 + }
  162 + return maxV
  163 +}
  164 +
  165 +// noerr(1/0) = 0
  166 +// noerr(2.5/(1-1)) = 0
  167 +func defNoErr(expr ...ExprAST) (r float64) {
  168 + defer func() {
  169 + if e := recover(); e != nil {
  170 + r = 0
  171 + }
  172 + }()
  173 + return ExprASTResult(expr[0])
  174 +}
  175 +
  176 +func defNone(expr ...ExprAST) (r float64) {
  177 + return 0
  178 +}
1 -package domain 1 +package astexpr
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "fmt" 5 "fmt"
  6 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
6 ) 7 )
7 8
8 const ( 9 const (
@@ -16,6 +17,11 @@ type ExprAST interface { @@ -16,6 +17,11 @@ type ExprAST interface {
16 toStr() string 17 toStr() string
17 } 18 }
18 19
  20 +type NumberExprAST struct {
  21 + Val float64
  22 + Str string
  23 +}
  24 +
19 type ValueExprAST struct { 25 type ValueExprAST struct {
20 ExprType string `json:"exprType"` 26 ExprType string `json:"exprType"`
21 Val string `json:"val"` 27 Val string `json:"val"`
@@ -25,7 +31,7 @@ type ValueExprAST struct { @@ -25,7 +31,7 @@ type ValueExprAST struct {
25 type FieldExprAST struct { 31 type FieldExprAST struct {
26 ExprType string `json:"exprType"` 32 ExprType string `json:"exprType"`
27 Str string `json:"str"` 33 Str string `json:"str"`
28 - Field *TableField `json:"field"` 34 + Field *domain.TableField `json:"field"`
29 } 35 }
30 36
31 type BinaryExprAST struct { 37 type BinaryExprAST struct {
@@ -42,6 +48,13 @@ type FunCallerExprAST struct { @@ -42,6 +48,13 @@ type FunCallerExprAST struct {
42 Args []ExprAST `json:"args"` 48 Args []ExprAST `json:"args"`
43 } 49 }
44 50
  51 +func (n NumberExprAST) toStr() string {
  52 + return fmt.Sprintf(
  53 + "NumberExprAST:%s",
  54 + n.Str,
  55 + )
  56 +}
  57 +
45 func (n ValueExprAST) toStr() string { 58 func (n ValueExprAST) toStr() string {
46 return fmt.Sprintf( 59 return fmt.Sprintf(
47 "ValueExprAST:%s", 60 "ValueExprAST:%s",
@@ -84,7 +97,7 @@ type CloneBinaryExprAST struct { @@ -84,7 +97,7 @@ type CloneBinaryExprAST struct {
84 Rhs json.RawMessage `json:"rhs"` 97 Rhs json.RawMessage `json:"rhs"`
85 } 98 }
86 99
87 -func AstExprUnmarshalJSON(data []byte) (interface{}, error) { 100 +func ExprUnmarshal(data []byte) (interface{}, error) {
88 var m = make(map[string]interface{}) 101 var m = make(map[string]interface{})
89 if err := json.Unmarshal(data, &m); err != nil { 102 if err := json.Unmarshal(data, &m); err != nil {
90 return nil, err 103 return nil, err
@@ -108,7 +121,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{ @@ -108,7 +121,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{
108 } 121 }
109 exprReturn := &FunCallerExprAST{Name: expr.Name, ExprType: TypeFunCallerExprAST} 122 exprReturn := &FunCallerExprAST{Name: expr.Name, ExprType: TypeFunCallerExprAST}
110 for i := range expr.Arg { 123 for i := range expr.Arg {
111 - subExpr, err := AstExprUnmarshalJSON(expr.Arg[i]) 124 + subExpr, err := ExprUnmarshal(expr.Arg[i])
112 if err != nil { 125 if err != nil {
113 return nil, err 126 return nil, err
114 } 127 }
@@ -126,7 +139,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{ @@ -126,7 +139,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{
126 } 139 }
127 exprReturn := &BinaryExprAST{Op: expr.Op, ExprType: TypeBinaryExprAST} 140 exprReturn := &BinaryExprAST{Op: expr.Op, ExprType: TypeBinaryExprAST}
128 if len(expr.Lhs) > 0 { 141 if len(expr.Lhs) > 0 {
129 - subExpr, err := AstExprUnmarshalJSON(expr.Lhs) 142 + subExpr, err := ExprUnmarshal(expr.Lhs)
130 if err != nil { 143 if err != nil {
131 return nil, err 144 return nil, err
132 } 145 }
@@ -135,7 +148,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{ @@ -135,7 +148,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{
135 } 148 }
136 } 149 }
137 if len(expr.Rhs) > 0 { 150 if len(expr.Rhs) > 0 {
138 - subExpr, err := AstExprUnmarshalJSON(expr.Rhs) 151 + subExpr, err := ExprUnmarshal(expr.Rhs)
139 if err != nil { 152 if err != nil {
140 return nil, err 153 return nil, err
141 } 154 }
1 -package domain 1 +package astexpr
2 2
3 import ( 3 import (
4 "github.com/linmadan/egglib-go/utils/json" 4 "github.com/linmadan/egglib-go/utils/json"
5 "github.com/stretchr/testify/assert" 5 "github.com/stretchr/testify/assert"
  6 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
6 "testing" 7 "testing"
7 ) 8 )
8 9
@@ -57,7 +58,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) { @@ -57,7 +58,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) {
57 &FieldExprAST{ 58 &FieldExprAST{
58 ExprType: TypeFieldExprAST, 59 ExprType: TypeFieldExprAST,
59 Str: "业绩1", 60 Str: "业绩1",
60 - Field: &TableField{ 61 + Field: &domain.TableField{
61 TableId: 1, 62 TableId: 1,
62 TableName: "测试ABC", 63 TableName: "测试ABC",
63 TableSqlName: "table_abc_test", 64 TableSqlName: "table_abc_test",
@@ -83,7 +84,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) { @@ -83,7 +84,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) {
83 if err != nil { 84 if err != nil {
84 t.Fatal(err) 85 t.Fatal(err)
85 } 86 }
86 - v, err := AstExprUnmarshalJSON(data) 87 + v, err := ExprUnmarshal(data)
87 if err != nil { 88 if err != nil {
88 t.Fatal(err) 89 t.Fatal(err)
89 } 90 }
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "fmt"
  6 + "strings"
  7 +)
  8 +
  9 +var precedence = map[string]int{
  10 + "+": 20, "-": 20, "*": 40, "/": 40, "%": 40, "^": 60,
  11 + "=": 10, ">": 10, "<": 10, "<=": 10, ">=": 10, "&": 40,
  12 +}
  13 +
  14 +const (
  15 + // Identifier 标识符 e.g.函数名、表字段
  16 + Identifier = iota
  17 + // Literal 文字 e.g. 50
  18 + Literal
  19 + // Operator 计算操作 e.g. + - * /
  20 + Operator
  21 + // COMMA 命令, e.g. (
  22 + COMMA
  23 + // CompareOperator 比较操作 e.g. < = >
  24 + CompareOperator
  25 + // StringArgs 字符串参数
  26 + StringArgs
  27 +)
  28 +
  29 +type Token struct {
  30 + // raw characters
  31 + Tok string
  32 + // type with Literal/Operator
  33 + Type,
  34 + Flag int
  35 +
  36 + Offset int
  37 +}
  38 +
  39 +type Parser struct {
  40 + Source string
  41 +
  42 + ch byte
  43 + offset int
  44 +
  45 + err error
  46 +}
  47 +
  48 +func ParseToken(s string) ([]*Token, error) {
  49 + p := &Parser{
  50 + Source: s,
  51 + err: nil,
  52 + ch: s[0],
  53 + }
  54 + toks := p.parse()
  55 + if p.err != nil {
  56 + return nil, p.err
  57 + }
  58 + return toks, nil
  59 +}
  60 +
  61 +func (p *Parser) parse() []*Token {
  62 + toks := make([]*Token, 0)
  63 + for {
  64 + tok := p.nextTok()
  65 + if tok == nil {
  66 + break
  67 + }
  68 + toks = append(toks, tok)
  69 + }
  70 + return toks
  71 +}
  72 +
  73 +func (p *Parser) nextTok() *Token {
  74 + if p.offset >= len(p.Source) || p.err != nil {
  75 + return nil
  76 + }
  77 + var err error
  78 + for p.isWhitespace(p.ch) && err == nil {
  79 + err = p.nextCh()
  80 + }
  81 + start := p.offset
  82 + var tok *Token
  83 + switch p.ch {
  84 + case
  85 + '(',
  86 + ')',
  87 + '+',
  88 + '-',
  89 + '*',
  90 + '/',
  91 + '^',
  92 + '%',
  93 + '&':
  94 + tok = &Token{
  95 + Tok: string(p.ch),
  96 + Type: Operator,
  97 + }
  98 + tok.Offset = start
  99 + err = p.nextCh()
  100 + case
  101 + '>',
  102 + '<',
  103 + '=':
  104 + if p.isCompareWordChar(p.ch) {
  105 + for p.isCompareWordChar(p.ch) && p.nextCh() == nil {
  106 + }
  107 + tok = &Token{
  108 + Tok: p.Source[start:p.offset],
  109 + Type: CompareOperator,
  110 + }
  111 + tok.Offset = start
  112 + } else if p.ch != ' ' {
  113 + s := fmt.Sprintf("symbol error: unknown '%v', pos [%v:]\n%s",
  114 + string(p.ch),
  115 + start,
  116 + ErrPos(p.Source, start))
  117 + p.err = errors.New(s)
  118 + }
  119 + case
  120 + '0',
  121 + '1',
  122 + '2',
  123 + '3',
  124 + '4',
  125 + '5',
  126 + '6',
  127 + '7',
  128 + '8',
  129 + '9':
  130 + for p.isDigitNum(p.ch) && p.nextCh() == nil {
  131 + if (p.ch == '-' || p.ch == '+') && p.Source[p.offset-1] != 'e' {
  132 + break
  133 + }
  134 + }
  135 + tok = &Token{
  136 + Tok: strings.ReplaceAll(p.Source[start:p.offset], "_", ""),
  137 + Type: Literal,
  138 + }
  139 + tok.Offset = start
  140 + case '"':
  141 + for (p.isDigitNum(p.ch) || p.isChar(p.ch)) && p.nextCh() == nil {
  142 + if p.ch == '"' {
  143 + break
  144 + }
  145 + }
  146 + err = p.nextCh()
  147 + tok = &Token{
  148 + Tok: p.Source[start:p.offset],
  149 + Type: StringArgs,
  150 + }
  151 + tok.Offset = start
  152 + case ',':
  153 + tok = &Token{
  154 + Tok: string(p.ch),
  155 + Type: COMMA,
  156 + }
  157 + tok.Offset = start
  158 + err = p.nextCh()
  159 +
  160 + default:
  161 + if p.isChar(p.ch) {
  162 + for p.isWordChar(p.ch) && p.nextCh() == nil {
  163 + }
  164 + tok = &Token{
  165 + Tok: p.Source[start:p.offset],
  166 + Type: Identifier,
  167 + }
  168 + tok.Offset = start
  169 + } else if p.ch != ' ' {
  170 + s := fmt.Sprintf("symbol error: unknown '%v', pos [%v:]\n%s",
  171 + string(p.ch),
  172 + start,
  173 + ErrPos(p.Source, start))
  174 + p.err = errors.New(s)
  175 + }
  176 + }
  177 + return tok
  178 +}
  179 +
  180 +func (p *Parser) nextCh() error {
  181 + p.offset++
  182 + if p.offset < len(p.Source) {
  183 + p.ch = p.Source[p.offset]
  184 + return nil
  185 + }
  186 + return errors.New("EOF")
  187 +}
  188 +
  189 +func (p *Parser) isWhitespace(c byte) bool {
  190 + return c == ' ' ||
  191 + c == '\t' ||
  192 + c == '\n' ||
  193 + c == '\v' ||
  194 + c == '\f' ||
  195 + c == '\r'
  196 +}
  197 +
  198 +func (p *Parser) isDigitNum(c byte) bool {
  199 + return '0' <= c && c <= '9' || c == '.' || c == '_' || c == 'e' || c == '-' || c == '+'
  200 +}
  201 +
  202 +func (p *Parser) isChar(c byte) bool {
  203 + return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '.' || c == '"'
  204 +}
  205 +
  206 +func (p *Parser) isWordChar(c byte) bool {
  207 + return p.isChar(c) || '0' <= c && c <= '9'
  208 +}
  209 +
  210 +func (p *Parser) isCompareWordChar(c byte) bool {
  211 + return c == '=' || c == '<' || c == '>'
  212 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "encoding/json"
  5 + "fmt"
  6 + "testing"
  7 +)
  8 +
  9 +func TestExecA(t *testing.T) {
  10 + exp := "1+2"
  11 + exec(exp)
  12 +}
  13 +
  14 +func TestExecB(t *testing.T) {
  15 + exp := "1+2-4"
  16 + exec(exp)
  17 +}
  18 +
  19 +func TestExecC(t *testing.T) {
  20 + exp := "1+2-4*3-8"
  21 + exec(exp)
  22 +}
  23 +
  24 +func TestExecD(t *testing.T) {
  25 + exp := "1+2-(4*3-8)"
  26 + exec(exp)
  27 +}
  28 +
  29 +func TestExecE(t *testing.T) {
  30 + exp := "1+2-(4*3+(1-8))"
  31 + exec(exp)
  32 +}
  33 +
  34 +func TestExecF(t *testing.T) {
  35 + exp := "1+(2-(4*3+(1-8)))"
  36 + exec(exp)
  37 +}
  38 +
  39 +func TestExecG(t *testing.T) {
  40 + exp := "((1-2)*(3-8))*((((9+2222))))"
  41 + exec(exp)
  42 +}
  43 +
  44 +func TestExecH(t *testing.T) {
  45 + exp := "0.8888-0.1 * 444 -0.2"
  46 + exec(exp)
  47 +}
  48 +
  49 +func TestExecI(t *testing.T) {
  50 + exp := "0.8888-0.1 * (444 -0.2)"
  51 + exec(exp)
  52 +}
  53 +
  54 +func TestExecJ(t *testing.T) {
  55 + exp := "1_234_567*2-3"
  56 + exec(exp)
  57 +}
  58 +
  59 +func TestExecK(t *testing.T) {
  60 + exp := "2.3e4*4/3"
  61 + exec(exp)
  62 +}
  63 +
  64 +func TestExecL(t *testing.T) {
  65 + exp := "-1+9-88"
  66 + exec(exp)
  67 +}
  68 +
  69 +func TestExecM(t *testing.T) {
  70 + exp := "-1+9-88+(88)"
  71 + exec(exp)
  72 +}
  73 +
  74 +func TestExecN(t *testing.T) {
  75 + exp := "-1+9-88+(-88)*666-1"
  76 + exec(exp)
  77 +}
  78 +
  79 +func TestExecO(t *testing.T) {
  80 + exp := "-(1)+(3)-(-3)*7-((-3))"
  81 + exec(exp)
  82 +}
  83 +
  84 +func TestExecP(t *testing.T) {
  85 + exp := "-(-9+3)"
  86 + exec(exp)
  87 +}
  88 +
  89 +func TestExecQ(t *testing.T) {
  90 + exp := "2e-3*2+2e2+1"
  91 + exec(exp)
  92 +}
  93 +
  94 +func TestExecR(t *testing.T) {
  95 + exp := "3.8 - 56 / (1-1) - 4"
  96 + exec(exp)
  97 +}
  98 +
  99 +func TestExecS(t *testing.T) {
  100 + exp := "noerr(3.8 - 56 / (1-1) - 4)"
  101 + exec(exp)
  102 +}
  103 +
  104 +func TestFunCaller(t *testing.T) {
  105 + funs := []struct {
  106 + Name string
  107 + Argc int
  108 + Fun func(expr ...ExprAST) float64
  109 + Exp string
  110 + R float64
  111 + }{
  112 + //{
  113 + // "double",
  114 + // 1,
  115 + // func(expr ...engine.ExprAST) float64 {
  116 + // return engine.ExprASTResult(expr[0]) * 2
  117 + // },
  118 + // "double(6)",
  119 + // 12,
  120 + //},
  121 + {
  122 + "sum",
  123 + -1,
  124 + nil,
  125 + "sum(if(100+10,table.a,20))",
  126 + 10,
  127 + },
  128 + {
  129 + "sum",
  130 + -1,
  131 + nil,
  132 + "sum(if(100<10,table.a,20))",
  133 + 10,
  134 + },
  135 + {
  136 + "sum",
  137 + -1,
  138 + nil,
  139 + "sum(if(100<10,table.a,20+30))",
  140 + 10,
  141 + },
  142 + {
  143 + "sum",
  144 + -1,
  145 + nil,
  146 + "sum(if(table.a<table.b,table.a,20+30))",
  147 + 10,
  148 + },
  149 + {
  150 + "sum",
  151 + -1,
  152 + nil,
  153 + "sum(if(table.a<=table.b,table.a,20+30))",
  154 + 10,
  155 + },
  156 + }
  157 + for _, f := range funs {
  158 + if f.Fun != nil {
  159 + _ = RegFunction(f.Name, f.Argc, f.Fun)
  160 + }
  161 + r, err := Parse(f.Exp)
  162 + if err != nil {
  163 +
  164 + }
  165 + if r != 0 {
  166 +
  167 + }
  168 + }
  169 +}
  170 +
  171 +func TestFunCaller2(t *testing.T) {
  172 + funs := []struct {
  173 + Name string
  174 + Exp []string
  175 + }{
  176 + {
  177 + "sum",
  178 + []string{"sum(table.a)"},
  179 + },
  180 + {
  181 + "sumif",
  182 + []string{"sumif(table.month,10,table.count)"},
  183 + },
  184 + {
  185 + "if",
  186 + []string{"if(table.month>10,table.count1,table.count2)"},
  187 + },
  188 + {
  189 + "and",
  190 + []string{"and(table.year=2011,table.month=6)"},
  191 + },
  192 + {
  193 + "or",
  194 + []string{"or(table.year=2011,table.year=2012)"},
  195 + },
  196 + {
  197 + "month",
  198 + []string{"month(\"1991-1-1\")"},
  199 + },
  200 + {
  201 + "year",
  202 + []string{"year(\"1991-1-1\")"},
  203 + },
  204 + {
  205 + "round",
  206 + []string{
  207 + "round(1.56)",
  208 + "round(table.a)",
  209 + },
  210 + },
  211 + {
  212 + "rounddown",
  213 + []string{
  214 + "rounddown(1.56)",
  215 + "rounddown(table.a)",
  216 + },
  217 + },
  218 + {
  219 + "roundup",
  220 + []string{
  221 + "roundup(1.56)",
  222 + "roundup(table.a)",
  223 + },
  224 + },
  225 + {
  226 + "count",
  227 + []string{
  228 + "count(1.56)",
  229 + "count(table.a)",
  230 + },
  231 + },
  232 + {
  233 + "&",
  234 + []string{
  235 + "table.a&table.b",
  236 + },
  237 + },
  238 + }
  239 + for _, f := range funs {
  240 + for _, exp := range f.Exp {
  241 + r, err := Parse(exp)
  242 + if err != nil {
  243 + t.Error(err)
  244 + }
  245 + if r != 0 {
  246 +
  247 + }
  248 + }
  249 + }
  250 +}
  251 +
  252 +func Parse(s string) (r float64, err error) {
  253 + toks, err := ParseToken(s)
  254 + if err != nil {
  255 + return 0, err
  256 + }
  257 + ast := NewAST(toks, s)
  258 + if ast.Err != nil {
  259 + return 0, ast.Err
  260 + }
  261 + ar := ast.ParseExpression()
  262 + if ast.Err != nil {
  263 + return 0, ast.Err
  264 + }
  265 + defer func() {
  266 + if e := recover(); e != nil {
  267 + err = e.(error)
  268 + }
  269 + }()
  270 + if ar != nil {
  271 + fmt.Printf("ExprAST: %+v\n", ar)
  272 + }
  273 + arData, _ := json.Marshal(ar)
  274 + fmt.Printf("%s\n", string(arData))
  275 +
  276 + return 0, err
  277 +}
  278 +
  279 +// call engine
  280 +func exec(exp string) {
  281 + // input text -> []token
  282 + toks, err := ParseToken(exp)
  283 + if err != nil {
  284 + fmt.Println("ERROR: " + err.Error())
  285 + return
  286 + }
  287 +
  288 + // []token -> AST Tree
  289 + ast := NewAST(toks, exp)
  290 + if ast.Err != nil {
  291 + fmt.Println("ERROR: " + ast.Err.Error())
  292 + return
  293 + }
  294 + // AST builder
  295 + ar := ast.ParseExpression()
  296 + if ast.Err != nil {
  297 + fmt.Println("ERROR: " + ast.Err.Error())
  298 + return
  299 + }
  300 + fmt.Printf("ExprAST: %+v\n", ar)
  301 + // catch runtime errors
  302 + defer func() {
  303 + if e := recover(); e != nil {
  304 + fmt.Println("ERROR: ", e)
  305 + }
  306 + }()
  307 + // AST traversal -> result
  308 + r := ExprASTResult(ar)
  309 + fmt.Println("progressing ...\t", r)
  310 + fmt.Printf("%s = %v\n", exp, r)
  311 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "fmt"
  6 + "math"
  7 + "math/big"
  8 + "strconv"
  9 + "strings"
  10 +)
  11 +
  12 +// Top level function
  13 +// Analytical expression and execution
  14 +// err is not nil if an error occurs (including arithmetic runtime errors)
  15 +func ParseAndExec(s string) (r float64, err error) {
  16 + toks, err := ParseToken(s)
  17 + if err != nil {
  18 + return 0, err
  19 + }
  20 + ast := NewAST(toks, s)
  21 + if ast.Err != nil {
  22 + return 0, ast.Err
  23 + }
  24 + ar := ast.ParseExpression()
  25 + if ast.Err != nil {
  26 + return 0, ast.Err
  27 + }
  28 + defer func() {
  29 + if e := recover(); e != nil {
  30 + err = e.(error)
  31 + }
  32 + }()
  33 + return ExprASTResult(ar), err
  34 +}
  35 +
  36 +func ErrPos(s string, pos int) string {
  37 + r := strings.Repeat("-", len(s)) + "\n"
  38 + s += "\n"
  39 + for i := 0; i < pos; i++ {
  40 + s += " "
  41 + }
  42 + s += "^\n"
  43 + return r + s + r
  44 +}
  45 +
  46 +// the integer power of a number
  47 +func Pow(x float64, n float64) float64 {
  48 + return math.Pow(x, n)
  49 +}
  50 +
  51 +func expr2Radian(expr ExprAST) float64 {
  52 + r := ExprASTResult(expr)
  53 + if TrigonometricMode == AngleMode {
  54 + r = r / 180 * math.Pi
  55 + }
  56 + return r
  57 +}
  58 +
  59 +// Float64ToStr float64 -> string
  60 +func Float64ToStr(f float64) string {
  61 + return strconv.FormatFloat(f, 'f', -1, 64)
  62 +}
  63 +
  64 +// RegFunction is Top level function
  65 +// register a new function to use in expressions
  66 +// name: be register function name. the same function name only needs to be registered once.
  67 +// argc: this is a number of parameter signatures. should be -1, 0, or a positive integer
  68 +//
  69 +// -1 variable-length argument; >=0 fixed numbers argument
  70 +//
  71 +// fun: function handler
  72 +func RegFunction(name string, argc int, fun func(...ExprAST) float64) error {
  73 + if len(name) == 0 {
  74 + return errors.New("RegFunction name is not empty")
  75 + }
  76 + if argc < -1 {
  77 + return errors.New("RegFunction argc should be -1, 0, or a positive integer")
  78 + }
  79 + if _, ok := defFunc[name]; ok {
  80 + return errors.New("RegFunction name is already exist")
  81 + }
  82 + defFunc[name] = defS{argc, fun}
  83 + return nil
  84 +}
  85 +
  86 +// ExprASTResult is a Top level function
  87 +// AST traversal
  88 +// if an arithmetic runtime error occurs, a panic exception is thrown
  89 +func ExprASTResult(expr ExprAST) float64 {
  90 + var l, r float64
  91 + switch expr.(type) {
  92 + case BinaryExprAST:
  93 + ast := expr.(BinaryExprAST)
  94 + l = ExprASTResult(ast.Lhs)
  95 + r = ExprASTResult(ast.Rhs)
  96 + switch ast.Op {
  97 + case "+":
  98 + lh, _ := new(big.Float).SetString(Float64ToStr(l))
  99 + rh, _ := new(big.Float).SetString(Float64ToStr(r))
  100 + f, _ := new(big.Float).Add(lh, rh).Float64()
  101 + return f
  102 + case "-":
  103 + lh, _ := new(big.Float).SetString(Float64ToStr(l))
  104 + rh, _ := new(big.Float).SetString(Float64ToStr(r))
  105 + f, _ := new(big.Float).Sub(lh, rh).Float64()
  106 + return f
  107 + case "*":
  108 + f, _ := new(big.Float).Mul(new(big.Float).SetFloat64(l), new(big.Float).SetFloat64(r)).Float64()
  109 + return f
  110 + case "/":
  111 + if r == 0 {
  112 + panic(errors.New(
  113 + fmt.Sprintf("violation of arithmetic specification: a division by zero in ExprASTResult: [%g/%g]",
  114 + l,
  115 + r)))
  116 + }
  117 + f, _ := new(big.Float).Quo(new(big.Float).SetFloat64(l), new(big.Float).SetFloat64(r)).Float64()
  118 + return f
  119 + case "%":
  120 + if r == 0 {
  121 + panic(errors.New(
  122 + fmt.Sprintf("violation of arithmetic specification: a division by zero in ExprASTResult: [%g%%%g]",
  123 + l,
  124 + r)))
  125 + }
  126 + return float64(int(l) % int(r))
  127 + case "^":
  128 + return Pow(l, r)
  129 + default:
  130 +
  131 + }
  132 + case NumberExprAST:
  133 + return expr.(NumberExprAST).Val
  134 + case FunCallerExprAST:
  135 + f := expr.(FunCallerExprAST)
  136 + def := defFunc[f.Name]
  137 + return def.fun(f.Args...)
  138 + }
  139 +
  140 + return 0.0
  141 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "math/rand"
  5 + "testing"
  6 + "time"
  7 +)
  8 +
  9 +func TestParseAndExecSimple(t *testing.T) {
  10 + type U struct {
  11 + Expr string
  12 + R float64
  13 + }
  14 + exprs := []U{
  15 + {"1", 1},
  16 + {"--1", 1},
  17 + {"1+2", 3},
  18 + {"-1+2", 1},
  19 + {"-(1+2)", -3},
  20 + {"-(1+2)*5", -15},
  21 + {"-(1+2)*5/3", -5},
  22 + {"1+(-(1+2)*5/3)", -4},
  23 + {"3^4", 81},
  24 + {"3^4.5", 140.29611541307906},
  25 + {"3.5^4.5", 280.7412308013823},
  26 + {"8%2", 0},
  27 + {"8%3", 2},
  28 + {"8%3.5", 2},
  29 + {"1e2", 100},
  30 + {"1e+2", 100},
  31 + {"1e-2", 0.01},
  32 + {"1e-2+1e2", 100.01},
  33 + {"1e-2+1e2*6/3", 200.01},
  34 + {"(1e-2+1e2)*6/3", 200.02},
  35 + {"(88*8)+(1+1+1+1)+(6/1.5)-(99%9*(2^4))", 712},
  36 + {"1/3*3", 1},
  37 + {"123_456_789", 123456789},
  38 + {"123_456_789___", 123456789},
  39 + {"pi", 3.141592653589793},
  40 + {"abs(1)", 1},
  41 + {"abs(-1)", 1},
  42 + {"ceil(90.2)", 91},
  43 + {"ceil(90.8)", 91},
  44 + {"ceil(90.0)", 90},
  45 + {"floor(90.2)", 90},
  46 + {"floor(90.8)", 90},
  47 + {"floor(90.0)", 90},
  48 + {"round(90.0)", 90},
  49 + {"round(90.4)", 90},
  50 + {"round(90.5)", 91},
  51 + {"round(90.9)", 91},
  52 + {"sqrt(4)", 2},
  53 + {"cbrt(27)", 3},
  54 + {"sqrt(4) + cbrt(27)", 5},
  55 + {"sqrt(2^2) + cbrt(3^3)", 5},
  56 + {"127^2+5/2-sqrt(2^2) + cbrt(3^3)", 16132.5},
  57 + {"max(2)", 2},
  58 + {"max(abs(1)+10)", 11},
  59 + {"max(abs(1)+10)*2-1", 21},
  60 + {"max(2,3.5)", 3.5},
  61 + {"max(2^3,3+abs(-1)*6)", 9},
  62 + {"max(2^3,3+abs(-1)*6, 20)", 20},
  63 + {"max(2^3,3+abs(-1)*6,ceil(9.4))", 10},
  64 + {"max(1,2,3,4,5,6,10,7,4,5,6,9.8)", 10},
  65 + {"min(3.5)", 3.5},
  66 + {"min(ceil(1.2))", 2},
  67 + {"min(2,3.5)", 2},
  68 + {"min(2^3,3+abs(-1)*6)", 8},
  69 + {"min(2^3,3+abs(-1)*6,1^10)", 1},
  70 + {"min(99.1,0.2,3,4,5,6,10,7,4,5,6,9.8)", 0.2},
  71 + {"max(2^3,3^2)", 9},
  72 + {"min(2^3,3^2)", 8},
  73 + {"noerr(1/0)", 0},
  74 + {"noerr(1/(1-1))", 0},
  75 + {"0.1+0.2", 0.3},
  76 + {"0.3-0.1", 0.2},
  77 + {"10^-1", 0.1},
  78 + {"10^-2", 0.01},
  79 + {"10^-1*100", 10},
  80 + {"10%0", 0},
  81 + }
  82 + for _, e := range exprs {
  83 + r, _ := ParseAndExec(e.Expr)
  84 + if r != e.R {
  85 + t.Error(e, " ParseAndExec:", r)
  86 + }
  87 + }
  88 +}
  89 +
  90 +func TestParseAndExecTrigonometric(t *testing.T) {
  91 + type U struct {
  92 + Expr string
  93 + RadianMode float64
  94 + AngleMode float64
  95 + }
  96 + exprs := []U{
  97 + {"sin(pi/2)", 1, 0.027412133592044294},
  98 + {"csc(pi/2)", 1, 36.48019577324057},
  99 + {"cos(0)", 1, 1},
  100 + {"sec(0)", 1, 1},
  101 + {"tan(pi/4)", 1, 0.013708642534394057},
  102 + {"cot(pi/4)", 1, 72.94668290394674},
  103 +
  104 + {"sin(90)", 0.893996663600558, 1},
  105 + {"csc(90)", 1.1185724071637082, 1},
  106 + {"cos(0)", 1, 1},
  107 + {"sec(0)", 1, 1},
  108 + {"tan(45)", 1.6197751905438615, 1},
  109 + {"cot(45)", 0.6173696237835551, 1},
  110 + }
  111 + for _, e := range exprs {
  112 + TrigonometricMode = RadianMode
  113 + r, _ := ParseAndExec(e.Expr)
  114 + if r != e.RadianMode {
  115 + t.Error(e, " ParseAndExec RadianMode:", r)
  116 + }
  117 + TrigonometricMode = AngleMode
  118 + r, _ = ParseAndExec(e.Expr)
  119 + if r != e.AngleMode {
  120 + t.Error(e, " ParseAndExec AngleMode:", r)
  121 + }
  122 + }
  123 +}
  124 +
  125 +func TestRegFunction(t *testing.T) {
  126 + funs := []struct {
  127 + Name string
  128 + Argc int
  129 + Fun func(expr ...ExprAST) float64
  130 + Exp string
  131 + R float64
  132 + }{
  133 + {
  134 + "double",
  135 + 1,
  136 + func(expr ...ExprAST) float64 {
  137 + return ExprASTResult(expr[0]) * 2
  138 + },
  139 + "double(6)",
  140 + 12,
  141 + },
  142 + {
  143 + "percentage50",
  144 + 1,
  145 + func(expr ...ExprAST) float64 {
  146 + return ExprASTResult(expr[0]) / 2
  147 + },
  148 + "percentage50(6)",
  149 + 3,
  150 + },
  151 + {
  152 + "range",
  153 + 0,
  154 + func(expr ...ExprAST) float64 {
  155 + return 10.0
  156 + },
  157 + "range()",
  158 + 10,
  159 + },
  160 + {
  161 + "choice",
  162 + -1,
  163 + func(expr ...ExprAST) float64 {
  164 + rand.Seed(time.Now().UnixNano())
  165 + return ExprASTResult(expr[rand.Intn(len(expr))])
  166 + },
  167 + "choice(1.1, 9.8, 2.5, 100)",
  168 + 10,
  169 + },
  170 + }
  171 + for _, f := range funs {
  172 + _ = RegFunction(f.Name, f.Argc, f.Fun)
  173 + r, err := ParseAndExec(f.Exp)
  174 + if f.Name == "choice" {
  175 + if !inSlices(r, []float64{1.1, 9.8, 2.5, 100}) {
  176 + t.Error(err, "RegFunction errors when register new function: ", f.Name)
  177 + }
  178 + continue
  179 + } else if r != f.R {
  180 + t.Error(err, "RegFunction errors when register new function: ", f.Name)
  181 + }
  182 + }
  183 +
  184 +}
  185 +
  186 +func TestParseAndExecError(t *testing.T) {
  187 + exprs := []string{
  188 + "(",
  189 + "((((((",
  190 + "((xscdfddff",
  191 + "(1",
  192 + "(1+",
  193 + "1+",
  194 + "1*",
  195 + "+2344",
  196 + "3+(",
  197 + "4+(90-",
  198 + "3-(4*7-2)+",
  199 + "3-(4*7-2)+98*",
  200 + "1#1",
  201 + "_123_456_789___",
  202 + "1ee3+3",
  203 + "sin()",
  204 + "sin",
  205 + "pi(",
  206 + "sin(1, 50)",
  207 + "max",
  208 + "max()",
  209 + "max(1,)",
  210 + "max(1,4,6,7,5,)",
  211 + "min",
  212 + "min(,)",
  213 + "min()",
  214 + "min(1,)",
  215 + "min(1,998,4,23,234,2,)",
  216 + "min(1,998,4,23,234,2,,,)",
  217 + "1/0",
  218 + "99.9 / (2-1-1)",
  219 + "(1+2)3",
  220 + "1+1 111",
  221 + "1+1 111+2",
  222 + "1 3",
  223 + "1 3-",
  224 + }
  225 + for _, e := range exprs {
  226 + _, err := ParseAndExec(e)
  227 + if err == nil {
  228 + t.Error(e, " this is error expr!")
  229 + }
  230 + }
  231 +}
  232 +
  233 +func inSlices(target float64, s []float64) bool {
  234 + for _, v := range s {
  235 + if v == target {
  236 + return true
  237 + }
  238 + }
  239 + return false
  240 +}
@@ -28,6 +28,16 @@ type Field struct { @@ -28,6 +28,16 @@ type Field struct {
28 Order string `json:"order,omitempty"` 28 Order string `json:"order,omitempty"`
29 } 29 }
30 30
  31 +func (f *Field) SqlTypeEqual(compare *Field) bool {
  32 + if f.SQLType == compare.SQLType {
  33 + return true
  34 + }
  35 + if SQLType(f.SQLType).IsString() == SQLType(compare.SQLType).IsString() {
  36 + return true
  37 + }
  38 + return false
  39 +}
  40 +
31 func (f *Field) Valid() error { 41 func (f *Field) Valid() error {
32 if _, ok := SQLTypeMap[strings.ToUpper(f.SQLType)]; !ok { 42 if _, ok := SQLTypeMap[strings.ToUpper(f.SQLType)]; !ok {
33 return fmt.Errorf("unknown sql type:%v", f.SQLType) 43 return fmt.Errorf("unknown sql type:%v", f.SQLType)
@@ -79,6 +79,12 @@ func (ptr *AppendDataToTableService) AppendData(ctx *domain.Context, fileId int, @@ -79,6 +79,12 @@ func (ptr *AppendDataToTableService) AppendData(ctx *domain.Context, fileId int,
79 continue 79 continue
80 } 80 }
81 } 81 }
  82 + if fromField.SQLType != "" && !toField.SqlTypeEqual(fromField) {
  83 + //return nil, fmt.Errorf("字段【%s】的类型与导入数据表的类型不匹配", toField.Name)
  84 + return map[string]interface{}{
  85 + "result": fmt.Sprintf("字段【%s】的类型与导入数据表的类型不匹配", toField.Name),
  86 + }, nil
  87 + }
82 requestData.To = append(requestData.To, toField) 88 requestData.To = append(requestData.To, toField)
83 requestData.From = append(requestData.From, fromField) 89 requestData.From = append(requestData.From, fromField)
84 } 90 }