Newer
Older
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
package gqlex
import (
"fmt"
"strings"
"unicode/utf8"
)
type itemType int
const (
itemError itemType = iota
itemEOF
itemLeftCurl // left curly bracket
itemRightCurl // right curly bracket
itemString // quoted string
itemText // plain text
itemIdentifier // variables
)
const EOF = -1
type item struct {
typ itemType
val string
}
func (i item) String() string {
switch i.typ {
case itemEOF:
return "EOF"
case itemError:
return i.val
case itemIdentifier:
return fmt.Sprintf("var: [%v]", i.val)
}
/*
if len(i.val) > 10 {
return fmt.Sprintf("%.10q...", i.val)
}
*/
return fmt.Sprintf("%q", i.val)
}
type lexer struct {
// NOTE: Using a text scanner wouldn't work because it's designed for parsing
// Golang. It won't keep track of start position, or allow us to retrieve
// slice from [start:pos]. Better to just use normal string.
input string // string being scanned.
start int // start position of this item.
pos int // current position of this item.
width int // width of last rune read from input.
items chan item // channel of scanned items.
depth int // nesting of {}
}
func newLexer(input string) *lexer {
l := &lexer{
input: input,
items: make(chan item),
}
go l.run()
return l
}
func (l *lexer) errorf(format string,
args ...interface{}) stateFn {
l.items <- item{
typ: itemError,
val: fmt.Sprintf(format, args...),
}
return nil
}
func (l *lexer) emit(t itemType) {
l.items <- item{
typ: t,
val: l.input[l.start:l.pos],
}
l.start = l.pos
}
func (l *lexer) run() {
for state := lexText; state != nil; {
state = state(l)
}
close(l.items) // No more tokens.
}
func (l *lexer) next() (result rune) {
if l.pos >= len(l.input) {
l.width = 0
return EOF
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = w
l.pos += l.width
return r
}
func (l *lexer) backup() {
l.pos -= l.width
}
func (l *lexer) peek() rune {
r := l.next()
l.backup()
return r
}
func (l *lexer) ignore() {
l.start = l.pos
}
func (l *lexer) accept(valid string) bool {
if strings.IndexRune(valid, l.next()) >= 0 {
return true
}
l.backup()
return false
}
func (l *lexer) acceptRun(valid string) {
for strings.IndexRune(valid, l.next()) >= 0 {
}
l.backup()
}