aboutsummaryrefslogtreecommitdiff
path: root/example/math_expression/rpn.go
blob: a4cacc016b5060d066f73c8eb1bb228206768e61 (plain) (blame)
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
package main

// Helper functions to convert infix notation to RPN and calculates expression result.

import (
	"fmt"
	"log"
	"strconv"

	"github.com/neonxp/unilex"
)

var opPriority = map[string]int{
	"^": 3,
	"!": 3,
	"*": 2,
	"/": 2,
	"+": 1,
	"-": 1,
}

func infixToRPNotation(l *unilex.Lexer) []unilex.Lexem {
	output := []unilex.Lexem{}
	stack := lexemStack{}
parseLoop:
	for ll := range l.Output { // Read lexems from Lexer output channel, convert starts as soon as first lexems scanned!
		fmt.Printf("Lexem: %+v\n", ll)

		switch {
		case ll.Type == NUMBER, ll.Type == OPERATOR && ll.Value == "!":
			output = append(output, ll)
		case ll.Type == LP:
			stack.Push(ll)
		case ll.Type == RP:
			for {
				cl := stack.Pop()
				if cl.Type == LP {
					break
				}
				if cl.Type == unilex.LexEOF {
					log.Fatalf("No pair for parenthesis at %d", ll.Start)
				}
				output = append(output, cl)
			}
		case ll.Type == OPERATOR:
			for {
				if stack.Head().Type == OPERATOR && (opPriority[stack.Head().Value] > opPriority[ll.Value]) {
					output = append(output, stack.Pop())
					continue
				}
				break
			}
			stack.Push(ll)
		case ll.Type == unilex.LexEOF:
			break parseLoop
		}
	}

	for stack.Head().Type != unilex.LexEOF {
		output = append(output, stack.Pop())
	}

	return output
}

func calculateRPN(rpnLexems []unilex.Lexem) string {
	stack := lexemStack{}
	for _, op := range rpnLexems {
		if op.Type == NUMBER {
			stack.Push(op)
		} else {
			switch op.Value {
			case "+":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: NUMBER, Value: strconv.FormatFloat(a2+a1, 'f', -1, 64)})
			case "-":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: NUMBER, Value: strconv.FormatFloat(a2-a1, 'f', -1, 64)})
			case "*":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: NUMBER, Value: strconv.FormatFloat(a2*a1, 'f', -1, 64)})
			case "/":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: NUMBER, Value: strconv.FormatFloat(a2/a1, 'f', -1, 64)})
			}
		}
	}
	return stack.Head().Value
}