Implement inline tables
diff --git a/.travis.yml b/.travis.yml
index 83e84ad..eeaf72b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,10 +2,10 @@
script: "./test.sh"
sudo: false
go:
- - 1.1
- 1.2
- 1.3
- 1.4.1
+ - 1.5
- tip
before_install:
- go get github.com/axw/gocov/gocov
diff --git a/lexer.go b/lexer.go
index 95beb09..25635b4 100644
--- a/lexer.go
+++ b/lexer.go
@@ -158,13 +158,17 @@
case '.':
return l.errorf("cannot start float with a dot")
case '=':
- return l.errorf("cannot have multiple equals for the same key")
+ return l.lexEqual
case '[':
l.depth++
return l.lexLeftBracket
case ']':
l.depth--
return l.lexRightBracket
+ case '{':
+ return l.lexLeftCurlyBrace
+ case '}':
+ return l.lexRightCurlyBrace
case '#':
return l.lexComment
case '"':
@@ -218,6 +222,20 @@
return nil
}
+func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn {
+ l.ignore()
+ l.pos++
+ l.emit(tokenLeftCurlyBrace)
+ return l.lexRvalue
+}
+
+func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn {
+ l.ignore()
+ l.pos++
+ l.emit(tokenRightCurlyBrace)
+ return l.lexRvalue
+}
+
func (l *tomlLexer) lexDate() tomlLexStateFn {
l.emit(tokenDate)
return l.lexRvalue
diff --git a/lexer_test.go b/lexer_test.go
index 82946bf..3509367 100644
--- a/lexer_test.go
+++ b/lexer_test.go
@@ -8,11 +8,12 @@
token := <-ch
if token != expected {
t.Log("While testing: ", input)
+ t.Log("compared (got)", token, "to (expected)", expected)
+ t.Log("\tvalue:", token.val, "<->", expected.val)
+ t.Log("\ttype:", token.typ.String(), "<->", expected.typ.String())
+ t.Log("\tline:", token.Line, "<->", expected.Line)
+ t.Log("\tcolumn:", token.Col, "<->", expected.Col)
t.Log("compared", token, "to", expected)
- t.Log(token.val, "<->", expected.val)
- t.Log(token.typ, "<->", expected.typ)
- t.Log(token.Line, "<->", expected.Line)
- t.Log(token.Col, "<->", expected.Col)
t.FailNow()
}
}
@@ -371,14 +372,6 @@
})
}
-func TestDoubleEqualKey(t *testing.T) {
- testFlow(t, "foo= = 2", []token{
- token{Position{1, 1}, tokenKey, "foo"},
- token{Position{1, 4}, tokenEqual, "="},
- token{Position{1, 5}, tokenError, "cannot have multiple equals for the same key"},
- })
-}
-
func TestInvalidEsquapeSequence(t *testing.T) {
testFlow(t, `foo = "\x"`, []token{
token{Position{1, 1}, tokenKey, "foo"},
diff --git a/parser.go b/parser.go
index 59f513b..9075002 100644
--- a/parser.go
+++ b/parser.go
@@ -244,6 +244,8 @@
return val
case tokenLeftBracket:
return p.parseArray()
+ case tokenLeftCurlyBrace:
+ return p.parseInlineTable()
case tokenError:
p.raiseError(tok, "%s", tok)
}
@@ -253,7 +255,51 @@
return nil
}
-func (p *tomlParser) parseArray() []interface{} {
+func tokenIsComma(t *token) bool {
+ return t != nil && t.typ == tokenComma
+}
+
+func (p *tomlParser) parseInlineTable() *TomlTree {
+ tree := newTomlTree()
+ var previous *token
+Loop:
+ for {
+ follow := p.peek()
+ if follow == nil || follow.typ == tokenEOF {
+ p.raiseError(follow, "unterminated inline table")
+ }
+ switch follow.typ {
+ case tokenRightCurlyBrace:
+ p.getToken()
+ break Loop
+ case tokenKey:
+ if !tokenIsComma(previous) && previous != nil {
+ p.raiseError(follow, "comma expected between fields in inline table")
+ }
+ key := p.getToken()
+ p.assume(tokenEqual)
+ value := p.parseRvalue()
+ tree.Set(key.val, value)
+ case tokenComma:
+ if previous == nil {
+ p.raiseError(follow, "inline table cannot start with a comma")
+ }
+ if tokenIsComma(previous) {
+ p.raiseError(follow, "need field between two commas in inline table")
+ }
+ p.getToken()
+ default:
+ p.raiseError(follow, "unexpected token type in inline table: %s", follow.typ.String())
+ }
+ previous = follow
+ }
+ if tokenIsComma(previous) {
+ p.raiseError(previous, "trailing comma at the end of inline table")
+ }
+ return tree
+}
+
+func (p *tomlParser) parseArray() interface{} {
var array []interface{}
arrayType := reflect.TypeOf(nil)
for {
@@ -263,6 +309,13 @@
}
if follow.typ == tokenRightBracket {
p.getToken()
+ if arrayType == reflect.TypeOf(newTomlTree()) {
+ tomlArray := make([]*TomlTree, len(array))
+ for i, v := range array {
+ tomlArray[i] = v.(*TomlTree)
+ }
+ return tomlArray
+ }
return array
}
val := p.parseRvalue()
diff --git a/parser_test.go b/parser_test.go
index ebf1626..c507a23 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -310,6 +310,52 @@
})
}
+func TestSimpleInlineGroup(t *testing.T) {
+ tree, err := Load("key = {a = 42}")
+ assertTree(t, tree, err, map[string]interface{}{
+ "key": map[string]interface{}{
+ "a": int64(42),
+ },
+ })
+}
+
+func TestDoubleInlineGroup(t *testing.T) {
+ tree, err := Load("key = {a = 42, b = \"foo\"}")
+ assertTree(t, tree, err, map[string]interface{}{
+ "key": map[string]interface{}{
+ "a": int64(42),
+ "b": "foo",
+ },
+ })
+}
+
+func TestExampleInlineGroup(t *testing.T) {
+ tree, err := Load(`name = { first = "Tom", last = "Preston-Werner" }
+point = { x = 1, y = 2 }`)
+ assertTree(t, tree, err, map[string]interface{}{
+ "name": map[string]interface{}{
+ "first": "Tom",
+ "last": "Preston-Werner",
+ },
+ "point": map[string]interface{}{
+ "x": int64(1),
+ "y": int64(2),
+ },
+ })
+}
+
+func TestExampleInlineGroupInArray(t *testing.T) {
+ tree, err := Load(`points = [{ x = 1, y = 2 }]`)
+ assertTree(t, tree, err, map[string]interface{}{
+ "points": []map[string]interface{}{
+ map[string]interface{}{
+ "x": int64(1),
+ "y": int64(2),
+ },
+ },
+ })
+}
+
func TestDuplicateGroups(t *testing.T) {
_, err := Load("[foo]\na=2\n[foo]b=3")
if err.Error() != "(3, 2): duplicated tables" {
diff --git a/token.go b/token.go
index f0fbac9..ebbf960 100644
--- a/token.go
+++ b/token.go
@@ -26,6 +26,8 @@
tokenEqual
tokenLeftBracket
tokenRightBracket
+ tokenLeftCurlyBrace
+ tokenRightCurlyBrace
tokenLeftParen
tokenRightParen
tokenDoubleLeftBracket
@@ -44,6 +46,7 @@
)
var tokenTypeNames = []string{
+ "Error",
"EOF",
"Comment",
"Key",
@@ -54,7 +57,9 @@
"Float",
"=",
"[",
- "[",
+ "]",
+ "{",
+ "}",
"(",
")",
"]]",