-
Notifications
You must be signed in to change notification settings - Fork 1
/
parse.go
127 lines (102 loc) · 2.32 KB
/
parse.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
package eval
import (
"errors"
"strconv"
"strings"
"github.com/jacalz/eval/data"
)
var errMismatchedParenthesis = errors.New("mismatched parenthesis")
func priority(token string) (int, bool) {
switch token {
case "^":
return 2, true
case "*", "/", "%":
return 1, true
case "+", "-":
return 0, true
}
return 0, false
}
func rightAssociated(token string) bool {
switch token {
case "^":
return true
case "*", "/", "%", "+", "-":
return false
}
return false
}
func isNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
// infixToRPN converts an infix notation string to reverse polish notation using a shunting yard algorithm.
func infixToRPN(input string) ([]string, error) {
output := data.NewQueue(16)
operators := data.NewStack(16)
for _, t := range strings.Fields(input) {
if t == "(" {
operators.Push(t)
} else if t == ")" {
foundLeftMatch := false
// Pop items from the stack to the queue until the matching parenthesis is found.
for len(operators.Items) > 0 {
oper, err := operators.Pop()
if err != nil {
return nil, err
}
if oper == "(" {
foundLeftMatch = true
break
}
output.Enqueue(oper)
}
if !foundLeftMatch {
return nil, errMismatchedParenthesis
}
// If the top in the stack is not an operator, pop over the corresponding function.
top, err := operators.Peek()
if err != nil {
continue // Don't fail if stack is empty.
}
if _, ok := priority(top); !ok {
operators.Pop()
output.Enqueue(top)
}
} else if target, ok := priority(t); ok {
for len(operators.Items) > 0 {
top, err := operators.Peek()
if err != nil {
return nil, err
}
if top == "(" {
break
}
prio, _ := priority(top)
if (prio > target && rightAssociated(t)) ||
(target <= prio && !rightAssociated(t)) {
operators.Pop()
output.Enqueue(top)
}
break
}
operators.Push(t)
} else if isNumeric(t) {
output.Enqueue(t)
} else { // Token is a function.
operators.Push(t)
}
}
// Pop remaining items from the stack to the queue.
for len(operators.Items) > 0 {
oper, err := operators.Pop()
if err != nil {
return nil, err
}
if oper == "(" {
return nil, errMismatchedParenthesis
}
output.Enqueue(oper)
}
return output.Items, nil
}