-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlexer.go
130 lines (114 loc) · 2.87 KB
/
lexer.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
package yymmdd
import (
"strconv"
"strings"
"unicode/utf8"
)
// LexToken holds is a (type, value) array.
type LexToken [3]string
// EOF character
var EOF string = "+++EOF+++"
// lexerState represents the state of the scanner
// as a function that returns the next state.
type lexerState func(*lexer) lexerState
// run lexes the input by executing state functions until
// the state is nil.
func (l *lexer) Run() {
for state := l.initialState; state != nil; {
state = state(l)
}
}
// Lexer creates a new scanner for the input string.
func Lexer(input string) (*lexer, []LexToken) {
l := &lexer{
input: input,
tokens: make([]LexToken, 0),
lineno: 1,
}
l.initialState = initLexerState
l.Run()
return l, l.tokens
}
// lexer holds the state of the scanner.
type lexer struct {
input string // the string being scanned.
start int // start position of this item.
pos int // current position in the input.
width int // width of last rune read from input.
tokens []LexToken // scanned items.
initialState lexerState
lineno int
}
// next returns the next rune in the input.
func (l *lexer) next() string {
var r rune
if l.pos >= len(l.input) {
l.width = 0
return EOF
}
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
l.pos += l.width
return string(r)
}
// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
l.start = l.pos
}
// backup steps back one rune.
// Can be called only once per call of next.
func (l *lexer) backup() {
l.pos -= l.width
}
// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
for strings.Index(valid, l.next()) >= 0 {
}
l.backup()
}
// emit passes an item back to the client.
func (l *lexer) emit(t string) {
l.tokens = append(l.tokens, LexToken{t, l.input[l.start:l.pos], strconv.Itoa(l.lineno)})
l.start = l.pos
}
// emit passes an item back to the client.
func (l *lexer) emitRaw() {
if l.pos-l.start > 1 {
l.tokens = append(l.tokens, LexToken{T_RAW_MARK, l.input[l.start : l.pos-1], strconv.Itoa(l.lineno)})
l.start = l.pos - 1
}
}
// initialState is the starting point for the
// scanner. It scans through each character and decides
// which state to create for the lexer. lexerState == nil
// is exit scanner.
func initLexerState(l *lexer) lexerState {
for r := l.next(); r != EOF; r = l.next() {
if r == "y" {
l.emitRaw()
l.acceptRun("y")
l.emit(T_YEAR_MARK)
} else if r == "M" {
l.emitRaw()
l.acceptRun("M")
l.emit(T_MONTH_MARK)
} else if r == "d" {
l.emitRaw()
l.acceptRun("d")
l.emit(T_DAY_MARK)
} else if r == "h" {
l.emitRaw()
l.acceptRun("h")
l.emit(T_DAY_MARK)
} else if r == "m" {
l.emitRaw()
l.acceptRun("m")
l.emit(T_DAY_MARK)
} else if r == "s" {
l.emitRaw()
l.acceptRun("s")
l.emit(T_DAY_MARK)
}
}
l.emit(T_EOF)
return nil
}