diff --git a/node.go b/node.go index bdf9574..3a0b1b2 100644 --- a/node.go +++ b/node.go @@ -5,6 +5,7 @@ import ( "bytes" "fmt" "io" + "sort" "strconv" "strings" ) @@ -19,6 +20,7 @@ const ( n_string n_number n_list + n_object ) var indent = " " @@ -341,5 +343,66 @@ func (l *listNode) eval(ctx map[string]interface{}) (interface{}, error) { return out, nil } -type list []interface{} -type object map[string]interface{} +type objectNode map[string]node + +func (o *objectNode) Type() nodeType { + return n_object +} + +func (o *objectNode) parse(p *parser) error { + if p.peek().t == t_object_end { + p.next() + return nil + } + if err := p.ensureNext(t_name, "looking for object field name in parseObject"); err != nil { + return err + } + field_name := p.next().s + if err := p.ensureNext(t_object_separator, "looking for object separator in parseObject"); err != nil { + return err + } + p.next() + + if n, err := p.parseValue(); err != nil { + return err + } else { + (*o)[field_name] = n + } + + switch t := p.peek(); t.t { + case t_object_end: + p.next() + return nil + default: + return o.parse(p) + } +} + +func (o *objectNode) pretty(w io.Writer, prefix string) error { + fmt.Fprintf(w, "%sobject:\n", prefix) + keys := make([]string, 0, len(*o)) + for key := range *o { + keys = append(keys, key) + } + sort.Strings(keys) + for _, key := range keys { + fmt.Fprintf(w, "%s%s:\n", prefix+indent, key) + err := (*o)[key].pretty(w, prefix+indent+indent) + if err != nil { + return err + } + } + return nil +} + +func (o *objectNode) eval(ctx map[string]interface{}) (interface{}, error) { + out := make(map[string]interface{}, len(*o)) + for name, node := range *o { + v, err := node.eval(ctx) + if err != nil { + return nil, err + } + out[name] = v + } + return out, nil +} diff --git a/parse.go b/parse.go index 6c87d3a..5325a60 100644 --- a/parse.go +++ b/parse.go @@ -99,40 +99,15 @@ func (p *parser) parseValue() (node, error) { return nil, err } return n, nil - // return p.parseList(make(list, 0, 4)) - // case t_object_start: - // return p.parseObject(make(object)) + case t_object_start: + p.next() + n := &objectNode{} + if err := n.parse(p); err != nil { + return nil, err + } + return n, nil default: return nil, fmt.Errorf("parse error: unexpected %v token while looking for value", t.t) } } } - -func (p *parser) parseObject(obj object) (object, error) { - if p.peek().t == t_object_end { - p.next() - return obj, nil - } - if err := p.ensureNext(t_name, "looking for object field name in parseObject"); err != nil { - return nil, err - } - field_name := p.next().s - if err := p.ensureNext(t_object_separator, "looking for object separator in parseObject"); err != nil { - return nil, err - } - p.next() - - if v, err := p.parseValue(); err != nil { - return nil, err - } else { - obj[field_name] = v - } - - switch t := p.peek(); t.t { - case t_object_end: - p.next() - return obj, nil - default: - return p.parseObject(obj) - } -} diff --git a/tests/parse/04.in b/tests/parse/04.in new file mode 100644 index 0000000..c4c5bf1 --- /dev/null +++ b/tests/parse/04.in @@ -0,0 +1,10 @@ +an_object: { + item_one: 1 + item_two: "two" + item_three: [1 2 3] + item_four: { + item_one: "one" + item_two: 2 + item_three: ["one" "two" "three"] + } +} diff --git a/tests/parse/04.out b/tests/parse/04.out new file mode 100644 index 0000000..f4027be --- /dev/null +++ b/tests/parse/04.out @@ -0,0 +1,36 @@ +root: + assign: + name: + an_object + value: + object: + item_four: + object: + item_one: + string: + one + item_three: + list: + string: + one + string: + two + string: + three + item_two: + number: + 2 + item_one: + number: + 1 + item_three: + list: + number: + 1 + number: + 2 + number: + 3 + item_two: + string: + two