package calculator
|
|
import (
|
"errors"
|
"fmt"
|
"math"
|
"math/big"
|
"strconv"
|
"strings"
|
)
|
|
// ParseAndExec : 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 := Parse(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
|
}
|
|
// Pow 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.Arg...)
|
}
|
|
return 0.0
|
}
|