initial commit
This commit is contained in:
commit
24ca753ba3
18 changed files with 1034 additions and 0 deletions
14
.codecov.yml
Normal file
14
.codecov.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 95% # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
23
.travis.yml
Normal file
23
.travis.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
language: go
|
||||
sudo: false
|
||||
|
||||
go_import_path: github.com/neonxp/GoMathExecutor
|
||||
env:
|
||||
global:
|
||||
- TEST_TIMEOUT_SCALE=10
|
||||
- GO111MODULE=on
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
env: LINT=1
|
||||
|
||||
script:
|
||||
- test -z "$LINT" || make lint
|
||||
- make test
|
||||
- make bench
|
||||
|
||||
after_success:
|
||||
- make cover
|
||||
- bash <(curl -s https://codecov.io/bash)
|
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "test",
|
||||
"program": "${workspaceFolder}",
|
||||
"env": {},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
20
LICENSE
Normal file
20
LICENSE
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
22
README.md
Normal file
22
README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# GoMathExecutor [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
Package GoMathExecutor provides simple expression executor.
|
||||
|
||||
## Installation
|
||||
|
||||
`go get github.com/neonxp/GoMathExecutor`
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
calc := executor.NewCalc()
|
||||
calc.AddOperators(executor.MathOperators) // Loads default MathOperators (see: defaults.go)
|
||||
calc.Prepare("2+2*2") // Prepare expression
|
||||
calc.Execute(nil) // == 6, nil
|
||||
calc.Prepare("x * (y+z)") // Prepare another expression with variables
|
||||
calc.Execute(map[string]float64{
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"z": 1,
|
||||
}) // == 9, nil
|
||||
```
|
142
calculator.go
Normal file
142
calculator.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Calc calculates expressions
|
||||
type Calc struct {
|
||||
preparedTokens []*token
|
||||
functions map[string]*Function
|
||||
operators map[string]*Operator
|
||||
}
|
||||
|
||||
// NewCalc instantinates new calculator
|
||||
func NewCalc() *Calc {
|
||||
c := &Calc{
|
||||
functions: map[string]*Function{},
|
||||
operators: map[string]*Operator{},
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Prepare expression before execution
|
||||
func (c *Calc) Prepare(expression string) error {
|
||||
t := newTokenizer(expression, c.operators)
|
||||
if err := t.tokenize(); err != nil {
|
||||
return err
|
||||
}
|
||||
tkns, err := t.toRPN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.preparedTokens = tkns
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute prepared expression with variables at `vars` argument
|
||||
func (c *Calc) Execute(vars map[string]float64) (float64, error) {
|
||||
if len(c.preparedTokens) == 0 {
|
||||
return 0, errors.New("must prepare expression")
|
||||
}
|
||||
if vars == nil {
|
||||
vars = map[string]float64{}
|
||||
}
|
||||
var stack []float64
|
||||
for _, tkn := range c.preparedTokens {
|
||||
switch tkn.Type {
|
||||
case literalType:
|
||||
stack = append(stack, tkn.FValue)
|
||||
case operatorType:
|
||||
sz := len(stack)
|
||||
if sz < 2 {
|
||||
return 0, errors.New("empty stack")
|
||||
}
|
||||
var args []float64
|
||||
args, stack = stack[sz-2:], stack[:sz-2]
|
||||
|
||||
if op, ok := c.operators[tkn.SValue]; ok {
|
||||
res, err := op.Fn(args[0], args[1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
stack = append(stack, res)
|
||||
} else {
|
||||
return 0, fmt.Errorf("unknown operator '%s'", tkn.SValue)
|
||||
}
|
||||
case functionType:
|
||||
fn, exists := c.functions[tkn.SValue]
|
||||
if !exists {
|
||||
return 0, fmt.Errorf("unknown function '%s'", tkn.SValue)
|
||||
}
|
||||
sz := len(stack)
|
||||
if sz < fn.Places {
|
||||
return 0, errors.New("not enough args")
|
||||
}
|
||||
var args []float64
|
||||
args, stack = stack[sz-fn.Places:], stack[:sz-fn.Places]
|
||||
res, err := fn.Fn(args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
stack = append(stack, res)
|
||||
case variableType:
|
||||
res, exists := vars[tkn.SValue]
|
||||
if !exists {
|
||||
return 0, fmt.Errorf("unknown variable '%s'", tkn.SValue)
|
||||
}
|
||||
stack = append(stack, res)
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown token %d, %s, %f", tkn.Type, tkn.SValue, tkn.FValue)
|
||||
}
|
||||
}
|
||||
if len(stack) != 1 {
|
||||
return 0, errors.New("invalid expression")
|
||||
}
|
||||
return stack[0], nil
|
||||
}
|
||||
|
||||
// AddFunction adds custom function
|
||||
func (c *Calc) AddFunction(cf *Function) {
|
||||
c.functions[cf.Name] = cf
|
||||
}
|
||||
|
||||
// AddOperator adds custom operator
|
||||
func (c *Calc) AddOperator(op *Operator) {
|
||||
c.operators[op.Op] = op
|
||||
}
|
||||
|
||||
// AddFunctions xadds many custom functions
|
||||
func (c *Calc) AddFunctions(funcs []*Function) {
|
||||
for _, fn := range funcs {
|
||||
c.AddFunction(fn)
|
||||
}
|
||||
}
|
||||
|
||||
// AddOperators adds many custom operators
|
||||
func (c *Calc) AddOperators(operators []*Operator) {
|
||||
for _, op := range operators {
|
||||
c.AddOperator(op)
|
||||
}
|
||||
}
|
95
calculator_test.go
Normal file
95
calculator_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCalc(t *testing.T) {
|
||||
funcs := []*Function{
|
||||
NewFunction("negative", func(args ...float64) (f float64, err error) {
|
||||
return -args[0], nil
|
||||
}, 1),
|
||||
NewFunction("sum", func(args ...float64) (f float64, err error) {
|
||||
return args[0] + args[1], nil
|
||||
}, 2),
|
||||
}
|
||||
operators := []*Operator{
|
||||
NewOperator("==", 1, LeftAssoc, func(a, b float64) (float64, error) {
|
||||
if a == b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}),
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
expression string
|
||||
expected float64
|
||||
vars map[string]float64
|
||||
funcs []*Function
|
||||
operators []*Operator
|
||||
}{
|
||||
{"simple", "((15/(7-(1+1)))*-3)-(-2+(1+1))", ((15.0 / (7.0 - (1.0 + 1.0))) * -3.0) - (-2.0 + (1.0 + 1.0)), nil, nil, nil},
|
||||
{"variables", "a+b*c", 14.0, map[string]float64{"a": 2.0, "b": 3.0, "c": 4.0}, nil, nil},
|
||||
{"functions 1 arg", "negative(10)", -10.0, nil, funcs, nil},
|
||||
{"functions 2 arg", "negative(sum(10, 20)+20)", -50.0, nil, funcs, nil},
|
||||
{"custom operator", "10 == 10", 1, nil, nil, operators},
|
||||
{"custom operator 2", "10 == 12", 0, nil, nil, operators},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewCalc()
|
||||
c.AddOperators(MathOperators)
|
||||
if test.funcs != nil {
|
||||
c.AddFunctions(test.funcs)
|
||||
}
|
||||
if test.operators != nil {
|
||||
c.AddOperators(test.operators)
|
||||
}
|
||||
if err := c.Prepare(test.expression); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actual, err := c.Execute(test.vars)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if actual != test.expected {
|
||||
t.Errorf("Expected %f, actual %f", test.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalc2(t *testing.T) {
|
||||
c := NewCalc()
|
||||
c.AddOperators(MathOperators)
|
||||
if err := c.Prepare("((15/(7-(1+1)))*-3)-(-2+(1+1))"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actual, err := c.Execute(nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := ((15.0 / (7.0 - (1.0 + 1.0))) * -3.0) - (-2.0 + (1.0 + 1.0))
|
||||
if actual != expected {
|
||||
t.Errorf("Expected %f, actual %f", expected, actual)
|
||||
}
|
||||
}
|
17
checklicense.sh
Executable file
17
checklicense.sh
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
ERROR_COUNT=0
|
||||
while read -r file
|
||||
do
|
||||
case "$(head -1 "${file}")" in
|
||||
*"Copyright (c) "*" Alexander Kiryukhin <a.kiryukhin@mail.ru>")
|
||||
# everything's cool
|
||||
;;
|
||||
*)
|
||||
echo "$file is missing license header."
|
||||
(( ERROR_COUNT++ ))
|
||||
;;
|
||||
esac
|
||||
done < <(git ls-files "*\.go")
|
||||
|
||||
exit $ERROR_COUNT
|
72
defaults.go
Normal file
72
defaults.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
import "math"
|
||||
|
||||
// MathOperators is default set for math expressions
|
||||
var MathOperators = []*Operator{
|
||||
{Op: "+", Assoc: LeftAssoc, Priority: 10, Fn: func(a float64, b float64) (float64, error) { return a + b, nil }},
|
||||
{Op: "-", Assoc: LeftAssoc, Priority: 10, Fn: func(a float64, b float64) (float64, error) { return a - b, nil }},
|
||||
{Op: "*", Assoc: LeftAssoc, Priority: 20, Fn: func(a float64, b float64) (float64, error) { return a * b, nil }},
|
||||
{Op: "/", Assoc: LeftAssoc, Priority: 20, Fn: func(a float64, b float64) (float64, error) { return a / b, nil }},
|
||||
{Op: "^", Assoc: RightAssoc, Priority: 30, Fn: func(a, b float64) (float64, error) { return math.Pow(a, b), nil }},
|
||||
}
|
||||
|
||||
// LogicOperators is default set for logic expressions
|
||||
var LogicOperators = []*Operator{
|
||||
{Op: "==", Assoc: LeftAssoc, Priority: 0, Fn: func(a float64, b float64) (float64, error) {
|
||||
if a == b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}},
|
||||
{Op: "!=", Assoc: LeftAssoc, Priority: 0, Fn: func(a float64, b float64) (float64, error) {
|
||||
if a != b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}},
|
||||
{Op: ">", Assoc: LeftAssoc, Priority: 0, Fn: func(a float64, b float64) (float64, error) {
|
||||
if a > b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}},
|
||||
{Op: "<", Assoc: LeftAssoc, Priority: 0, Fn: func(a float64, b float64) (float64, error) {
|
||||
if a < b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}},
|
||||
{Op: ">=", Assoc: LeftAssoc, Priority: 0, Fn: func(a float64, b float64) (float64, error) {
|
||||
if a >= b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}},
|
||||
{Op: "<=", Assoc: LeftAssoc, Priority: 0, Fn: func(a float64, b float64) (float64, error) {
|
||||
if a <= b {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}},
|
||||
}
|
38
doc.go
Normal file
38
doc.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Package GoMathExecutor provides simple expression executor.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// ```
|
||||
// calc := executor.NewCalc()
|
||||
// calc.AddOperators(executor.MathOperators) // Loads default MathOperators (see: defaults.go)
|
||||
// calc.Prepare("2+2*2") // Prepare expression
|
||||
// calc.Execute(nil) // == 6, nil
|
||||
// calc.Prepare("x * (y+z)") // Prepare another expression with variables
|
||||
// calc.Execute(map[string]float64{
|
||||
// "x": 3,
|
||||
// "y": 2,
|
||||
// "z": 1,
|
||||
// }) // == 9, nil
|
||||
// ```
|
||||
|
||||
package executor // import "github.com/neonxp/GoMathExecutor"
|
57
examples/logic.go
Normal file
57
examples/logic.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
executor "github.com/neonxp/GoMathExecutor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := executor.NewCalc()
|
||||
c.AddOperators(executor.MathOperators)
|
||||
c.AddOperators(executor.LogicOperators)
|
||||
c.Prepare("x == (y+z)")
|
||||
log.Println(c.Execute(map[string]float64{
|
||||
"x": 10,
|
||||
"y": 2,
|
||||
"z": 8,
|
||||
}))
|
||||
log.Println(c.Execute(map[string]float64{
|
||||
"x": 10,
|
||||
"y": 2,
|
||||
"z": 10,
|
||||
}))
|
||||
c.Prepare("x != (y+z)")
|
||||
log.Println(c.Execute(map[string]float64{
|
||||
"x": 10,
|
||||
"y": 2,
|
||||
"z": 8,
|
||||
}))
|
||||
log.Println(c.Execute(map[string]float64{
|
||||
"x": 10,
|
||||
"y": 2,
|
||||
"z": 10,
|
||||
}))
|
||||
}
|
42
examples/math.go
Normal file
42
examples/math.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
executor "github.com/neonxp/GoMathExecutor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := executor.NewCalc()
|
||||
c.AddOperators(executor.MathOperators)
|
||||
c.Prepare("2+2*2")
|
||||
log.Println(c.Execute(nil))
|
||||
c.Prepare("x * (y+z)")
|
||||
log.Println(c.Execute(map[string]float64{
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"z": 1,
|
||||
}))
|
||||
}
|
33
function.go
Normal file
33
function.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
// Function represents custom functions
|
||||
type Function struct {
|
||||
Name string
|
||||
Fn func(args ...float64) (float64, error)
|
||||
Places int
|
||||
}
|
||||
|
||||
// NewFunction creates Function instance
|
||||
func NewFunction(name string, fn func(args ...float64) (float64, error), places int) *Function {
|
||||
return &Function{Name: name, Fn: fn, Places: places}
|
||||
}
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module github.com/neonxp/GoMathExecutor
|
||||
|
||||
go 1.13
|
44
operator.go
Normal file
44
operator.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
// Operator implements math operators
|
||||
type Operator struct {
|
||||
Op string
|
||||
Priority int
|
||||
Assoc Assoc
|
||||
Fn func(a float64, b float64) (float64, error)
|
||||
}
|
||||
|
||||
// NewOperator returns new instance of Operator
|
||||
func NewOperator(op string, priority int, assoc Assoc, fn func(a float64, b float64) (float64, error)) *Operator {
|
||||
return &Operator{Op: op, Priority: priority, Assoc: assoc, Fn: fn}
|
||||
}
|
||||
|
||||
// Assoc right or left association of operator
|
||||
type Assoc int
|
||||
|
||||
// LeftAssoc for left associated operators
|
||||
// RighAssoc for right associated operators
|
||||
const (
|
||||
LeftAssoc Assoc = iota
|
||||
RightAssoc
|
||||
)
|
242
tokenizer.go
Normal file
242
tokenizer.go
Normal file
|
@ -0,0 +1,242 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type tokenizer struct {
|
||||
str string
|
||||
numberBuffer string
|
||||
strBuffer string
|
||||
allowNegative bool
|
||||
tkns []*token
|
||||
operators map[string]*Operator
|
||||
}
|
||||
|
||||
func newTokenizer(str string, operators map[string]*Operator) *tokenizer {
|
||||
return &tokenizer{str: str, numberBuffer: "", strBuffer: "", allowNegative: true, tkns: []*token{}, operators: operators}
|
||||
}
|
||||
|
||||
func (t *tokenizer) emptyNumberBufferAsLiteral() error {
|
||||
if t.numberBuffer != "" {
|
||||
f, err := strconv.ParseFloat(t.numberBuffer, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid number %s", t.numberBuffer)
|
||||
}
|
||||
t.tkns = append(t.tkns, newToken(literalType, "", f))
|
||||
}
|
||||
t.numberBuffer = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tokenizer) emptyStrBufferAsVariable() {
|
||||
if t.strBuffer != "" {
|
||||
t.tkns = append(t.tkns, newToken(variableType, t.strBuffer, 0))
|
||||
t.strBuffer = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tokenizer) tokenize() error {
|
||||
for _, ch := range t.str {
|
||||
if ch == ' ' {
|
||||
continue
|
||||
}
|
||||
ch := byte(ch)
|
||||
switch true {
|
||||
case isAlpha(ch):
|
||||
if t.numberBuffer != "" {
|
||||
if err := t.emptyNumberBufferAsLiteral(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.tkns = append(t.tkns, newToken(operatorType, "*", 0))
|
||||
t.numberBuffer = ""
|
||||
}
|
||||
t.allowNegative = false
|
||||
t.strBuffer += string(ch)
|
||||
case isNumber(ch):
|
||||
t.numberBuffer += string(ch)
|
||||
t.allowNegative = false
|
||||
case isDot(ch):
|
||||
t.numberBuffer += string(ch)
|
||||
t.allowNegative = false
|
||||
case isLP(ch):
|
||||
if t.strBuffer != "" {
|
||||
t.tkns = append(t.tkns, newToken(functionType, t.strBuffer, 0))
|
||||
t.strBuffer = ""
|
||||
} else if t.numberBuffer != "" {
|
||||
if err := t.emptyNumberBufferAsLiteral(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.tkns = append(t.tkns, newToken(operatorType, "*", 0))
|
||||
t.numberBuffer = ""
|
||||
}
|
||||
t.allowNegative = true
|
||||
t.tkns = append(t.tkns, newToken(leftParenthesisType, "", 0))
|
||||
case isRP(ch):
|
||||
if err := t.emptyNumberBufferAsLiteral(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.emptyStrBufferAsVariable()
|
||||
t.allowNegative = false
|
||||
t.tkns = append(t.tkns, newToken(rightParenthesisType, "", 0))
|
||||
case isComma(ch):
|
||||
if err := t.emptyNumberBufferAsLiteral(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.emptyStrBufferAsVariable()
|
||||
t.tkns = append(t.tkns, newToken(funcSep, "", 0))
|
||||
t.allowNegative = true
|
||||
default:
|
||||
if t.allowNegative && ch == '-' {
|
||||
t.numberBuffer += "-"
|
||||
t.allowNegative = false
|
||||
continue
|
||||
}
|
||||
if err := t.emptyNumberBufferAsLiteral(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.emptyStrBufferAsVariable()
|
||||
if len(t.tkns) > 0 && t.tkns[len(t.tkns)-1].Type == operatorType {
|
||||
t.tkns[len(t.tkns)-1].SValue += string(ch)
|
||||
} else {
|
||||
t.tkns = append(t.tkns, newToken(operatorType, string(ch), 0))
|
||||
}
|
||||
t.allowNegative = true
|
||||
}
|
||||
}
|
||||
if err := t.emptyNumberBufferAsLiteral(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.emptyStrBufferAsVariable()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tokenizer) toRPN() ([]*token, error) {
|
||||
var tkns []*token
|
||||
var stack tokenStack
|
||||
for _, tkn := range t.tkns {
|
||||
switch tkn.Type {
|
||||
case literalType:
|
||||
tkns = append(tkns, tkn)
|
||||
case variableType:
|
||||
tkns = append(tkns, tkn)
|
||||
case functionType:
|
||||
stack.Push(tkn)
|
||||
case funcSep:
|
||||
for stack.Head().Type != leftParenthesisType {
|
||||
if stack.Head().Type == eof {
|
||||
return nil, ErrInvalidExpression
|
||||
}
|
||||
tkns = append(tkns, stack.Pop())
|
||||
}
|
||||
case operatorType:
|
||||
leftOp, ok := t.operators[tkn.SValue]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown operator: %s", tkn.SValue)
|
||||
}
|
||||
for {
|
||||
if stack.Head().Type == operatorType {
|
||||
rightOp, ok := t.operators[stack.Head().SValue]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown operator: %s", stack.Head().SValue)
|
||||
}
|
||||
if leftOp.Priority < rightOp.Priority || (leftOp.Priority == rightOp.Priority && leftOp.Assoc == RightAssoc) {
|
||||
tkns = append(tkns, stack.Pop())
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
stack.Push(tkn)
|
||||
case leftParenthesisType:
|
||||
stack.Push(tkn)
|
||||
case rightParenthesisType:
|
||||
for stack.Head().Type != leftParenthesisType {
|
||||
if stack.Head().Type == eof {
|
||||
return nil, ErrInvalidParenthesis
|
||||
}
|
||||
tkns = append(tkns, stack.Pop())
|
||||
}
|
||||
stack.Pop()
|
||||
if stack.Head().Type == functionType {
|
||||
tkns = append(tkns, stack.Pop())
|
||||
}
|
||||
}
|
||||
}
|
||||
for stack.Head().Type != eof {
|
||||
if stack.Head().Type == leftParenthesisType {
|
||||
return nil, ErrInvalidParenthesis
|
||||
}
|
||||
tkns = append(tkns, stack.Pop())
|
||||
}
|
||||
return tkns, nil
|
||||
}
|
||||
|
||||
type tokenStack struct {
|
||||
ts []*token
|
||||
}
|
||||
|
||||
func (ts *tokenStack) Push(t *token) {
|
||||
ts.ts = append(ts.ts, t)
|
||||
}
|
||||
|
||||
func (ts *tokenStack) Pop() *token {
|
||||
if len(ts.ts) == 0 {
|
||||
return &token{Type: eof}
|
||||
}
|
||||
var head *token
|
||||
head, ts.ts = ts.ts[len(ts.ts)-1], ts.ts[:len(ts.ts)-1]
|
||||
return head
|
||||
}
|
||||
|
||||
func (ts *tokenStack) Head() *token {
|
||||
if len(ts.ts) == 0 {
|
||||
return &token{Type: eof}
|
||||
}
|
||||
return ts.ts[len(ts.ts)-1]
|
||||
}
|
||||
|
||||
func isComma(ch byte) bool {
|
||||
return ch == ','
|
||||
}
|
||||
|
||||
func isDot(ch byte) bool {
|
||||
return ch == '.'
|
||||
}
|
||||
|
||||
func isNumber(ch byte) bool {
|
||||
return ch >= '0' && ch <= '9'
|
||||
}
|
||||
|
||||
func isAlpha(ch byte) bool {
|
||||
return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_'
|
||||
}
|
||||
|
||||
func isLP(ch byte) bool {
|
||||
return ch == '('
|
||||
}
|
||||
|
||||
func isRP(ch byte) bool {
|
||||
return ch == ')'
|
||||
}
|
98
tokenizer_test.go
Normal file
98
tokenizer_test.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTokenize(t *testing.T) {
|
||||
operators := map[string]*Operator{
|
||||
"+": {Op: "+", Assoc: LeftAssoc, Priority: 1, Fn: func(a float64, b float64) (float64, error) { return a + b, nil }},
|
||||
"-": {Op: "-", Assoc: LeftAssoc, Priority: 1, Fn: func(a float64, b float64) (float64, error) { return a - b, nil }},
|
||||
"*": {Op: "*", Assoc: LeftAssoc, Priority: 2, Fn: func(a float64, b float64) (float64, error) { return a * b, nil }},
|
||||
"/": {Op: "/", Assoc: LeftAssoc, Priority: 2, Fn: func(a float64, b float64) (float64, error) { return a / b, nil }},
|
||||
}
|
||||
tk := newTokenizer("((15/(7-(1+1)))*-3)-(-2+(1+1))", operators)
|
||||
if err := tk.tokenize(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
tkns, err := tk.toRPN()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := []token{
|
||||
{literalType, "", 15},
|
||||
{literalType, "", 7},
|
||||
{literalType, "", 1},
|
||||
{literalType, "", 1},
|
||||
{operatorType, "+", 0},
|
||||
{operatorType, "-", 0},
|
||||
{operatorType, "/", 0},
|
||||
{literalType, "", -3},
|
||||
{operatorType, "*", 0},
|
||||
{literalType, "", -2},
|
||||
{literalType, "", 1},
|
||||
{literalType, "", 1},
|
||||
{operatorType, "+", 0},
|
||||
{operatorType, "+", 0},
|
||||
{operatorType, "-", 0},
|
||||
}
|
||||
if len(tkns) != len(expected) {
|
||||
t.Errorf("Expected len = %d, got %d", len(expected), len(tkns))
|
||||
}
|
||||
for i, tkn := range tkns {
|
||||
if tkn.Type != expected[i].Type {
|
||||
t.Errorf("Expected type %d, got %d at pos %d", expected[i].Type, tkn.Type, i)
|
||||
}
|
||||
if tkn.SValue != expected[i].SValue {
|
||||
t.Errorf("Expected %s, got %s at pos %d", expected[i].SValue, tkn.SValue, i)
|
||||
}
|
||||
if tkn.FValue != expected[i].FValue {
|
||||
t.Errorf("Expected %f, got %f at pos %d", expected[i].FValue, tkn.FValue, i)
|
||||
}
|
||||
}
|
||||
tk = newTokenizer("a**b==10", operators)
|
||||
if err := tk.tokenize(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected = []token{
|
||||
{variableType, "a", 0},
|
||||
{operatorType, "**", 0},
|
||||
{variableType, "b", 0},
|
||||
{operatorType, "==", 0},
|
||||
{literalType, "", 10},
|
||||
}
|
||||
if len(tk.tkns) != len(expected) {
|
||||
t.Errorf("Expected len = %d, got %d", len(expected), len(tkns))
|
||||
}
|
||||
for i, tkn := range tk.tkns {
|
||||
if tkn.Type != expected[i].Type {
|
||||
t.Errorf("Expected type %d, got %d at pos %d", expected[i].Type, tkn.Type, i)
|
||||
}
|
||||
if tkn.SValue != expected[i].SValue {
|
||||
t.Errorf("Expected %s, got %s at pos %d", expected[i].SValue, tkn.SValue, i)
|
||||
}
|
||||
if tkn.FValue != expected[i].FValue {
|
||||
t.Errorf("Expected %f, got %f at pos %d", expected[i].FValue, tkn.FValue, i)
|
||||
}
|
||||
}
|
||||
}
|
55
types.go
Normal file
55
types.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package executor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrInvalidExpression invalid expression error
|
||||
// ErrInvalidParenthesis invalid parenthesis error
|
||||
var (
|
||||
ErrInvalidExpression = errors.New("invalid expression")
|
||||
ErrInvalidParenthesis = errors.New("invalid parenthesis")
|
||||
)
|
||||
|
||||
type tokenType int
|
||||
|
||||
const (
|
||||
literalType tokenType = iota
|
||||
variableType
|
||||
operatorType
|
||||
leftParenthesisType
|
||||
rightParenthesisType
|
||||
functionType
|
||||
funcSep
|
||||
eof
|
||||
)
|
||||
|
||||
type token struct {
|
||||
Type tokenType
|
||||
SValue string
|
||||
FValue float64
|
||||
}
|
||||
|
||||
func newToken(ttype tokenType, SValue string, FValue float64) *token {
|
||||
return &token{Type: ttype, SValue: SValue, FValue: FValue}
|
||||
}
|
Loading…
Reference in a new issue