-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcalculator.go
147 lines (136 loc) · 3.15 KB
/
calculator.go
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
142
143
144
145
146
147
package calculator
import (
"math"
"strconv"
"strings"
)
var operators = map[string]struct {
prec int
rAssoc bool
function func(float64, float64) float64
}{
"^": {4, true, math.Pow},
"*": {3, false, func(x, y float64) float64 { return x * y }},
"/": {3, false, func(x, y float64) float64 { return x / y }},
"+": {2, false, func(x, y float64) float64 { return x + y }},
"-": {2, false, func(x, y float64) float64 { return x - y }},
}
var functions = map[string]func(float64) float64{
"sin": math.Sin,
"cos": math.Cos,
"sqrt": math.Sqrt,
"tan": math.Tan,
}
var constants = map[string]float64{
"e": math.E,
"pi": math.Pi,
"phi": math.Phi,
}
func isParentheses(token string) bool {
switch token {
case "(",
")":
return true
}
return false
}
func CmdLineInputParsing(input string) []string {
var output []string
input = strings.Replace(input, " ", "", -1)
i := 0
for j, token := range input {
token := string(token)
if _, exists := operators[token]; exists || isParentheses(token) {
switch {
case i < j:
output = append(output, input[i:j])
fallthrough
case i == j:
output = append(output, token)
}
i = j + 1
} else {
continue
}
}
if i < len(input) {
output = append(output, input[i:])
}
return output
}
func ShuntingYardAlgorithm(input []string) []string {
var stack []string
var rpn []string
for _, token := range input {
switch token {
case "(":
stack = append(stack, token)
case ")":
for {
operator := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if operator == "(" {
break
}
rpn = append(rpn, operator)
}
default:
if operator, exists := operators[token]; exists {
for len(stack) > 0 {
top := stack[len(stack)-1]
if prevOp, exists := operators[top]; !exists || prevOp.prec < operator.prec || (prevOp.prec == operator.prec && operator.rAssoc) {
break
}
stack = stack[:len(stack)-1]
rpn = append(rpn, top)
}
stack = append(stack, token)
} else if _, exists := functions[token]; exists {
stack = append(stack, token)
} else {
rpn = append(rpn, token)
}
}
}
// drain the stack
for len(stack) > 0 {
op := stack[len(stack)-1]
stack = stack[:len(stack)-1]
rpn = append(rpn, op)
}
return rpn
}
func ComputeResult(rpn []string) float64 {
var result []float64
for _, token := range rpn {
if operator, exists := operators[token]; exists {
// pop y
y := result[len(result)-1]
result = result[:len(result)-1]
// pop x
x := result[len(result)-1]
result = result[:len(result)-1]
x = operator.function(x, y)
result = append(result, x)
} else if function, exists := functions[token]; exists {
x := result[len(result)-1]
result = result[:len(result)-1]
x = function(x)
result = append(result, x)
} else {
if value, exists := constants[token]; exists {
result = append(result, value)
} else {
value, _ := strconv.ParseFloat(token, 64)
result = append(result, value)
}
}
}
return result[0]
}
func Calculate(input string) float64 {
tokens := CmdLineInputParsing(input)
rpn := ShuntingYardAlgorithm(tokens)
result := ComputeResult(rpn)
return result
}