Newer
Older
package gql
import (
"fmt"
"unicode/utf8"
"github.com/Sirupsen/logrus"
"github.com/manishrjain/dgraph/x"
var glog = x.Log("lexer")
type itemType int
const (
itemEOF itemType = iota
itemError // error
itemText // plain text
itemLeftCurl // left curly bracket
itemRightCurl // right curly bracket
itemComment // comment
itemName // names
itemOpType // operation type
itemString // quoted string
itemLeftRound // left round bracket
itemRightRound // right round bracket
itemArgName // argument name
itemArgVal // argument val
)
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 itemName:
return fmt.Sprintf("name: [%v]", i.val)
return fmt.Sprintf("[%v] %q", i.typ, 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) {
if t != itemEOF && l.pos <= l.start {
// Let itemEOF go through.
glog.WithFields(logrus.Fields{
"start": l.start,
"pos": l.pos,
"typ": t,
}).Info("Invalid emit")
return
}
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
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) acceptRun(c checkRune) {
for {
r := l.next()
if !c(r) {
break
}