Merge pull request #39 from pelletier/pelletier/integers_underscores
Accept underscores in integers
diff --git a/.travis.yml b/.travis.yml
index cdd30d2..83e84ad 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,3 +7,9 @@
- 1.3
- 1.4.1
- tip
+before_install:
+ - go get github.com/axw/gocov/gocov
+ - go get github.com/mattn/goveralls
+ - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
+after_success:
+ - $HOME/gopath/bin/goveralls -service=travis-ci
diff --git a/README.md b/README.md
index db73b4b..d154726 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@
[](http://godoc.org/github.com/pelletier/go-toml)
[](https://travis-ci.org/pelletier/go-toml)
+[](https://coveralls.io/github/pelletier/go-toml?branch=master)
## Features
diff --git a/keysparsing.go b/keysparsing.go
index 528f1de..7535379 100644
--- a/keysparsing.go
+++ b/keysparsing.go
@@ -13,7 +13,16 @@
var buffer bytes.Buffer
inQuotes := false
escapeNext := false
+ ignoreSpace := true
+ expectDot := false
+
for _, char := range key {
+ if ignoreSpace {
+ if char == ' ' {
+ continue
+ }
+ ignoreSpace = false
+ }
if escapeNext {
buffer.WriteRune(char)
escapeNext = false
@@ -25,18 +34,31 @@
continue
case '"':
inQuotes = !inQuotes
+ expectDot = false
case '.':
if inQuotes {
buffer.WriteRune(char)
} else {
groups = append(groups, buffer.String())
buffer.Reset()
+ ignoreSpace = true
+ expectDot = false
+ }
+ case ' ':
+ if inQuotes {
+ buffer.WriteRune(char)
+ } else {
+ expectDot = true
}
default:
if !inQuotes && !isValidBareChar(char) {
return nil, fmt.Errorf("invalid bare character: %c", char)
}
+ if !inQuotes && expectDot {
+ return nil, fmt.Errorf("what?")
+ }
buffer.WriteRune(char)
+ expectDot = false
}
}
if inQuotes {
diff --git a/lexer.go b/lexer.go
index b8ba2ed..95beb09 100644
--- a/lexer.go
+++ b/lexer.go
@@ -423,7 +423,11 @@
return l.errorf("invalid escape sequence: \\" + string(l.peek()))
}
} else {
- growingString += string(l.peek())
+ r := l.peek()
+ if 0x00 <= r && r <= 0x1F {
+ return l.errorf("unescaped control character %U", r)
+ }
+ growingString += string(r)
}
if l.next() == eof {
diff --git a/lexer_test.go b/lexer_test.go
index 37535e5..82946bf 100644
--- a/lexer_test.go
+++ b/lexer_test.go
@@ -39,6 +39,15 @@
})
}
+func TestNestedQuotedUnicodeKeyGroup(t *testing.T) {
+ testFlow(t, `[ j . "ʞ" . l ]`, []token{
+ token{Position{1, 1}, tokenLeftBracket, "["},
+ token{Position{1, 2}, tokenKeyGroup, ` j . "ʞ" . l `},
+ token{Position{1, 15}, tokenRightBracket, "]"},
+ token{Position{1, 16}, tokenEOF, ""},
+ })
+}
+
func TestUnclosedKeyGroup(t *testing.T) {
testFlow(t, "[hello world", []token{
token{Position{1, 1}, tokenLeftBracket, "["},
@@ -484,6 +493,19 @@
})
}
+func TestKeyEqualStringNoEscape(t *testing.T) {
+ testFlow(t, "foo = \"hello \u0002\"", []token{
+ token{Position{1, 1}, tokenKey, "foo"},
+ token{Position{1, 5}, tokenEqual, "="},
+ token{Position{1, 8}, tokenError, "unescaped control character U+0002"},
+ })
+ testFlow(t, "foo = \"hello \u001F\"", []token{
+ token{Position{1, 1}, tokenKey, "foo"},
+ token{Position{1, 5}, tokenEqual, "="},
+ token{Position{1, 8}, tokenError, "unescaped control character U+001F"},
+ })
+}
+
func TestLiteralString(t *testing.T) {
testFlow(t, `foo = 'C:\Users\nodejs\templates'`, []token{
token{Position{1, 1}, tokenKey, "foo"},
@@ -535,19 +557,33 @@
token{Position{1, 34}, tokenEOF, ""},
})
- testFlow(t, "foo = \"\"\"\nhello\n\"literal\"\nworld\"\"\"", []token{
+ testFlow(t, "foo = \"\"\"\nhello\\\n\"literal\"\\\nworld\"\"\"", []token{
token{Position{1, 1}, tokenKey, "foo"},
token{Position{1, 5}, tokenEqual, "="},
- token{Position{2, 1}, tokenString, "hello\n\"literal\"\nworld"},
+ token{Position{2, 1}, tokenString, "hello\"literal\"world"},
token{Position{4, 9}, tokenEOF, ""},
})
- testFlow(t, "foo = \"\"\"\\\n \\\n \\\n hello\nmultiline\nworld\"\"\"", []token{
+ testFlow(t, "foo = \"\"\"\\\n \\\n \\\n hello\\\nmultiline\\\nworld\"\"\"", []token{
token{Position{1, 1}, tokenKey, "foo"},
token{Position{1, 5}, tokenEqual, "="},
- token{Position{1, 10}, tokenString, "hello\nmultiline\nworld"},
+ token{Position{1, 10}, tokenString, "hellomultilineworld"},
token{Position{6, 9}, tokenEOF, ""},
})
+
+ testFlow(t, "key2 = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"", []token{
+ token{Position{1, 1}, tokenKey, "key2"},
+ token{Position{1, 6}, tokenEqual, "="},
+ token{Position{2, 1}, tokenString, "The quick brown fox jumps over the lazy dog."},
+ token{Position{6, 21}, tokenEOF, ""},
+ })
+
+ testFlow(t, "key2 = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", []token{
+ token{Position{1, 1}, tokenKey, "key2"},
+ token{Position{1, 6}, tokenEqual, "="},
+ token{Position{1, 11}, tokenString, "The quick brown fox jumps over the lazy dog."},
+ token{Position{5, 11}, tokenEOF, ""},
+ })
}
func TestUnicodeString(t *testing.T) {
diff --git a/parser_test.go b/parser_test.go
index 9bdf9b2..ebf1626 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -174,6 +174,41 @@
})
}
+func TestNestedQuotedUnicodeKeys(t *testing.T) {
+ tree, err := Load("[ j . \"ʞ\" . l ]\nd = 42")
+ assertTree(t, tree, err, map[string]interface{}{
+ "j": map[string]interface{}{
+ "ʞ": map[string]interface{}{
+ "l": map[string]interface{}{
+ "d": int64(42),
+ },
+ },
+ },
+ })
+
+ tree, err = Load("[ g . h . i ]\nd = 42")
+ assertTree(t, tree, err, map[string]interface{}{
+ "g": map[string]interface{}{
+ "h": map[string]interface{}{
+ "i": map[string]interface{}{
+ "d": int64(42),
+ },
+ },
+ },
+ })
+
+ tree, err = Load("[ d.e.f ]\nk = 42")
+ assertTree(t, tree, err, map[string]interface{}{
+ "d": map[string]interface{}{
+ "e": map[string]interface{}{
+ "f": map[string]interface{}{
+ "k": int64(42),
+ },
+ },
+ },
+ })
+}
+
func TestArrayOne(t *testing.T) {
tree, err := Load("a = [1]")
assertTree(t, tree, err, map[string]interface{}{