| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package parse |
| |
| import ( |
| "fmt" |
| "reflect" |
| "strconv" |
| "testing" |
| ) |
| |
| type listNode []interface{} |
| |
| type arrayNode listNode |
| |
| type callNode struct { |
| name *valueNode |
| args listNode |
| } |
| |
| type numberNode uint64 |
| type valueNode string |
| |
| const ( |
| tagComma = "," |
| tagBeginArray = "[" |
| tagEndArray = "]" |
| tagBeginCall = "(" |
| tagEndCall = ")" |
| ) |
| |
| var ( |
| comma = opParser(tagComma) |
| beginArray = opParser(tagBeginArray) |
| endArray = opParser(tagEndArray) |
| beginCall = opParser(tagBeginCall) |
| endCall = opParser(tagEndCall) |
| ) |
| |
| func opParser(op string) LeafParser { |
| return func(p *Parser, _ *Leaf) { |
| if !p.String(op) { |
| p.Expected(op) |
| } |
| } |
| } |
| |
| func maybeValue(p *Parser, in *Branch) interface{} { |
| switch { |
| case peek(&p.Reader, tagBeginArray): |
| a := &arrayNode{} |
| p.ParseBranch(in, a.parse) |
| return a |
| case p.Numeric() != NotNumeric: |
| var n numberNode |
| p.ParseLeaf(in, n.consume) |
| return &n |
| case p.AlphaNumeric(): |
| var n valueNode |
| p.ParseLeaf(in, n.consume) |
| if peek(&p.Reader, tagBeginCall) { |
| c := &callNode{name: &n} |
| p.ParseBranch(in, c.parse) |
| return c |
| } else { |
| return &n |
| } |
| } |
| return nil |
| } |
| |
| func parseValue(p *Parser, in *Branch) interface{} { |
| v := maybeValue(p, in) |
| if v == nil { |
| p.Expected("value") |
| } |
| return v |
| } |
| |
| func (n *valueNode) parse(p *Parser, l *Leaf) { |
| if !p.AlphaNumeric() { |
| p.Expected("value") |
| } |
| n.consume(p, l) |
| } |
| |
| func (n *valueNode) consume(p *Parser, l *Leaf) { |
| l.SetToken(p.Consume()) |
| *n = valueNode(l.Token().String()) |
| } |
| |
| func (n *numberNode) consume(p *Parser, l *Leaf) { |
| l.SetToken(p.Consume()) |
| v, _ := strconv.ParseUint(l.Token().String(), 0, 32) |
| *n = numberNode(v) |
| } |
| |
| func (n *listNode) parse(p *Parser, cst *Branch) { |
| v := maybeValue(p, cst) |
| if v == nil { |
| return |
| } |
| *n = append(*n, v) |
| for p.String(tagComma) { |
| p.ParseLeaf(cst, nil) |
| *n = append(*n, parseValue(p, cst)) |
| } |
| } |
| |
| func (n *arrayNode) parse(p *Parser, cst *Branch) { |
| p.ParseLeaf(cst, beginArray) |
| p.ParseBranch(cst, (*listNode)(n).parse) |
| p.ParseLeaf(cst, endArray) |
| } |
| |
| func (n *callNode) parse(p *Parser, cst *Branch) { |
| p.ParseLeaf(cst, beginCall) |
| p.ParseBranch(cst, n.args.parse) |
| p.ParseLeaf(cst, endCall) |
| } |
| |
| func compareAST(t *testing.T, expect, got *listNode) { |
| compareList(t, "root", expect, got) |
| } |
| |
| func compareValue(t *testing.T, in string, expect, got *valueNode) { |
| if *expect != *got { |
| t.Fatalf("expected %q got %q in %s", *expect, *got, in) |
| } |
| } |
| |
| func compareNumber(t *testing.T, in string, expect, got *numberNode) { |
| if *expect != *got { |
| t.Fatalf("expected %d got %d in %s", *expect, *got, in) |
| } |
| } |
| |
| func compareList(t *testing.T, in string, expect, got *listNode) { |
| if len(*expect) != len(*got) { |
| t.Fatalf("expected %v entries got %v in %s", len(*expect), len(*got), in) |
| } |
| for i, e := range *expect { |
| in := fmt.Sprintf("%s#%v", in, i) |
| g := (*got)[i] |
| if reflect.TypeOf(e) != reflect.TypeOf(g) { |
| t.Fatalf("expected type %T got %T in %s", e, g, in) |
| } |
| switch v := e.(type) { |
| case *callNode: |
| compareCall(t, in, v, g.(*callNode)) |
| case *arrayNode: |
| compareArray(t, in, v, g.(*arrayNode)) |
| case *listNode: |
| compareList(t, in, v, g.(*listNode)) |
| case *valueNode: |
| compareValue(t, in, v, g.(*valueNode)) |
| case *numberNode: |
| compareNumber(t, in, v, g.(*numberNode)) |
| default: |
| t.Fatalf("Unknown types %T", e) |
| } |
| } |
| } |
| |
| func compareArray(t *testing.T, in string, expect, got *arrayNode) { |
| compareList(t, fmt.Sprintf("%s#array", in), (*listNode)(expect), (*listNode)(got)) |
| } |
| |
| func compareCall(t *testing.T, in string, expect, got *callNode) { |
| compareValue(t, fmt.Sprintf("%s#call", in), expect.name, got.name) |
| compareList(t, fmt.Sprintf("%s#call %s", in, *expect.name), &expect.args, &got.args) |
| } |
| |
| func valueOf(v interface{}) interface{} { |
| switch t := v.(type) { |
| case string: |
| n := valueNode(t) |
| v = &n |
| case int: |
| n := numberNode(t) |
| v = &n |
| } |
| return v |
| } |
| |
| func list(values ...interface{}) *listNode { |
| n := &listNode{} |
| for _, v := range values { |
| *n = append(*n, valueOf(v)) |
| } |
| return n |
| } |
| |
| func array(values ...interface{}) *arrayNode { |
| n := &arrayNode{} |
| for _, v := range values { |
| *n = append(*n, valueOf(v)) |
| } |
| return n |
| } |
| |
| func call(name string, values ...interface{}) *callNode { |
| n := &callNode{} |
| nv := valueNode(name) |
| n.name = &nv |
| for _, v := range values { |
| n.args = append(n.args, valueOf(v)) |
| } |
| return n |
| } |