Skip to content

Commit

Permalink
big refactoring around literals
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed May 23, 2017
1 parent 3d63ae8 commit 94cb291
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 171 deletions.
12 changes: 1 addition & 11 deletions internal/common/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ type Ident struct {
Loc errors.Location
}

type Variable string

func New(sc *scanner.Scanner) *Lexer {
l := &Lexer{sc: sc}
l.Consume()
Expand Down Expand Up @@ -95,17 +93,9 @@ func (l *Lexer) ConsumeKeyword(keyword string) {
l.Consume()
}

func (l *Lexer) ConsumeVariable() Variable {
l.ConsumeToken('$')
return Variable(l.ConsumeIdent())
}

func (l *Lexer) ConsumeLiteral() interface{} {
func (l *Lexer) ConsumeLiteral() *BasicLit {
lit := &BasicLit{Type: l.next, Text: l.sc.TokenText()}
l.Consume()
if lit.Type == scanner.Ident && lit.Text == "null" {
return nil
}
return lit
}

Expand Down
153 changes: 151 additions & 2 deletions internal/common/literals.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
package common

import (
"sort"
"strconv"
"strings"
"text/scanner"

"github.com/neelance/graphql-go/errors"
)

type Literal interface {
Value() interface{}
Value(vars map[string]interface{}) interface{}
String() string
Location() errors.Location
}

type BasicLit struct {
Type rune
Text string
Loc errors.Location
}

func (lit *BasicLit) Value() interface{} {
func (lit *BasicLit) Value(vars map[string]interface{}) interface{} {
switch lit.Type {
case scanner.Int, scanner.Float:
value, err := strconv.ParseFloat(lit.Text, 64)
Expand Down Expand Up @@ -44,3 +51,145 @@ func (lit *BasicLit) Value() interface{} {
panic("invalid literal")
}
}

func (lit *BasicLit) String() string {
return lit.Text
}

func (lit *BasicLit) Location() errors.Location {
return lit.Loc
}

type ListLit struct {
Entries []Literal
Loc errors.Location
}

func (lit *ListLit) Value(vars map[string]interface{}) interface{} {
entries := make([]interface{}, len(lit.Entries))
for i, entry := range lit.Entries {
entries[i] = entry.Value(vars)
}
return entries
}

func (lit *ListLit) String() string {
entries := make([]string, len(lit.Entries))
for i, entry := range lit.Entries {
entries[i] = entry.String()
}
return "[" + strings.Join(entries, ", ") + "]"
}

func (lit *ListLit) Location() errors.Location {
return lit.Loc
}

type ObjectLit struct {
Fields map[string]Literal
Loc errors.Location
}

func (lit *ObjectLit) Value(vars map[string]interface{}) interface{} {
fields := make(map[string]interface{}, len(lit.Fields))
for k, v := range lit.Fields {
fields[k] = v.Value(vars)
}
return fields
}

func (lit *ObjectLit) String() string {
names := make([]string, 0, len(lit.Fields))
for name := range lit.Fields {
names = append(names, name)
}
sort.Strings(names)

entries := make([]string, 0, len(names))
for _, name := range names {
entries = append(entries, name+": "+lit.Fields[name].String())
}
return "{" + strings.Join(entries, ", ") + "}"
}

func (lit *ObjectLit) Location() errors.Location {
return lit.Loc
}

type NullLit struct {
Loc errors.Location
}

func (lit *NullLit) Value(vars map[string]interface{}) interface{} {
return nil
}

func (lit *NullLit) String() string {
return "null"
}

func (lit *NullLit) Location() errors.Location {
return lit.Loc
}

type Variable struct {
Name string
Loc errors.Location
}

func (v Variable) Value(vars map[string]interface{}) interface{} {
return vars[v.Name]
}

func (v Variable) String() string {
return "$" + v.Name
}

func (v *Variable) Location() errors.Location {
return v.Loc
}

func ParseLiteral(l *Lexer, constOnly bool) Literal {
loc := l.Location()
switch l.Peek() {
case '$':
if constOnly {
l.SyntaxError("variable not allowed")
panic("unreachable")
}
l.ConsumeToken('$')
return &Variable{l.ConsumeIdent(), loc}

case scanner.Int, scanner.Float, scanner.String, scanner.Ident:
lit := l.ConsumeLiteral()
if lit.Type == scanner.Ident && lit.Text == "null" {
return &NullLit{loc}
}
lit.Loc = loc
return lit

case '[':
l.ConsumeToken('[')
var list []Literal
for l.Peek() != ']' {
list = append(list, ParseLiteral(l, constOnly))
}
l.ConsumeToken(']')
return &ListLit{list, loc}

case '{':
l.ConsumeToken('{')
obj := make(map[string]Literal)
for l.Peek() != '}' {
name := l.ConsumeIdent()
l.ConsumeToken(':')
obj[name] = ParseLiteral(l, constOnly)
}
l.ConsumeToken('}')
return &ObjectLit{obj, loc}

default:
l.SyntaxError("invalid value")
panic("unreachable")
}
}
40 changes: 0 additions & 40 deletions internal/common/stringify.go

This file was deleted.

65 changes: 7 additions & 58 deletions internal/common/values.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package common

import (
"text/scanner"

"github.com/neelance/graphql-go/errors"
)

type InputValue struct {
Name Ident
Type Type
TypeLoc errors.Location
Default *ValueWithLoc
Default Literal
Desc string
}

Expand All @@ -25,11 +23,6 @@ func (l InputValueList) Get(name string) *InputValue {
return nil
}

type ValueWithLoc struct {
Value interface{}
Loc errors.Location
}

func ParseInputValue(l *Lexer) *InputValue {
p := &InputValue{}
p.Desc = l.DescComment()
Expand All @@ -39,29 +32,28 @@ func ParseInputValue(l *Lexer) *InputValue {
p.Type = ParseType(l)
if l.Peek() == '=' {
l.ConsumeToken('=')
v := ParseValue(l, true)
p.Default = &v
p.Default = ParseLiteral(l, true)
}
return p
}

type Argument struct {
Name Ident
Value ValueWithLoc
Value Literal
}

type ArgumentList []Argument

func (l ArgumentList) Get(name string) (ValueWithLoc, bool) {
func (l ArgumentList) Get(name string) (Literal, bool) {
for _, arg := range l {
if arg.Name.Name == name {
return arg.Value, true
}
}
return ValueWithLoc{}, false
return nil, false
}

func (l ArgumentList) MustGet(name string) ValueWithLoc {
func (l ArgumentList) MustGet(name string) Literal {
value, ok := l.Get(name)
if !ok {
panic("argument not found")
Expand All @@ -75,52 +67,9 @@ func ParseArguments(l *Lexer) ArgumentList {
for l.Peek() != ')' {
name := l.ConsumeIdentWithLoc()
l.ConsumeToken(':')
value := ParseValue(l, false)
value := ParseLiteral(l, false)
args = append(args, Argument{Name: name, Value: value})
}
l.ConsumeToken(')')
return args
}

func ParseValue(l *Lexer, constOnly bool) ValueWithLoc {
loc := l.Location()
value := parseValue(l, constOnly)
return ValueWithLoc{
Value: value,
Loc: loc,
}
}

func parseValue(l *Lexer, constOnly bool) interface{} {
switch l.Peek() {
case '$':
if constOnly {
l.SyntaxError("variable not allowed")
panic("unreachable")
}
return l.ConsumeVariable()
case scanner.Int, scanner.Float, scanner.String, scanner.Ident:
return l.ConsumeLiteral()
case '[':
l.ConsumeToken('[')
var list []interface{}
for l.Peek() != ']' {
list = append(list, parseValue(l, constOnly))
}
l.ConsumeToken(']')
return list
case '{':
l.ConsumeToken('{')
obj := make(map[string]interface{})
for l.Peek() != '}' {
name := l.ConsumeIdent()
l.ConsumeToken(':')
obj[name] = parseValue(l, constOnly)
}
l.ConsumeToken('}')
return obj
default:
l.SyntaxError("invalid value")
panic("unreachable")
}
}
Loading

0 comments on commit 94cb291

Please sign in to comment.