1package common
2
3import (
4 "strconv"
5 "strings"
6 "text/scanner"
7
8 "github.com/vektah/gqlgen/neelance/errors"
9)
10
11type Literal interface {
12 Value(vars map[string]interface{}) interface{}
13 String() string
14 Location() errors.Location
15}
16
17type BasicLit struct {
18 Type rune
19 Text string
20 Loc errors.Location
21}
22
23func (lit *BasicLit) Value(vars map[string]interface{}) interface{} {
24 switch lit.Type {
25 case scanner.Int:
26 value, err := strconv.ParseInt(lit.Text, 10, 64)
27 if err != nil {
28 panic(err)
29 }
30 return int(value)
31
32 case scanner.Float:
33 value, err := strconv.ParseFloat(lit.Text, 64)
34 if err != nil {
35 panic(err)
36 }
37 return value
38
39 case scanner.String:
40 value, err := strconv.Unquote(lit.Text)
41 if err != nil {
42 panic(err)
43 }
44 return value
45
46 case scanner.Ident:
47 switch lit.Text {
48 case "true":
49 return true
50 case "false":
51 return false
52 default:
53 return lit.Text
54 }
55
56 default:
57 panic("invalid literal")
58 }
59}
60
61func (lit *BasicLit) String() string {
62 return lit.Text
63}
64
65func (lit *BasicLit) Location() errors.Location {
66 return lit.Loc
67}
68
69type ListLit struct {
70 Entries []Literal
71 Loc errors.Location
72}
73
74func (lit *ListLit) Value(vars map[string]interface{}) interface{} {
75 entries := make([]interface{}, len(lit.Entries))
76 for i, entry := range lit.Entries {
77 entries[i] = entry.Value(vars)
78 }
79 return entries
80}
81
82func (lit *ListLit) String() string {
83 entries := make([]string, len(lit.Entries))
84 for i, entry := range lit.Entries {
85 entries[i] = entry.String()
86 }
87 return "[" + strings.Join(entries, ", ") + "]"
88}
89
90func (lit *ListLit) Location() errors.Location {
91 return lit.Loc
92}
93
94type ObjectLit struct {
95 Fields []*ObjectLitField
96 Loc errors.Location
97}
98
99type ObjectLitField struct {
100 Name Ident
101 Value Literal
102}
103
104func (lit *ObjectLit) Value(vars map[string]interface{}) interface{} {
105 fields := make(map[string]interface{}, len(lit.Fields))
106 for _, f := range lit.Fields {
107 fields[f.Name.Name] = f.Value.Value(vars)
108 }
109 return fields
110}
111
112func (lit *ObjectLit) String() string {
113 entries := make([]string, 0, len(lit.Fields))
114 for _, f := range lit.Fields {
115 entries = append(entries, f.Name.Name+": "+f.Value.String())
116 }
117 return "{" + strings.Join(entries, ", ") + "}"
118}
119
120func (lit *ObjectLit) Location() errors.Location {
121 return lit.Loc
122}
123
124type NullLit struct {
125 Loc errors.Location
126}
127
128func (lit *NullLit) Value(vars map[string]interface{}) interface{} {
129 return nil
130}
131
132func (lit *NullLit) String() string {
133 return "null"
134}
135
136func (lit *NullLit) Location() errors.Location {
137 return lit.Loc
138}
139
140type Variable struct {
141 Name string
142 Loc errors.Location
143}
144
145func (v Variable) Value(vars map[string]interface{}) interface{} {
146 return vars[v.Name]
147}
148
149func (v Variable) String() string {
150 return "$" + v.Name
151}
152
153func (v *Variable) Location() errors.Location {
154 return v.Loc
155}
156
157func ParseLiteral(l *Lexer, constOnly bool) Literal {
158 loc := l.Location()
159 switch l.Peek() {
160 case '$':
161 if constOnly {
162 l.SyntaxError("variable not allowed")
163 panic("unreachable")
164 }
165 l.ConsumeToken('$')
166 return &Variable{l.ConsumeIdent(), loc}
167
168 case scanner.Int, scanner.Float, scanner.String, scanner.Ident:
169 lit := l.ConsumeLiteral()
170 if lit.Type == scanner.Ident && lit.Text == "null" {
171 return &NullLit{loc}
172 }
173 lit.Loc = loc
174 return lit
175 case '-':
176 l.ConsumeToken('-')
177 lit := l.ConsumeLiteral()
178 lit.Text = "-" + lit.Text
179 lit.Loc = loc
180 return lit
181 case '[':
182 l.ConsumeToken('[')
183 var list []Literal
184 for l.Peek() != ']' {
185 list = append(list, ParseLiteral(l, constOnly))
186 }
187 l.ConsumeToken(']')
188 return &ListLit{list, loc}
189
190 case '{':
191 l.ConsumeToken('{')
192 var fields []*ObjectLitField
193 for l.Peek() != '}' {
194 name := l.ConsumeIdentWithLoc()
195 l.ConsumeToken(':')
196 value := ParseLiteral(l, constOnly)
197 fields = append(fields, &ObjectLitField{name, value})
198 }
199 l.ConsumeToken('}')
200 return &ObjectLit{fields, loc}
201
202 default:
203 l.SyntaxError("invalid value")
204 panic("unreachable")
205 }
206}