aboutsummaryrefslogtreecommitdiff
path: root/infixrpn.go
blob: 18dde3983d428f82737f28d757511f2c593da4d4 (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
94
95
96
97
98
99
100
101
102
103
104
package expression

import (
	"fmt"
	"go/token"
)

func (e *Evaluator) ToPRN(in <-chan Token) chan Token {
	out := make(chan Token)
	stack := &Stack{}

	go func() {
		defer func() {
			for !stack.Empty() {
				tok := stack.Pop()
				if tok.LP() {
					out <- Token{
						Token:   token.ILLEGAL,
						Literal: "no closing parenthesis",
						Pos:     tok.Pos,
					}
				} else {
					out <- tok
				}
			}
			close(out)
		}()
		for tok := range in {
			switch {
			case tok.Token == token.ILLEGAL:
				return
			case tok.IsNumber():
				out <- tok
			case tok.IsFunc():
				stack.Push(tok)
			case tok.IsSeparator():
				for {
					if stack.Empty() {
						out <- Token{
							Token:   token.ILLEGAL,
							Literal: "no opening parenthesis",
							Pos:     tok.Pos,
						}
						return
					}
					if stack.Head().LP() {
						break
					}
					out <- tok
				}
			case tok.IsOperator():
				op1 := e.operators[tok.Token]
				for {
					if stack.Empty() {
						break
					}
					if stack.Head().IsOperator() {
						op2, hasOp := e.operators[stack.Head().Token]
						if !hasOp {
							out <- Token{
								Token:   token.ILLEGAL,
								Literal: fmt.Sprintf("unknown operator: %s", stack.Head().Literal),
								Pos:     tok.Pos,
							}
							return
						}
						if op2.priority > op1.priority {
							out <- stack.Pop()
							continue
						} else {
							break
						}
					} else {
						break
					}
				}
				stack.Push(tok)
			case tok.LP():
				stack.Push(tok)
			case tok.RP():
				for {
					if stack.Empty() {
						out <- Token{
							Token:   token.ILLEGAL,
							Literal: "no opening parenthesis",
							Pos:     tok.Pos,
						}
						return
					}
					if stack.Head().LP() {
						break
					}
					out <- tok
				}
				stack.Pop()
				if stack.Head().IsFunc() {
					out <- stack.Pop()
				}
			}
		}
	}()

	return out
}