// 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 }