ast_util.go
3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package astexpr
import (
"errors"
"fmt"
"math"
"math/big"
"strconv"
"strings"
)
// Top level function
// Analytical expression and execution
// err is not nil if an error occurs (including arithmetic runtime errors)
func ParseAndExec(s string) (r float64, err error) {
toks, err := ParseToken(s)
if err != nil {
return 0, err
}
ast := NewAST(toks, s)
if ast.Err != nil {
return 0, ast.Err
}
ar := ast.ParseExpression()
if ast.Err != nil {
return 0, ast.Err
}
defer func() {
if e := recover(); e != nil {
err = e.(error)
}
}()
return ExprASTResult(ar), err
}
func ErrPos(s string, pos int) string {
r := strings.Repeat("-", len(s)) + "\n"
s += "\n"
for i := 0; i < pos; i++ {
s += " "
}
s += "^\n"
return r + s + r
}
// the integer power of a number
func Pow(x float64, n float64) float64 {
return math.Pow(x, n)
}
func expr2Radian(expr ExprAST) float64 {
r := ExprASTResult(expr)
if TrigonometricMode == AngleMode {
r = r / 180 * math.Pi
}
return r
}
// Float64ToStr float64 -> string
func Float64ToStr(f float64) string {
return strconv.FormatFloat(f, 'f', -1, 64)
}
// RegFunction is Top level function
// register a new function to use in expressions
// name: be register function name. the same function name only needs to be registered once.
// argc: this is a number of parameter signatures. should be -1, 0, or a positive integer
//
// -1 variable-length argument; >=0 fixed numbers argument
//
// fun: function handler
func RegFunction(name string, argc int, fun func(...ExprAST) float64) error {
if len(name) == 0 {
return errors.New("RegFunction name is not empty")
}
if argc < -1 {
return errors.New("RegFunction argc should be -1, 0, or a positive integer")
}
if _, ok := defFunc[name]; ok {
return errors.New("RegFunction name is already exist")
}
defFunc[name] = defS{argc, fun}
return nil
}
// ExprASTResult is a Top level function
// AST traversal
// if an arithmetic runtime error occurs, a panic exception is thrown
func ExprASTResult(expr ExprAST) float64 {
var l, r float64
switch expr.(type) {
case BinaryExprAST:
ast := expr.(BinaryExprAST)
l = ExprASTResult(ast.Lhs)
r = ExprASTResult(ast.Rhs)
switch ast.Op {
case "+":
lh, _ := new(big.Float).SetString(Float64ToStr(l))
rh, _ := new(big.Float).SetString(Float64ToStr(r))
f, _ := new(big.Float).Add(lh, rh).Float64()
return f
case "-":
lh, _ := new(big.Float).SetString(Float64ToStr(l))
rh, _ := new(big.Float).SetString(Float64ToStr(r))
f, _ := new(big.Float).Sub(lh, rh).Float64()
return f
case "*":
f, _ := new(big.Float).Mul(new(big.Float).SetFloat64(l), new(big.Float).SetFloat64(r)).Float64()
return f
case "/":
if r == 0 {
panic(errors.New(
fmt.Sprintf("violation of arithmetic specification: a division by zero in ExprASTResult: [%g/%g]",
l,
r)))
}
f, _ := new(big.Float).Quo(new(big.Float).SetFloat64(l), new(big.Float).SetFloat64(r)).Float64()
return f
case "%":
if r == 0 {
panic(errors.New(
fmt.Sprintf("violation of arithmetic specification: a division by zero in ExprASTResult: [%g%%%g]",
l,
r)))
}
return float64(int(l) % int(r))
case "^":
return Pow(l, r)
default:
}
case NumberExprAST:
return expr.(NumberExprAST).Val
case FunCallerExprAST:
f := expr.(FunCallerExprAST)
def := defFunc[f.Name]
return def.fun(f.Args...)
}
return 0.0
}