-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
176 lines (165 loc) · 3.71 KB
/
main.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package main
import (
"container/ring"
"fmt"
"math/big"
"strings"
"github.com/chzyer/readline"
"github.com/fatih/color"
)
func main() {
color.Cyan("欢迎使用 g-calc 计算器!")
color.Cyan("- 支持 +、-、*、/ 和括号。")
color.Cyan("- 使用上下键查看历史记录。")
color.Cyan("- 输入 'q' 退出。")
fmt.Println()
greenFunc := color.New(color.FgGreen).SprintfFunc()
history := ring.New(10)
rl, err := readline.NewEx(&readline.Config{
Prompt: greenFunc("> "),
HistoryLimit: 10,
HistorySearchFold: true,
})
if err != nil {
color.Red("Error: %v", err)
return
}
defer rl.Close()
for {
input, err := rl.Readline()
if err != nil {
break
}
input = strings.TrimSpace(input)
if input == "q" {
break
}
if input != "" {
history.Value = input
history = history.Next()
_ = rl.SaveHistory(input)
}
result, err := evaluate(input)
if err != nil {
color.Red("错误: %v", err)
} else {
color.Yellow("结果: %v", result.Text('f', 10))
}
}
color.Cyan("谢谢使用,再见!")
fmt.Println()
}
func evaluate(expr string) (*big.Float, error) {
// 替换中文括号为英文括号
expr = strings.ReplaceAll(expr, "(", "(")
expr = strings.ReplaceAll(expr, ")", ")")
tokens, err := tokenize(expr)
if err != nil {
return nil, err
}
return evalRPN(toRPN(tokens))
}
func tokenize(expr string) ([]string, error) {
var tokens []string
var num strings.Builder
for i := 0; i < len(expr); i++ {
c := expr[i]
switch {
case c == ' ':
continue
case c >= '0' && c <= '9' || c == '.':
num.WriteByte(c)
case c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')':
if num.Len() > 0 {
tokens = append(tokens, num.String())
num.Reset()
}
tokens = append(tokens, string(c))
default:
return nil, fmt.Errorf("无效字符: %c", c)
}
}
if num.Len() > 0 {
tokens = append(tokens, num.String())
}
return tokens, nil
}
func toRPN(tokens []string) []string {
var output []string
var stack []string
for _, token := range tokens {
switch token {
case "+", "-", "*", "/":
for len(stack) > 0 && precedence(stack[len(stack)-1]) >= precedence(token) {
output = append(output, stack[len(stack)-1])
stack = stack[:len(stack)-1]
}
stack = append(stack, token)
case "(":
stack = append(stack, token)
case ")":
for len(stack) > 0 && stack[len(stack)-1] != "(" {
output = append(output, stack[len(stack)-1])
stack = stack[:len(stack)-1]
}
if len(stack) > 0 && stack[len(stack)-1] == "(" {
stack = stack[:len(stack)-1]
}
default:
output = append(output, token)
}
}
for len(stack) > 0 {
output = append(output, stack[len(stack)-1])
stack = stack[:len(stack)-1]
}
return output
}
func precedence(op string) int {
switch op {
case "+", "-":
return 1
case "*", "/":
return 2
default:
return 0
}
}
func evalRPN(tokens []string) (*big.Float, error) {
var stack []*big.Float
for _, token := range tokens {
switch token {
case "+", "-", "*", "/":
if len(stack) < 2 {
return nil, fmt.Errorf("无效的表达式")
}
b, a := stack[len(stack)-1], stack[len(stack)-2]
stack = stack[:len(stack)-2]
var result big.Float
switch token {
case "+":
result.Add(a, b)
case "-":
result.Sub(a, b)
case "*":
result.Mul(a, b)
case "/":
if b.Sign() == 0 {
return nil, fmt.Errorf("除数不能为零")
}
result.Quo(a, b)
}
stack = append(stack, &result)
default:
f, _, err := new(big.Float).Parse(token, 10)
if err != nil {
return nil, fmt.Errorf("无效的数字: %s", token)
}
stack = append(stack, f)
}
}
if len(stack) != 1 {
return nil, fmt.Errorf("无效的表达式")
}
return stack[0], nil
}