diff --git a/lex.go b/lex.go index ee8c36f..3f47c2e 100644 --- a/lex.go +++ b/lex.go @@ -27,19 +27,28 @@ func (t tokenType) String() string { return "t_equals" case t_comment: return "t_comment" + case t_list_start: + return "t_list_start" + case t_list_end: + return "t_list_end" + case t_list_separator: + return "t_list_separator" default: panic(fmt.Sprintf("unknown token type: %v", t)) } } const ( - t_error tokenType = iota // a stored lex error - t_eof // end of file token - t_string // a string literal - t_name // a name - t_type // a type - t_equals // equals sign - t_comment // a comment + t_error tokenType = iota // a stored lex error + t_eof // end of file token + t_string // a string literal + t_name // a name + t_type // a type + t_equals // equals sign + t_comment // a comment + t_list_start // [ + t_list_end // ] + t_list_separator // , ) type stateFn func(*lexer) (stateFn, error) @@ -140,6 +149,18 @@ func lexRoot(l *lexer) (stateFn, error) { return lexStringLiteral(r), nil case r == '#': return lexComment, nil + case r == '[': + l.keep(r) + l.emit(t_list_start) + return lexRoot, nil + case r == ']': + l.keep(r) + l.emit(t_list_end) + return lexRoot, nil + case r == ',': + l.keep(r) + l.emit(t_list_separator) + return lexRoot, nil case unicode.IsSpace(r): return lexRoot, nil case unicode.IsLower(r): diff --git a/lex_test.go b/lex_test.go index 60e8baf..db669a5 100644 --- a/lex_test.go +++ b/lex_test.go @@ -35,6 +35,8 @@ var primitivesTests = []struct { # comment line one # comment line two `, []token{{t_comment, " comment line one"}, {t_comment, " comment line two"}}}, + {`[]`, []token{{t_list_start, "["}, {t_list_end, "]"}}}, + {`["item"]`, []token{{t_list_start, "["}, {t_string, "item"}, {t_list_end, "]"}}}, } func TestLexPrimities(t *testing.T) { diff --git a/node.go b/node.go index 399dd4b..cf3b4a1 100644 --- a/node.go +++ b/node.go @@ -67,7 +67,7 @@ func (n *rootNode) String() string { for _, child := range n.children { fmt.Fprintf(&buf, "%s, ", child) } - if len(n.children) > 0 { + if buf.Len() > 1 { buf.Truncate(buf.Len() - 2) } buf.WriteString("}") @@ -109,20 +109,51 @@ func (n *assignmentNode) parse(p *parser) error { return fmt.Errorf("parse error: unexpected %v token after name, expected =", t.t) } - t = p.next() - switch t.t { - case t_error: - return fmt.Errorf("parse error: saw lex error while parsing assignment node: %v", t.s) - case t_eof: - return fmt.Errorf("parse error: unexpected eof in assignment node") - case t_string: - n.value = t.s - return nil - default: - return fmt.Errorf("parse error: unexpected %v token after =, expected some kind of value", t.t) + v, err := p.parseValue() + if err != nil { + return err } + n.value = v + return nil } func (n *assignmentNode) String() string { return fmt.Sprintf("{assign: name=%s, val=%s}", n.name, n.value) } + +type list struct { + head *listElem + tail *listElem +} + +func (l list) String() string { + var buf bytes.Buffer + buf.WriteString("[") + for e := l.head; e != nil; e = e.next { + fmt.Fprintf(&buf, "%v, ", e.value) + } + if buf.Len() > 1 { + buf.Truncate(buf.Len() - 2) + } + buf.WriteString("]") + return buf.String() +} + +func (l *list) append(v interface{}) { + e := listElem{value: v} + if l.head == nil { + l.head = &e + } + + if l.tail != nil { + l.tail.next = &e + e.prev = l.tail + } + l.tail = &e +} + +type listElem struct { + value interface{} + prev *listElem + next *listElem +} diff --git a/parse.go b/parse.go index f6178c8..56fa44a 100644 --- a/parse.go +++ b/parse.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "io" ) @@ -40,3 +41,57 @@ func (p *parser) next() token { } return <-p.input } + +func (p *parser) peek() token { + t := p.next() + p.unread(t) + return t +} + +func (p *parser) unread(t token) { + if p.backup == nil { + p.backup = make([]token, 0, 8) + } + p.backup = append(p.backup, t) +} + +// 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. +func (p *parser) parseValue() (interface{}, error) { + for { + t := p.next() + switch t.t { + case t_error: + return nil, fmt.Errorf("parse error: saw lex error when looking for value: %v", t.s) + case t_eof: + return nil, fmt.Errorf("parse error: unexpected eof when looking for value") + case t_string: + return t.s, nil + case t_list_start: + l := new(list) + + SIN: + if p.peek().t == t_list_end { + p.next() + return l, nil + } + + if v, err := p.parseValue(); err != nil { + return nil, err + } else { + l.append(v) + } + + switch t := p.next(); t.t { + case t_list_separator: + goto SIN + case t_list_end: + return l, nil + default: + return nil, fmt.Errorf("parse error: unexpected %v token while scanning for list", t.t) + } + default: + return nil, fmt.Errorf("parse error: unexpected %v token while looking for value", t.t) + } + } +} diff --git a/parse_test.go b/parse_test.go index 544ef09..ef123e8 100644 --- a/parse_test.go +++ b/parse_test.go @@ -53,6 +53,12 @@ var parseTests = []parseTest{ `, root: &rootNode{}, }, + { + source: ` + heroes = ["lina", "cm"] + `, + root: &rootNode{}, + }, } type parseTest struct {