parse an object

master
Jordan Orelli 10 years ago
parent 213561bc59
commit 04d982c3d9

@ -33,22 +33,31 @@ func (t tokenType) String() string {
return "t_list_end" return "t_list_end"
case t_list_separator: case t_list_separator:
return "t_list_separator" return "t_list_separator"
case t_object_start:
return "t_object_start"
case t_object_separator:
return "t_object_separator"
case t_object_end:
return "t_object_end"
default: default:
panic(fmt.Sprintf("unknown token type: %v", t)) panic(fmt.Sprintf("unknown token type: %v", t))
} }
} }
const ( const (
t_error tokenType = iota // a stored lex error t_error tokenType = iota // a stored lex error
t_eof // end of file token t_eof // end of file token
t_string // a string literal t_string // a string literal
t_name // a name t_name // a name
t_type // a type t_type // a type
t_equals // equals sign t_equals // equals sign
t_comment // a comment t_comment // a comment
t_list_start // [ t_list_start // [
t_list_end // ] t_list_end // ]
t_list_separator // , t_list_separator // ,
t_object_start // {
t_object_end // }
t_object_separator // :
) )
type stateFn func(*lexer) (stateFn, error) type stateFn func(*lexer) (stateFn, error)
@ -161,6 +170,18 @@ func lexRoot(l *lexer) (stateFn, error) {
l.keep(r) l.keep(r)
l.emit(t_list_separator) l.emit(t_list_separator)
return lexRoot, nil return lexRoot, nil
case r == '{':
l.keep(r)
l.emit(t_object_start)
return lexRoot, nil
case r == '}':
l.keep(r)
l.emit(t_object_end)
return lexRoot, nil
case r == ':':
l.keep(r)
l.emit(t_object_separator)
return lexRoot, nil
case unicode.IsSpace(r): case unicode.IsSpace(r):
return lexRoot, nil return lexRoot, nil
case unicode.IsLower(r): case unicode.IsLower(r):

@ -37,6 +37,18 @@ var primitivesTests = []struct {
`, []token{{t_comment, " comment line one"}, {t_comment, " comment line two"}}}, `, []token{{t_comment, " comment line one"}, {t_comment, " comment line two"}}},
{`[]`, []token{{t_list_start, "["}, {t_list_end, "]"}}}, {`[]`, []token{{t_list_start, "["}, {t_list_end, "]"}}},
{`["item"]`, []token{{t_list_start, "["}, {t_string, "item"}, {t_list_end, "]"}}}, {`["item"]`, []token{{t_list_start, "["}, {t_string, "item"}, {t_list_end, "]"}}},
{`{}`, []token{{t_object_start, "{"}, {t_object_end, "}"}}},
{`{first_name: "jordan", last_name: "orelli"}`, []token{
{t_object_start, "{"},
{t_name, "first_name"},
{t_object_separator, ":"},
{t_string, "jordan"},
{t_list_separator, ","},
{t_name, "last_name"},
{t_object_separator, ":"},
{t_string, "orelli"},
{t_object_end, "}"},
}},
} }
func TestLexPrimities(t *testing.T) { func TestLexPrimities(t *testing.T) {

@ -157,3 +157,5 @@ type listElem struct {
prev *listElem prev *listElem
next *listElem next *listElem
} }
type object map[string]interface{}

@ -55,6 +55,13 @@ func (p *parser) unread(t token) {
p.backup = append(p.backup, t) p.backup = append(p.backup, t)
} }
func (p *parser) ensureNext(tt tokenType, context string) error {
if p.peek().t != tt {
return fmt.Errorf("unexpected %v in %s: expected %v", p.peek().t, context, tt)
}
return nil
}
// parse the next value. This is to be executed in a context where we know we // parse the next value. This is to be executed in a context where we know we
// want something that is a value to come next, such as after an equals sign. // want something that is a value to come next, such as after an equals sign.
func (p *parser) parseValue() (interface{}, error) { func (p *parser) parseValue() (interface{}, error) {
@ -69,6 +76,8 @@ func (p *parser) parseValue() (interface{}, error) {
return t.s, nil return t.s, nil
case t_list_start: case t_list_start:
return p.parseList(new(list)) return p.parseList(new(list))
case t_object_start:
return p.parseObject(make(object))
default: default:
return nil, fmt.Errorf("parse error: unexpected %v token while looking for value", t.t) return nil, fmt.Errorf("parse error: unexpected %v token while looking for value", t.t)
} }
@ -96,3 +105,33 @@ func (p *parser) parseList(l *list) (*list, error) {
return nil, fmt.Errorf("parse error: unexpected %v token while scanning for list", t.t) return nil, fmt.Errorf("parse error: unexpected %v token while scanning for list", 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.next(); t.t {
case t_list_separator:
return p.parseObject(obj)
case t_object_end:
return obj, nil
default:
return nil, fmt.Errorf("parse error: unexpected %v token while scanning for object", t.t)
}
}

@ -59,6 +59,36 @@ var parseTests = []parseTest{
`, `,
root: &rootNode{}, root: &rootNode{},
}, },
{
source: `
nested = [["one", "two"], ["three", "four"]]
`,
root: &rootNode{},
},
{
source: `
nested = [
["one", "two"],
["three", "four"],
]
`,
root: &rootNode{},
},
{
source: `
admin = {first_name: "jordan", last_name: "orelli"}
`,
root: &rootNode{},
},
{
source: `
http = {
port: "9000",
routes: "/path/to/some/file",
}
`,
root: &rootNode{},
},
} }
type parseTest struct { type parseTest struct {

Loading…
Cancel
Save