Implement inline tables
This commit is contained in:
+1
-1
@@ -2,10 +2,10 @@ language: go
|
|||||||
script: "./test.sh"
|
script: "./test.sh"
|
||||||
sudo: false
|
sudo: false
|
||||||
go:
|
go:
|
||||||
- 1.1
|
|
||||||
- 1.2
|
- 1.2
|
||||||
- 1.3
|
- 1.3
|
||||||
- 1.4.1
|
- 1.4.1
|
||||||
|
- 1.5
|
||||||
- tip
|
- tip
|
||||||
before_install:
|
before_install:
|
||||||
- go get github.com/axw/gocov/gocov
|
- go get github.com/axw/gocov/gocov
|
||||||
|
|||||||
@@ -158,13 +158,17 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn {
|
|||||||
case '.':
|
case '.':
|
||||||
return l.errorf("cannot start float with a dot")
|
return l.errorf("cannot start float with a dot")
|
||||||
case '=':
|
case '=':
|
||||||
return l.errorf("cannot have multiple equals for the same key")
|
return l.lexEqual
|
||||||
case '[':
|
case '[':
|
||||||
l.depth++
|
l.depth++
|
||||||
return l.lexLeftBracket
|
return l.lexLeftBracket
|
||||||
case ']':
|
case ']':
|
||||||
l.depth--
|
l.depth--
|
||||||
return l.lexRightBracket
|
return l.lexRightBracket
|
||||||
|
case '{':
|
||||||
|
return l.lexLeftCurlyBrace
|
||||||
|
case '}':
|
||||||
|
return l.lexRightCurlyBrace
|
||||||
case '#':
|
case '#':
|
||||||
return l.lexComment
|
return l.lexComment
|
||||||
case '"':
|
case '"':
|
||||||
@@ -218,6 +222,20 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn {
|
|||||||
return nil
|
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 {
|
func (l *tomlLexer) lexDate() tomlLexStateFn {
|
||||||
l.emit(tokenDate)
|
l.emit(tokenDate)
|
||||||
return l.lexRvalue
|
return l.lexRvalue
|
||||||
|
|||||||
+5
-12
@@ -8,11 +8,12 @@ func testFlow(t *testing.T, input string, expectedFlow []token) {
|
|||||||
token := <-ch
|
token := <-ch
|
||||||
if token != expected {
|
if token != expected {
|
||||||
t.Log("While testing: ", input)
|
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("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()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,14 +372,6 @@ func TestFloatWithExponent5(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
func TestInvalidEsquapeSequence(t *testing.T) {
|
||||||
testFlow(t, `foo = "\x"`, []token{
|
testFlow(t, `foo = "\x"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
token{Position{1, 1}, tokenKey, "foo"},
|
||||||
|
|||||||
@@ -244,6 +244,8 @@ func (p *tomlParser) parseRvalue() interface{} {
|
|||||||
return val
|
return val
|
||||||
case tokenLeftBracket:
|
case tokenLeftBracket:
|
||||||
return p.parseArray()
|
return p.parseArray()
|
||||||
|
case tokenLeftCurlyBrace:
|
||||||
|
return p.parseInlineTable()
|
||||||
case tokenError:
|
case tokenError:
|
||||||
p.raiseError(tok, "%s", tok)
|
p.raiseError(tok, "%s", tok)
|
||||||
}
|
}
|
||||||
@@ -253,7 +255,51 @@ func (p *tomlParser) parseRvalue() interface{} {
|
|||||||
return nil
|
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{}
|
var array []interface{}
|
||||||
arrayType := reflect.TypeOf(nil)
|
arrayType := reflect.TypeOf(nil)
|
||||||
for {
|
for {
|
||||||
@@ -263,6 +309,13 @@ func (p *tomlParser) parseArray() []interface{} {
|
|||||||
}
|
}
|
||||||
if follow.typ == tokenRightBracket {
|
if follow.typ == tokenRightBracket {
|
||||||
p.getToken()
|
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
|
return array
|
||||||
}
|
}
|
||||||
val := p.parseRvalue()
|
val := p.parseRvalue()
|
||||||
|
|||||||
@@ -310,6 +310,52 @@ func TestArrayWithExtraCommaComment(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestDuplicateGroups(t *testing.T) {
|
||||||
_, err := Load("[foo]\na=2\n[foo]b=3")
|
_, err := Load("[foo]\na=2\n[foo]b=3")
|
||||||
if err.Error() != "(3, 2): duplicated tables" {
|
if err.Error() != "(3, 2): duplicated tables" {
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const (
|
|||||||
tokenEqual
|
tokenEqual
|
||||||
tokenLeftBracket
|
tokenLeftBracket
|
||||||
tokenRightBracket
|
tokenRightBracket
|
||||||
|
tokenLeftCurlyBrace
|
||||||
|
tokenRightCurlyBrace
|
||||||
tokenLeftParen
|
tokenLeftParen
|
||||||
tokenRightParen
|
tokenRightParen
|
||||||
tokenDoubleLeftBracket
|
tokenDoubleLeftBracket
|
||||||
@@ -44,6 +46,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var tokenTypeNames = []string{
|
var tokenTypeNames = []string{
|
||||||
|
"Error",
|
||||||
"EOF",
|
"EOF",
|
||||||
"Comment",
|
"Comment",
|
||||||
"Key",
|
"Key",
|
||||||
@@ -54,7 +57,9 @@ var tokenTypeNames = []string{
|
|||||||
"Float",
|
"Float",
|
||||||
"=",
|
"=",
|
||||||
"[",
|
"[",
|
||||||
"[",
|
"]",
|
||||||
|
"{",
|
||||||
|
"}",
|
||||||
"(",
|
"(",
|
||||||
")",
|
")",
|
||||||
"]]",
|
"]]",
|
||||||
|
|||||||
Reference in New Issue
Block a user