Source file
src/tawesoft.co.uk/go/glcaps/parse.go
Documentation:
src/tawesoft.co.uk/go/glcaps/parse.go
1 package glcaps
2
3 import (
4 "fmt"
5 "reflect"
6 "strings"
7
8 "tawesoft.co.uk/go/operator"
9 )
10
11
12
13 func parseAtom(tag string, offset int) (atom string, next int) {
14
15
16 var i = offset
17 for (i < len(tag)) && (tag[i] == ' ') { i++ }
18
19
20 var j = i
21 for (j < len(tag)) && (tag[j] != ' ') { j++ }
22
23 if j - i == 0 {
24 return "", -1
25 }
26
27 return tag[i:j], j
28 }
29
30
31 func parseCommand2(tag string, offset int) (c1 command, c2 command, next int, _err error) {
32 var ac, ao, ae = parseCommand(tag, offset)
33 if ae != nil { return c1, c2, 0, ae }
34
35 var bc, bo, be = parseCommand(tag, ao)
36 if be != nil { return c1, c2, 0, be }
37
38 return ac, bc, bo, nil
39 }
40
41
42
43 func parseBinaryBooleanCommand(tag string, offset int, f func(bool, bool) bool) (c command, next int, _err error) {
44 var c1, c2, o, e = parseCommand2(tag, offset)
45 if e != nil { return c, 0, e }
46 return commandBinaryBoolean{c1, c2, f}, o, nil
47 }
48
49
50
51 func parseCompareCommand(
52 tag string,
53 offset int,
54 fi func(int, int) bool,
55 ff func(float32, float32) bool,
56 fs func(string, string) bool,
57 ) (c command, next int, _err error) {
58 var c1, c2, o, e = parseCommand2(tag, offset)
59 if e != nil { return c, 0, e }
60 return commandCompare{c1, c2, fi, ff, fs}, o, nil
61 }
62
63
64
65 func parseCommand(tag string, _offset int) (c command, next int, err error) {
66 var start, offset = parseAtom(tag, _offset)
67 if offset < 0 { return c, 0, fmt.Errorf("expected command") }
68
69 switch start {
70 case "and": return parseBinaryBooleanCommand(tag, offset, operator.Bool.Binary.And)
71 case "or": return parseBinaryBooleanCommand(tag, offset, operator.Bool.Binary.Or)
72
73 case "eq": return parseCompareCommand(tag, offset, operator.Int.Binary.Eq, operator.Float32.Binary.Eq, operationStringEq)
74 case "neq": return parseCompareCommand(tag, offset, operator.Int.Binary.Eq, operator.Float32.Binary.Neq, operationStringNeq)
75 case "lt": return parseCompareCommand(tag, offset, operator.Int.Binary.Lt, operator.Float32.Binary.Lt, nil)
76 case "lte": return parseCompareCommand(tag, offset, operator.Int.Binary.Lte, operator.Float32.Binary.Lte, nil)
77 case "gt": return parseCompareCommand(tag, offset, operator.Int.Binary.Gt, operator.Float32.Binary.Gt, nil)
78 case "gte": return parseCompareCommand(tag, offset, operator.Int.Binary.Gte, operator.Float32.Binary.Gte, nil)
79
80 case "if":
81 var ac, ao, ae = parseCommand(tag, offset)
82 if ae != nil { return c, 0, ae }
83
84 var bc, bo, be = parseCommand(tag, ao)
85 if be != nil { return c, 0, be }
86
87 var cc, co, ce = parseCommand(tag, bo)
88 if ce != nil { return c, 0, ce }
89
90 return commandIf{ac, bc, cc}, co, nil
91
92 case "not":
93 var c1, o, e = parseCommand(tag, offset)
94 if e != nil { return c, 0, e }
95 return commandNot{c1}, o, nil
96
97 case "ext":
98 var c1, o = parseAtom(tag, offset)
99 if o < 0 { return c, 0, fmt.Errorf("expected name after ext") }
100 return commandExt{c1}, o, nil
101
102 case "GetString":
103 var c1, o = parseAtom(tag, offset)
104 if o < 0 { return c, 0, fmt.Errorf("expected name after GetString") }
105 return commandGetString{c1}, o, nil
106
107 case "GetIntegerv":
108 var c1, o = parseAtom(tag, offset)
109 if o < 0 { return c, 0, fmt.Errorf("expected name after GetIntegerv") }
110 return commandGetIntegerv{c1}, o, nil
111
112 case "GetFloatv":
113 var c1, o = parseAtom(tag, offset)
114 if o < 0 { return c, 0, fmt.Errorf("expected name after GetFloatv") }
115 return commandGetFloatv{c1}, o, nil
116
117 default:
118 return commandValue{start}, offset, nil
119 }
120 }
121
122
123 func parseParts(s string) (string, string) {
124 var offset = strings.IndexByte(s, ';')
125 if offset < 0 {
126 return s[0: len(s)], s[0:0]
127 } else {
128 return s[0: offset], s[offset + 1:]
129 }
130 }
131
132 func parseCompareRequirement(
133 tag string,
134 offset int,
135 symbol string,
136 fi func(int, int) bool,
137 ff func(float32, float32) bool,
138 fs func(string, string) bool,
139 ) (r requirement, next int, _err error) {
140 var r1, o = parseAtom(tag, offset)
141 if o < 0 { return r, 0, fmt.Errorf("expected constant after comparison") }
142 return requirementComparison{
143 constant: r1,
144 symbol: symbol,
145 operationi: fi,
146 operationf: ff,
147 operations: fs,
148 }, o, nil
149 }
150
151
152
153 func parseRequirement(tag string, _offset int) (r requirement, next int, err error) {
154 var start, offset = parseAtom(tag, _offset)
155 if offset < 0 { return r, -1, nil }
156
157 switch start {
158 case "required":
159 return requirementRequired{}, offset, nil
160
161 case "eq": return parseCompareRequirement(tag, offset, "=", operator.Int.Binary.Eq, operator.Float32.Binary.Eq, operationStringEq)
162 case "neq": return parseCompareRequirement(tag, offset, "!=", operator.Int.Binary.Neq, operator.Float32.Binary.Neq, operationStringNeq)
163 case "lt": return parseCompareRequirement(tag, offset, "<", operator.Int.Binary.Lt, operator.Float32.Binary.Lt, nil)
164 case "lte": return parseCompareRequirement(tag, offset, "<=", operator.Int.Binary.Lte, operator.Float32.Binary.Lte, nil)
165 case "gt": return parseCompareRequirement(tag, offset, ">", operator.Int.Binary.Gt, operator.Float32.Binary.Gt, nil)
166 case "gte": return parseCompareRequirement(tag, offset, ">=", operator.Int.Binary.Gte, operator.Float32.Binary.Gte, nil)
167
168 default:
169 return r, 0, fmt.Errorf("unknown requirement: '%s'", start)
170 }
171 }
172
173 func parseRequirements(t string) (requirements []requirement, err error) {
174 var offset int
175 var r requirement
176 requirements = make([]requirement, 0)
177
178 for {
179 r, offset, err = parseRequirement(t, offset)
180 if err != nil { return requirements, err }
181 if offset < 0 { return requirements, nil }
182
183 requirements = append(requirements, r)
184 }
185 }
186
187
188 func parseTag(t string) (tag, error) {
189 var left, right = parseParts(t)
190
191 var command, index, err = parseCommand(left, 0)
192 if err != nil { return tag{}, err }
193
194 if index < len(left) && strings.TrimSpace(left[index + 1:]) != "" {
195 return tag{}, fmt.Errorf("unexpected trailing string after end of command: '%s'", left[index:])
196 }
197
198 var requirements, rerr = parseRequirements(right)
199 if rerr != nil { return tag{}, rerr }
200
201 return tag{
202 command: command,
203 requirements: requirements,
204 }, nil
205 }
206
207 func checkBoolRequirements(field reflect.StructField, result bool, rs []requirement) (errors Errors) {
208 for _, r := range rs {
209 var err = r.evalBool(field.Name, result)
210 if err == nil { continue }
211
212 errors.append(Error{
213 Field: field.Name,
214 Tag: field.Tag.Get("glcaps"),
215 Requirement: r,
216 Message: err.Error(),
217 })
218 }
219
220 return errors
221 }
222
223 func checkIntRequirements(field reflect.StructField, result int, rs []requirement) (errors Errors) {
224 for _, r := range rs {
225 var err = r.evalInt(field.Name, result)
226 if err == nil { continue }
227
228 errors.append(Error{
229 Field: field.Name,
230 Tag: field.Tag.Get("glcaps"),
231 Requirement: r,
232 Message: err.Error(),
233 })
234 }
235
236 return errors
237 }
238
239 func checkFloatRequirements(field reflect.StructField, result float32, rs []requirement) (errors Errors) {
240 for _, r := range rs {
241 var err = r.evalFloat(field.Name, result)
242 if err == nil { continue }
243
244 errors.append(Error{
245 Field: field.Name,
246 Tag: field.Tag.Get("glcaps"),
247 Requirement: r,
248 Message: err.Error(),
249 })
250 }
251
252 return errors
253 }
254
255 func checkStringRequirements(field reflect.StructField, result string, rs []requirement) (errors Errors) {
256 for _, r := range rs {
257 var err = r.evalString(field.Name, result)
258 if err == nil { continue }
259
260 errors.append(Error{
261 Field: field.Name,
262 Tag: field.Tag.Get("glcaps"),
263 Requirement: r,
264 Message: err.Error(),
265 })
266 }
267
268 return errors
269 }
270
271 func parseStructField(binding *Binding, extensions []string, field reflect.StructField, setter reflect.Value, value interface{}) (errors Errors) {
272
273 var parse = func(field reflect.StructField) (_tag tag, ok bool) {
274
275 var glcapstag, exists = field.Tag.Lookup("glcaps")
276 if !exists { return tag{}, false }
277
278 var t, err = parseTag(glcapstag)
279 if err != nil {
280 errors.append(Error{
281 Field: field.Name,
282 Tag: glcapstag,
283 Message: fmt.Sprintf("tag parse error: %v", err),
284 })
285 return t, false
286 }
287
288 return t, true
289 }
290
291 var kind = field.Type.Kind()
292
293 if kind == reflect.Struct {
294 errors.append(parseStruct(binding, extensions, setter)...)
295 } else {
296 var t, ok = parse(field)
297 if ok {
298 switch kind {
299 case reflect.Bool:
300 var result = t.command.evalBool(binding, extensions)
301 errors.append(checkBoolRequirements(field, result, t.requirements)...)
302 setter.SetBool(result)
303
304 case reflect.Int:
305 var result = t.command.evalInt(binding, extensions)
306 errors.append(checkIntRequirements(field, result, t.requirements)...)
307 setter.SetInt(int64(result))
308
309 case reflect.Float32: fallthrough
310 case reflect.Float64:
311 var result = t.command.evalFloat(binding, extensions)
312 errors.append(checkFloatRequirements(field, result, t.requirements)...)
313 setter.SetFloat(float64(result))
314
315 case reflect.String:
316 var result = t.command.evalString(binding, extensions)
317 errors.append(checkStringRequirements(field, result, t.requirements)...)
318 setter.SetString(result)
319 }
320 }
321 }
322
323 return errors
324 }
325
326 func parseStruct(binding *Binding, extensions []string, s reflect.Value) (errors Errors) {
327
328 if s.Kind() != reflect.Struct {
329 panic("target must be a struct or pointer to struct")
330 }
331
332 for i := 0; i < s.NumField(); i++ {
333 errors.append(parseStructField(binding, extensions, s.Type().Field(i), s.Field(i), s.Field(i).Interface())...)
334 }
335
336 return errors
337 }
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 func Parse(binding *Binding, target interface{}) (extensions Extensions, errors Errors) {
364 extensions = binding.QueryExtensions()
365 return extensions, parseStruct(binding, extensions, reflect.ValueOf(target).Elem())
366 }
367
368
View as plain text