diff --git a/gql/lexer/lexer.go b/gql/lexer/lexer.go index 4edaa5e22e625333d9b3ac9ea035db81bb00215b..4a4420e1d67458bff073cbdf0873cef07886a6a1 100644 --- a/gql/lexer/lexer.go +++ b/gql/lexer/lexer.go @@ -2,7 +2,6 @@ package gqlex import ( "fmt" - "strings" "unicode/utf8" "github.com/Sirupsen/logrus" @@ -14,15 +13,19 @@ 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 + 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 @@ -123,16 +126,15 @@ 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 -} +type checkRune func(r rune) bool -func (l *lexer) acceptRun(valid string) { - for strings.IndexRune(valid, l.next()) >= 0 { +func (l *lexer) acceptRun(c checkRune) { + for { + r := l.next() + if !c(r) { + break + } } + l.backup() } diff --git a/gql/lexer/lexer_test.go b/gql/lexer/lexer_test.go index ca843d5f4165a6319ca3f11988f02b84679b435b..a358a2af2cc15f65012c12ef9a07afada168e6a8 100644 --- a/gql/lexer/lexer_test.go +++ b/gql/lexer/lexer_test.go @@ -8,9 +8,10 @@ import ( func TestNewLexer(t *testing.T) { input := ` mutation { - me { + me( id: 10, xid: rick ) { name0 # my name _city, # 0what would fail lex. + profilePic(width: 100, height: 100) friends { name } diff --git a/gql/lexer/state.go b/gql/lexer/state.go index c02c0ffd20d6dd8e2867ae70c368a3178dc63cf9..163ec6ad957d72236997afb318ec215391f2cab0 100644 --- a/gql/lexer/state.go +++ b/gql/lexer/state.go @@ -55,11 +55,13 @@ func lexInside(l *lexer) stateFn { case isSpace(r) || isEndOfLine(r) || r == ',': l.ignore() case isNameBegin(r): - l.backup() return lexName case r == '#': l.backup() return lexComment + case r == '(': + l.emit(itemLeftRound) + return lexArgInside default: return l.errorf("Unrecognized character in lexInside: %#U", r) } @@ -68,7 +70,7 @@ func lexInside(l *lexer) stateFn { func lexName(l *lexer) stateFn { for { - // The caller must have already checked isNameBegin. + // The caller already checked isNameBegin, and absorbed one rune. r := l.next() if isNameSuffix(r) { continue @@ -114,6 +116,68 @@ func lexOperationType(l *lexer) stateFn { return lexText } +func lexArgInside(l *lexer) stateFn { + for { + switch r := l.next(); { + case r == EOF: + return l.errorf("unclosed argument") + case isSpace(r) || isEndOfLine(r): + l.ignore() + case isNameBegin(r): + return lexArgName + case r == ':': + l.ignore() + return lexArgVal + case r == ')': + l.emit(itemRightRound) + return lexInside + case r == ',': + l.ignore() + } + } +} + +func lexArgName(l *lexer) stateFn { + for { + r := l.next() + if isNameSuffix(r) { + continue + } + l.backup() + l.emit(itemArgName) + break + } + return lexArgInside +} + +func lexArgVal(l *lexer) stateFn { + l.acceptRun(isSpace) + l.ignore() // Any spaces encountered. + for { + r := l.next() + if isSpace(r) || isEndOfLine(r) || r == ')' || r == ',' { + l.backup() + l.emit(itemArgVal) + return lexArgInside + } + if r == EOF { + return l.errorf("Reached EOF while reading var value: %v", + l.input[l.start:l.pos]) + } + } + glog.Fatal("This shouldn't be reached.") + return nil +} + +func lexArgumentVal(l *lexer) stateFn { + for { + switch r := l.next(); { + case isSpace(r): + l.ignore() + } + } +} + func isSpace(r rune) bool { return r == '\u0009' || r == '\u0020' }