-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsymbol_scanner.go
160 lines (144 loc) · 3.3 KB
/
symbol_scanner.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
package gmars
import (
"fmt"
"strings"
)
// symbol scanner accepts a tokenReader and scans for any
// equ symbols contained. Symbols defined inside for loops
// are ignored, allowing us to run the same code both before
// and after for loops have been expanded.
type symbolScanner struct {
lex tokenReader
nextToken token
atEOF bool
valBuf []token
labelBuf []string
forSeen bool
err error
symbols map[string][]token
}
type scanStateFn func(p *symbolScanner) scanStateFn
func newSymbolScanner(lex tokenReader) *symbolScanner {
pre := &symbolScanner{
lex: lex,
symbols: make(map[string][]token),
}
pre.next()
return pre
}
func (p *symbolScanner) next() token {
if p.atEOF {
return token{typ: tokEOF}
}
tok, err := p.lex.NextToken()
if err != nil {
p.atEOF = true
return token{tokError, fmt.Sprintf("%s\n", err)}
}
if tok.typ == tokEOF || tok.typ == tokError {
p.atEOF = true
}
retTok := p.nextToken
p.nextToken = tok
return retTok
}
func ScanInput(lex tokenReader) (map[string][]token, bool, error) {
scanner := newSymbolScanner(lex)
return scanner.ScanInput()
}
func (p *symbolScanner) ScanInput() (map[string][]token, bool, error) {
for state := scanLine; state != nil; {
state = state(p)
}
if p.err != nil {
return nil, false, p.err
}
return p.symbols, p.forSeen, nil
}
// consume the current nextToken and go to nextState unless EOF
func (p *symbolScanner) consume(nextState scanStateFn) scanStateFn {
p.next()
if p.nextToken.typ == tokEOF {
return nil
}
return nextState
}
// run at start of each line
// on text: preLabels
// on other: preConsumeLine
func scanLine(p *symbolScanner) scanStateFn {
switch p.nextToken.typ {
case tokText:
p.labelBuf = make([]string, 0)
return scanLabels
default:
return scanConsumeLine
}
}
// text equ: consumeValue
// text op: consumLine
// text default: scanLabels
// anything else: consumeLine
func scanLabels(p *symbolScanner) scanStateFn {
switch p.nextToken.typ {
case tokText:
if p.nextToken.IsPseudoOp() {
opLower := strings.ToLower(p.nextToken.val)
switch opLower {
case "equ":
p.valBuf = make([]token, 0)
return p.consume(scanEquValue)
case "for":
p.forSeen = true
return nil
case "end":
return nil
default:
return scanConsumeLine
}
} else if p.nextToken.IsOp() {
return scanConsumeLine
} else if p.nextToken.typ == tokInvalid {
return nil
}
p.labelBuf = append(p.labelBuf, p.nextToken.val)
return p.consume(scanLabels)
case tokComment:
fallthrough
case tokNewline:
return p.consume(scanLabels)
case tokEOF:
return nil
default:
return scanConsumeLine
}
}
func scanConsumeLine(p *symbolScanner) scanStateFn {
switch p.nextToken.typ {
case tokNewline:
return p.consume(scanLine)
case tokError:
return nil
case tokEOF:
return nil
default:
return p.consume(scanConsumeLine)
}
}
func scanEquValue(p *symbolScanner) scanStateFn {
for p.nextToken.typ != tokNewline && p.nextToken.typ != tokEOF && p.nextToken.typ != tokError {
p.valBuf = append(p.valBuf, p.nextToken)
p.next()
}
for _, label := range p.labelBuf {
_, ok := p.symbols[label]
if ok {
p.err = fmt.Errorf("symbol '%s' redefined", label)
return nil
}
p.symbols[label] = p.valBuf
}
p.valBuf = make([]token, 0)
p.labelBuf = make([]string, 0)
return p.consume(scanLine)
}