02a7661d85026c69e2d60fd203116df0b0a07fe1
- Author
- TheEdgeOfRage <git@theedgeofrage.com>
- Committer
- TheEdgeOfRage <git@theedgeofrage.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/cmd/main.go b/cmd/main.go
2new file mode 100644
3index 0000000000000000000000000000000000000000..eca8fdcf42fd7e407a29d86f9a8dd6adad01f30e
4--- /dev/null
5+++ b/cmd/main.go
6@@ -0,0 +1,38 @@
7+package main
8+
9+import (
10+ "bufio"
11+ "errors"
12+ "fmt"
13+ "io"
14+ "os"
15+
16+ "gitea.theedgeofrage.com/theedgeofrage/rpn/rpn"
17+)
18+
19+func main() {
20+ rpn := rpn.NewRPN()
21+ reader := bufio.NewReader(os.Stdin)
22+ for {
23+ rpn.PrintStack()
24+ // input := ""
25+ // _, err := fmt.Scanln(&input)
26+
27+ fmt.Print("> ")
28+ input, err := reader.ReadString('\n')
29+ if err != nil {
30+ if errors.Is(err, io.EOF) {
31+ fmt.Println()
32+ return
33+ }
34+ fmt.Println(err)
35+ continue
36+ }
37+
38+ err = rpn.Eval(input)
39+ if err != nil {
40+ fmt.Println(err)
41+ continue
42+ }
43+ }
44+}
45diff --git a/go.mod b/go.mod
46new file mode 100644
47index 0000000000000000000000000000000000000000..9f077233d21dfb39f807a297305fbcddb0b718d5
48--- /dev/null
49+++ b/go.mod
50@@ -0,0 +1,3 @@
51+module gitea.theedgeofrage.com/theedgeofrage/rpn
52+
53+go 1.21.5
54diff --git a/rpn/lexer.go b/rpn/lexer.go
55new file mode 100644
56index 0000000000000000000000000000000000000000..1a01c15ec9fb20071821a45958e6f622e43637c5
57--- /dev/null
58+++ b/rpn/lexer.go
59@@ -0,0 +1,143 @@
60+package rpn
61+
62+import (
63+ "fmt"
64+ "math"
65+ "strconv"
66+ "unicode"
67+)
68+
69+type Lexer struct {
70+ input *Input
71+}
72+
73+func (l *Lexer) parseNumber() (float64, error) {
74+ numStr := ""
75+ char := l.input.NextChar()
76+ for {
77+ if !unicode.IsNumber(char) && char != '.' {
78+ break
79+ }
80+
81+ numStr += string(char)
82+ char = l.input.Eat()
83+ }
84+
85+ num, err := strconv.ParseFloat(numStr, 64)
86+ if err != nil {
87+ return 0, err
88+ }
89+
90+ return num, nil
91+}
92+
93+func (l *Lexer) parseWord() (*Token, error) {
94+ word := ""
95+ char := l.input.NextChar()
96+ for {
97+ if !unicode.IsLetter(char) {
98+ break
99+ }
100+
101+ word += string(char)
102+ char = l.input.Eat()
103+ }
104+
105+ switch word {
106+ case "sqrt":
107+ return &Token{unaryOp, sqrt, 0}, nil
108+ case "dec":
109+ return &Token{dec, 0, 0}, nil
110+ case "bin":
111+ return &Token{bin, 0, 0}, nil
112+ case "hex":
113+ return &Token{hex, 0, 0}, nil
114+ case "pi":
115+ return &Token{number, 0, math.Pi}, nil
116+ case "pop":
117+ return &Token{pop, 0, 0}, nil
118+ case "swap":
119+ return &Token{swap, 0, 0}, nil
120+ case "clr":
121+ return &Token{clr, 0, 0}, nil
122+ case "help":
123+ return &Token{help, 0, 0}, nil
124+ case "exit":
125+ return &Token{exit, 0, 0}, nil
126+ default:
127+ return nil, fmt.Errorf("Unknown input: %s", word)
128+ }
129+}
130+
131+func (l *Lexer) Parse(input string) ([]*Token, error) {
132+ var err error
133+ var num float64
134+ var token *Token
135+ l.input = NewInput(input)
136+ tokens := []*Token{}
137+ for {
138+ char := l.input.NextChar()
139+ if char == 0 {
140+ break
141+ }
142+
143+ switch char {
144+ case '+':
145+ l.input.Eat()
146+ token = &Token{binaryOp, plus, 0}
147+ case '-':
148+ char = l.input.Eat()
149+ if unicode.IsNumber(char) {
150+ num, err = l.parseNumber()
151+ token = &Token{number, 0, -num}
152+ if err != nil {
153+ return nil, err
154+ }
155+ } else {
156+ token = &Token{binaryOp, minus, 0}
157+ }
158+ case '*':
159diff --git a/rpn/parser.go b/rpn/parser.go
160new file mode 100644
161index 0000000000000000000000000000000000000000..b7c02f12533ab0de5db4c93b9c499db7708ff272
162--- /dev/null
163+++ b/rpn/parser.go
164@@ -0,0 +1,39 @@
165+package rpn
166+
167+type Input struct {
168+ characters []rune
169+ nextChar rune
170+}
171+
172+func NewInput(input string) *Input {
173+ characters := []rune(input)
174+ if len(characters) == 0 {
175+ return nil
176+ }
177+
178+ i := &Input{
179+ characters: characters,
180+ nextChar: characters[0],
181+ }
182+ return i
183+}
184+
185+// Eat consumes a character from the input and returns the next one.
186+func (i *Input) Eat() rune {
187+ if len(i.characters) == 0 {
188+ return 0
189+ }
190+
191+ i.characters = i.characters[1:]
192+ if len(i.characters) == 0 {
193+ i.nextChar = 0
194+ } else {
195+ i.nextChar = i.characters[0]
196+ }
197+
198+ return i.nextChar
199+}
200+
201+func (i *Input) NextChar() rune {
202+ return i.nextChar
203+}
204diff --git a/rpn/rpn.go b/rpn/rpn.go
205new file mode 100644
206index 0000000000000000000000000000000000000000..558c8d5f6db0d706fb8df65062410019b1a0e004
207--- /dev/null
208+++ b/rpn/rpn.go
209@@ -0,0 +1,108 @@
210+package rpn
211+
212+import (
213+ "errors"
214+ "math"
215+)
216+
217+var ErrExit = errors.New("exit")
218+
219+type RPN struct {
220+ stack *Stack
221+ lexer *Lexer
222+}
223+
224+func NewRPN() *RPN {
225+ return &RPN{
226+ stack: &Stack{},
227+ lexer: &Lexer{},
228+ }
229+}
230+
231+func printHelp() {
232+ println("RPN Calculator")
233+ println("==============")
234+ println("Commands:")
235+ println(" +, -, *, /, %, ^, sqrt")
236+ println(" pop, swap")
237+ println(" help")
238+ println(" exit (or Ctrl-D)")
239+ println("Examples:")
240+ println(" 1 2 + == 3")
241+ println(" 2 3 4 + *")
242+ println(" 2 3 4 + * 5 /")
243+ println(" 9 sqrt")
244+ println(" 2 3 swap -")
245+ println(" exit")
246+}
247+
248+func (r *RPN) Eval(input string) error {
249+ tokens, err := r.lexer.Parse(input)
250+ if err != nil {
251+ return err
252+ }
253+
254+ for _, token := range tokens {
255+ switch token.Type {
256+ case number:
257+ r.stack.Push(token.Value)
258+ case binaryOp:
259+ a, b, err := r.stack.Pop2()
260+ if err != nil {
261+ return err
262+ }
263+ switch token.Operator {
264+ case plus:
265+ r.stack.Push(b + a)
266+ case minus:
267+ r.stack.Push(b - a)
268+ case multiply:
269+ r.stack.Push(b * a)
270+ case divide:
271+ if a == 0 {
272+ r.stack.Push(b)
273+ r.stack.Push(a)
274+ return errors.New("can't divide by zero")
275+ }
276+ r.stack.Push(b / a)
277+ case mod:
278+ r.stack.Push(float64(int64(b) % int64(a)))
279+ case power:
280+ r.stack.Push(math.Pow(b, a))
281+ }
282+ case unaryOp:
283+ a, err := r.stack.Pop()
284+ if err != nil {
285+ return err
286+ }
287+ switch token.Operator {
288+ case sqrt:
289+ r.stack.Push(math.Sqrt(a))
290+ }
291+ case pop:
292+ _, err := r.stack.Pop()
293+ if err != nil {
294+ return err
295+ }
296+ case swap:
297+ err := r.stack.Swap()
298+ if err != nil {
299+ return err
300+ }
301+ case clr:
302+ r.stack.Clear()
303+ case help:
304+ printHelp()
305+ case exit:
306+ return ErrExit
307+ default:
308+ return errors.New("Unknown operation")
309diff --git a/rpn/stack.go b/rpn/stack.go
310new file mode 100644
311index 0000000000000000000000000000000000000000..318334905bf475a5590168b26de4f2ca8faffe62
312--- /dev/null
313+++ b/rpn/stack.go
314@@ -0,0 +1,71 @@
315+package rpn
316+
317+import (
318+ "errors"
319+ "fmt"
320+ "math"
321+)
322+
323+var (
324+ ErrStackEmpty = errors.New("stack is empty")
325+ ErrNotEnoughValues = errors.New("not enough values on stack")
326+)
327+
328+type Stack struct {
329+ values []float64
330+}
331+
332+func (s *Stack) Push(value float64) {
333+ s.values = append(s.values, value)
334+}
335+
336+func (s *Stack) Pop() (float64, error) {
337+ count := s.Len()
338+ if count == 0 {
339+ return 0, ErrStackEmpty
340+ }
341+ value := s.values[count-1]
342+ s.values = s.values[:count-1]
343+ return value, nil
344+}
345+
346+func (s *Stack) Pop2() (float64, float64, error) {
347+ count := s.Len()
348+ if count < 2 {
349+ return 0, 0, ErrNotEnoughValues
350+ }
351+ a := s.values[count-1]
352+ b := s.values[count-2]
353+ s.values = s.values[:count-2]
354+ return a, b, nil
355+}
356+
357+func (s *Stack) Len() int {
358+ return len(s.values)
359+}
360+
361+func (s *Stack) Swap() error {
362+ count := s.Len()
363+ if count < 2 {
364+ return ErrNotEnoughValues
365+ }
366+ a := s.values[count-1]
367+ b := s.values[count-2]
368+ s.values[count-1] = b
369+ s.values[count-2] = a
370+ return nil
371+}
372+
373+func (s *Stack) Clear() {
374+ s.values = []float64{}
375+}
376+
377+func (s *Stack) Print() {
378+ for _, value := range s.values {
379+ if value == math.Trunc(value) {
380+ fmt.Printf("%d\n", int64(value))
381+ } else {
382+ fmt.Printf("%f\n", value)
383+ }
384+ }
385+}
386diff --git a/rpn/token.go b/rpn/token.go
387new file mode 100644
388index 0000000000000000000000000000000000000000..1d0623ca4115f20a14a6997c3440eef3c198ad93
389--- /dev/null
390+++ b/rpn/token.go
391@@ -0,0 +1,35 @@
392+package rpn
393+
394+type TokenType int
395+
396+const (
397+ number TokenType = iota
398+ binaryOp
399+ unaryOp
400+ dec
401+ bin
402+ hex
403+ pop
404+ swap
405+ clr
406+ help
407+ exit
408+)
409+
410+type Operator int
411+
412+const (
413+ plus Operator = iota
414+ minus
415+ multiply
416+ divide
417+ mod
418+ power
419+ sqrt
420+)
421+
422+type Token struct {
423+ Type TokenType
424+ Operator Operator
425+ Value float64
426+}