Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d464759235 | |||
| 7cb988051d | |||
| 3ddb37c944 | |||
| f7f14983c3 | |||
| 45932ad32d | |||
| 67b7b944a8 | |||
| 31055c2ff0 | |||
| 5a62685873 | |||
| d05a14897c | |||
| 0599275eb9 | |||
| 0049ab3dc4 | |||
| bfe4a7e160 | |||
| e6271032cc | |||
| 887411a2a8 | |||
| 31c735e72c | |||
| 06484b677b | |||
| de2e921d55 | |||
| 7f292800de | |||
| 923742e542 | |||
| 65ad89c1a7 |
+8
-3
@@ -1,10 +1,15 @@
|
|||||||
language: go
|
language: go
|
||||||
script: "./test.sh"
|
|
||||||
go:
|
go:
|
||||||
- 1.4.3
|
|
||||||
- 1.5.4
|
- 1.5.4
|
||||||
- 1.6.2
|
- 1.6.4
|
||||||
|
- 1.7.4
|
||||||
- tip
|
- tip
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
fast_finish: true
|
||||||
|
script:
|
||||||
|
- ./test.sh
|
||||||
before_install:
|
before_install:
|
||||||
- go get github.com/axw/gocov/gocov
|
- go get github.com/axw/gocov/gocov
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 - 2016 Thomas Pelletier, Eric Anderton
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
@@ -6,8 +6,10 @@ This library supports TOML version
|
|||||||
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
||||||
|
|
||||||
[](http://godoc.org/github.com/pelletier/go-toml)
|
[](http://godoc.org/github.com/pelletier/go-toml)
|
||||||
|
[](https://github.com/goadesign/goa/blob/master/LICENSE)
|
||||||
[](https://travis-ci.org/pelletier/go-toml)
|
[](https://travis-ci.org/pelletier/go-toml)
|
||||||
[](https://coveralls.io/github/pelletier/go-toml?branch=master)
|
[](https://coveralls.io/github/pelletier/go-toml?branch=master)
|
||||||
|
[](https://goreportcard.com/report/github.com/pelletier/go-toml)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -81,6 +83,23 @@ if err != nil {
|
|||||||
The documentation and additional examples are available at
|
The documentation and additional examples are available at
|
||||||
[godoc.org](http://godoc.org/github.com/pelletier/go-toml).
|
[godoc.org](http://godoc.org/github.com/pelletier/go-toml).
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
Go-toml provides two handy command line tools:
|
||||||
|
|
||||||
|
* `tomll`: Reads TOML files and lint them.
|
||||||
|
|
||||||
|
```
|
||||||
|
go install github.com/pelletier/go-toml/cmd/tomll
|
||||||
|
tomll --help
|
||||||
|
```
|
||||||
|
* `tomljson`: Reads a TOML file and outputs its JSON representation.
|
||||||
|
|
||||||
|
```
|
||||||
|
go install github.com/pelletier/go-toml/cmd/tomjson
|
||||||
|
tomljson --help
|
||||||
|
```
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
Feel free to report bugs and patches using GitHub's pull requests system on
|
Feel free to report bugs and patches using GitHub's pull requests system on
|
||||||
@@ -98,22 +117,4 @@ You can run both of them using `./test.sh`.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (c) 2013 - 2016 Thomas Pelletier, Eric Anderton
|
The MIT License (MIT). Read [LICENSE](LICENSE).
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|||||||
+2
-1
@@ -3,11 +3,12 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pelletier/go-toml"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pelletier/go-toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pelletier/go-toml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintln(os.Stderr, `tomljson can be used in two ways:
|
||||||
|
Writing to STDIN and reading from STDOUT:
|
||||||
|
cat file.toml | tomljson > file.json
|
||||||
|
|
||||||
|
Reading from a file name:
|
||||||
|
tomljson file.toml
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
os.Exit(processMain(flag.Args(), os.Stdin, os.Stdout, os.Stderr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func processMain(files []string, defaultInput io.Reader, output io.Writer, errorOutput io.Writer) int {
|
||||||
|
// read from stdin and print to stdout
|
||||||
|
inputReader := defaultInput
|
||||||
|
|
||||||
|
if len(files) > 0 {
|
||||||
|
var err error
|
||||||
|
inputReader, err = os.Open(files[0])
|
||||||
|
if err != nil {
|
||||||
|
printError(err, errorOutput)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s, err := reader(inputReader)
|
||||||
|
if err != nil {
|
||||||
|
printError(err, errorOutput)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
io.WriteString(output, s+"\n")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func printError(err error, output io.Writer) {
|
||||||
|
io.WriteString(output, err.Error()+"\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func reader(r io.Reader) (string, error) {
|
||||||
|
tree, err := toml.LoadReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return mapToJSON(tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToJSON(tree *toml.TomlTree) (string, error) {
|
||||||
|
treeMap := tree.ToMap()
|
||||||
|
bytes, err := json.MarshalIndent(treeMap, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(bytes[:]), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func expectBufferEquality(t *testing.T, name string, buffer *bytes.Buffer, expected string) {
|
||||||
|
output := buffer.String()
|
||||||
|
if output != expected {
|
||||||
|
t.Errorf("incorrect %s:\n%s\n\nexpected %s:\n%s", name, output, name, expected)
|
||||||
|
t.Log([]rune(output))
|
||||||
|
t.Log([]rune(expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectProcessMainResults(t *testing.T, input string, args []string, exitCode int, expectedOutput string, expectedError string) {
|
||||||
|
inputReader := strings.NewReader(input)
|
||||||
|
outputBuffer := new(bytes.Buffer)
|
||||||
|
errorBuffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
returnCode := processMain(args, inputReader, outputBuffer, errorBuffer)
|
||||||
|
|
||||||
|
expectBufferEquality(t, "output", outputBuffer, expectedOutput)
|
||||||
|
expectBufferEquality(t, "error", errorBuffer, expectedError)
|
||||||
|
|
||||||
|
if returnCode != exitCode {
|
||||||
|
t.Error("incorrect return code:", returnCode, "expected", exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessMainReadFromStdin(t *testing.T) {
|
||||||
|
input := `
|
||||||
|
[mytoml]
|
||||||
|
a = 42`
|
||||||
|
expectedOutput := `{
|
||||||
|
"mytoml": {
|
||||||
|
"a": 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
expectedError := ``
|
||||||
|
expectedExitCode := 0
|
||||||
|
|
||||||
|
expectProcessMainResults(t, input, []string{}, expectedExitCode, expectedOutput, expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessMainReadFromFile(t *testing.T) {
|
||||||
|
input := `
|
||||||
|
[mytoml]
|
||||||
|
a = 42`
|
||||||
|
|
||||||
|
tmpfile, err := ioutil.TempFile("", "example.toml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := tmpfile.Write([]byte(input)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
expectedOutput := `{
|
||||||
|
"mytoml": {
|
||||||
|
"a": 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
expectedError := ``
|
||||||
|
expectedExitCode := 0
|
||||||
|
|
||||||
|
expectProcessMainResults(t, ``, []string{tmpfile.Name()}, expectedExitCode, expectedOutput, expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessMainReadFromMissingFile(t *testing.T) {
|
||||||
|
expectedError := `open /this/file/does/not/exist: no such file or directory
|
||||||
|
`
|
||||||
|
expectProcessMainResults(t, ``, []string{"/this/file/does/not/exist"}, -1, ``, expectedError)
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ func parseKey(key string) ([]string, error) {
|
|||||||
groups := []string{}
|
groups := []string{}
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
inQuotes := false
|
inQuotes := false
|
||||||
|
wasInQuotes := false
|
||||||
escapeNext := false
|
escapeNext := false
|
||||||
ignoreSpace := true
|
ignoreSpace := true
|
||||||
expectDot := false
|
expectDot := false
|
||||||
@@ -33,16 +34,27 @@ func parseKey(key string) ([]string, error) {
|
|||||||
escapeNext = true
|
escapeNext = true
|
||||||
continue
|
continue
|
||||||
case '"':
|
case '"':
|
||||||
|
if inQuotes {
|
||||||
|
groups = append(groups, buffer.String())
|
||||||
|
buffer.Reset()
|
||||||
|
wasInQuotes = true
|
||||||
|
}
|
||||||
inQuotes = !inQuotes
|
inQuotes = !inQuotes
|
||||||
expectDot = false
|
expectDot = false
|
||||||
case '.':
|
case '.':
|
||||||
if inQuotes {
|
if inQuotes {
|
||||||
buffer.WriteRune(char)
|
buffer.WriteRune(char)
|
||||||
} else {
|
} else {
|
||||||
|
if !wasInQuotes {
|
||||||
|
if buffer.Len() == 0 {
|
||||||
|
return nil, fmt.Errorf("empty key group")
|
||||||
|
}
|
||||||
groups = append(groups, buffer.String())
|
groups = append(groups, buffer.String())
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
|
}
|
||||||
ignoreSpace = true
|
ignoreSpace = true
|
||||||
expectDot = false
|
expectDot = false
|
||||||
|
wasInQuotes = false
|
||||||
}
|
}
|
||||||
case ' ':
|
case ' ':
|
||||||
if inQuotes {
|
if inQuotes {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
func testResult(t *testing.T, key string, expected []string) {
|
func testResult(t *testing.T, key string, expected []string) {
|
||||||
parsed, err := parseKey(key)
|
parsed, err := parseKey(key)
|
||||||
|
t.Logf("key=%s expected=%s parsed=%s", key, expected, parsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Unexpected error:", err)
|
t.Fatal("Unexpected error:", err)
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,13 @@ func TestBaseKeyPound(t *testing.T) {
|
|||||||
testError(t, "hello#world", "invalid bare character: #")
|
testError(t, "hello#world", "invalid bare character: #")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQuotedKeys(t *testing.T) {
|
||||||
|
testResult(t, `hello."foo".bar`, []string{"hello", "foo", "bar"})
|
||||||
|
testResult(t, `"hello!"`, []string{"hello!"})
|
||||||
|
}
|
||||||
|
|
||||||
func TestEmptyKey(t *testing.T) {
|
func TestEmptyKey(t *testing.T) {
|
||||||
testError(t, "", "empty key")
|
testError(t, "", "empty key")
|
||||||
testError(t, " ", "empty key")
|
testError(t, " ", "empty key")
|
||||||
|
testResult(t, `""`, []string{""})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// TOML lexer.
|
// TOML lexer.
|
||||||
//
|
//
|
||||||
// Written using the principles developped by Rob Pike in
|
// Written using the principles developed by Rob Pike in
|
||||||
// http://www.youtube.com/watch?v=HxaD_trXwRE
|
// http://www.youtube.com/watch?v=HxaD_trXwRE
|
||||||
|
|
||||||
package toml
|
package toml
|
||||||
|
|||||||
+335
-335
@@ -37,260 +37,260 @@ func testFlow(t *testing.T, input string, expectedFlow []token) {
|
|||||||
|
|
||||||
func TestValidKeyGroup(t *testing.T) {
|
func TestValidKeyGroup(t *testing.T) {
|
||||||
testFlow(t, "[hello world]", []token{
|
testFlow(t, "[hello world]", []token{
|
||||||
token{Position{1, 1}, tokenLeftBracket, "["},
|
{Position{1, 1}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 2}, tokenKeyGroup, "hello world"},
|
{Position{1, 2}, tokenKeyGroup, "hello world"},
|
||||||
token{Position{1, 13}, tokenRightBracket, "]"},
|
{Position{1, 13}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 14}, tokenEOF, ""},
|
{Position{1, 14}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNestedQuotedUnicodeKeyGroup(t *testing.T) {
|
func TestNestedQuotedUnicodeKeyGroup(t *testing.T) {
|
||||||
testFlow(t, `[ j . "ʞ" . l ]`, []token{
|
testFlow(t, `[ j . "ʞ" . l ]`, []token{
|
||||||
token{Position{1, 1}, tokenLeftBracket, "["},
|
{Position{1, 1}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 2}, tokenKeyGroup, ` j . "ʞ" . l `},
|
{Position{1, 2}, tokenKeyGroup, ` j . "ʞ" . l `},
|
||||||
token{Position{1, 15}, tokenRightBracket, "]"},
|
{Position{1, 15}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 16}, tokenEOF, ""},
|
{Position{1, 16}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnclosedKeyGroup(t *testing.T) {
|
func TestUnclosedKeyGroup(t *testing.T) {
|
||||||
testFlow(t, "[hello world", []token{
|
testFlow(t, "[hello world", []token{
|
||||||
token{Position{1, 1}, tokenLeftBracket, "["},
|
{Position{1, 1}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 2}, tokenError, "unclosed key group"},
|
{Position{1, 2}, tokenError, "unclosed key group"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestComment(t *testing.T) {
|
func TestComment(t *testing.T) {
|
||||||
testFlow(t, "# blahblah", []token{
|
testFlow(t, "# blahblah", []token{
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyGroupComment(t *testing.T) {
|
func TestKeyGroupComment(t *testing.T) {
|
||||||
testFlow(t, "[hello world] # blahblah", []token{
|
testFlow(t, "[hello world] # blahblah", []token{
|
||||||
token{Position{1, 1}, tokenLeftBracket, "["},
|
{Position{1, 1}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 2}, tokenKeyGroup, "hello world"},
|
{Position{1, 2}, tokenKeyGroup, "hello world"},
|
||||||
token{Position{1, 13}, tokenRightBracket, "]"},
|
{Position{1, 13}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 25}, tokenEOF, ""},
|
{Position{1, 25}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultipleKeyGroupsComment(t *testing.T) {
|
func TestMultipleKeyGroupsComment(t *testing.T) {
|
||||||
testFlow(t, "[hello world] # blahblah\n[test]", []token{
|
testFlow(t, "[hello world] # blahblah\n[test]", []token{
|
||||||
token{Position{1, 1}, tokenLeftBracket, "["},
|
{Position{1, 1}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 2}, tokenKeyGroup, "hello world"},
|
{Position{1, 2}, tokenKeyGroup, "hello world"},
|
||||||
token{Position{1, 13}, tokenRightBracket, "]"},
|
{Position{1, 13}, tokenRightBracket, "]"},
|
||||||
token{Position{2, 1}, tokenLeftBracket, "["},
|
{Position{2, 1}, tokenLeftBracket, "["},
|
||||||
token{Position{2, 2}, tokenKeyGroup, "test"},
|
{Position{2, 2}, tokenKeyGroup, "test"},
|
||||||
token{Position{2, 6}, tokenRightBracket, "]"},
|
{Position{2, 6}, tokenRightBracket, "]"},
|
||||||
token{Position{2, 7}, tokenEOF, ""},
|
{Position{2, 7}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimpleWindowsCRLF(t *testing.T) {
|
func TestSimpleWindowsCRLF(t *testing.T) {
|
||||||
testFlow(t, "a=4\r\nb=2", []token{
|
testFlow(t, "a=4\r\nb=2", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 2}, tokenEqual, "="},
|
{Position{1, 2}, tokenEqual, "="},
|
||||||
token{Position{1, 3}, tokenInteger, "4"},
|
{Position{1, 3}, tokenInteger, "4"},
|
||||||
token{Position{2, 1}, tokenKey, "b"},
|
{Position{2, 1}, tokenKey, "b"},
|
||||||
token{Position{2, 2}, tokenEqual, "="},
|
{Position{2, 2}, tokenEqual, "="},
|
||||||
token{Position{2, 3}, tokenInteger, "2"},
|
{Position{2, 3}, tokenInteger, "2"},
|
||||||
token{Position{2, 4}, tokenEOF, ""},
|
{Position{2, 4}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicKey(t *testing.T) {
|
func TestBasicKey(t *testing.T) {
|
||||||
testFlow(t, "hello", []token{
|
testFlow(t, "hello", []token{
|
||||||
token{Position{1, 1}, tokenKey, "hello"},
|
{Position{1, 1}, tokenKey, "hello"},
|
||||||
token{Position{1, 6}, tokenEOF, ""},
|
{Position{1, 6}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicKeyWithUnderscore(t *testing.T) {
|
func TestBasicKeyWithUnderscore(t *testing.T) {
|
||||||
testFlow(t, "hello_hello", []token{
|
testFlow(t, "hello_hello", []token{
|
||||||
token{Position{1, 1}, tokenKey, "hello_hello"},
|
{Position{1, 1}, tokenKey, "hello_hello"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicKeyWithDash(t *testing.T) {
|
func TestBasicKeyWithDash(t *testing.T) {
|
||||||
testFlow(t, "hello-world", []token{
|
testFlow(t, "hello-world", []token{
|
||||||
token{Position{1, 1}, tokenKey, "hello-world"},
|
{Position{1, 1}, tokenKey, "hello-world"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicKeyWithUppercaseMix(t *testing.T) {
|
func TestBasicKeyWithUppercaseMix(t *testing.T) {
|
||||||
testFlow(t, "helloHELLOHello", []token{
|
testFlow(t, "helloHELLOHello", []token{
|
||||||
token{Position{1, 1}, tokenKey, "helloHELLOHello"},
|
{Position{1, 1}, tokenKey, "helloHELLOHello"},
|
||||||
token{Position{1, 16}, tokenEOF, ""},
|
{Position{1, 16}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicKeyWithInternationalCharacters(t *testing.T) {
|
func TestBasicKeyWithInternationalCharacters(t *testing.T) {
|
||||||
testFlow(t, "héllÖ", []token{
|
testFlow(t, "héllÖ", []token{
|
||||||
token{Position{1, 1}, tokenKey, "héllÖ"},
|
{Position{1, 1}, tokenKey, "héllÖ"},
|
||||||
token{Position{1, 6}, tokenEOF, ""},
|
{Position{1, 6}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicKeyAndEqual(t *testing.T) {
|
func TestBasicKeyAndEqual(t *testing.T) {
|
||||||
testFlow(t, "hello =", []token{
|
testFlow(t, "hello =", []token{
|
||||||
token{Position{1, 1}, tokenKey, "hello"},
|
{Position{1, 1}, tokenKey, "hello"},
|
||||||
token{Position{1, 7}, tokenEqual, "="},
|
{Position{1, 7}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenEOF, ""},
|
{Position{1, 8}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyWithSharpAndEqual(t *testing.T) {
|
func TestKeyWithSharpAndEqual(t *testing.T) {
|
||||||
testFlow(t, "key#name = 5", []token{
|
testFlow(t, "key#name = 5", []token{
|
||||||
token{Position{1, 1}, tokenError, "keys cannot contain # character"},
|
{Position{1, 1}, tokenError, "keys cannot contain # character"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyWithSymbolsAndEqual(t *testing.T) {
|
func TestKeyWithSymbolsAndEqual(t *testing.T) {
|
||||||
testFlow(t, "~!@$^&*()_+-`1234567890[]\\|/?><.,;:' = 5", []token{
|
testFlow(t, "~!@$^&*()_+-`1234567890[]\\|/?><.,;:' = 5", []token{
|
||||||
token{Position{1, 1}, tokenError, "keys cannot contain ~ character"},
|
{Position{1, 1}, tokenError, "keys cannot contain ~ character"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualStringEscape(t *testing.T) {
|
func TestKeyEqualStringEscape(t *testing.T) {
|
||||||
testFlow(t, `foo = "hello\""`, []token{
|
testFlow(t, `foo = "hello\""`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, "hello\""},
|
{Position{1, 8}, tokenString, "hello\""},
|
||||||
token{Position{1, 16}, tokenEOF, ""},
|
{Position{1, 16}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualStringUnfinished(t *testing.T) {
|
func TestKeyEqualStringUnfinished(t *testing.T) {
|
||||||
testFlow(t, `foo = "bar`, []token{
|
testFlow(t, `foo = "bar`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "unclosed string"},
|
{Position{1, 8}, tokenError, "unclosed string"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualString(t *testing.T) {
|
func TestKeyEqualString(t *testing.T) {
|
||||||
testFlow(t, `foo = "bar"`, []token{
|
testFlow(t, `foo = "bar"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, "bar"},
|
{Position{1, 8}, tokenString, "bar"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualTrue(t *testing.T) {
|
func TestKeyEqualTrue(t *testing.T) {
|
||||||
testFlow(t, "foo = true", []token{
|
testFlow(t, "foo = true", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenTrue, "true"},
|
{Position{1, 7}, tokenTrue, "true"},
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualFalse(t *testing.T) {
|
func TestKeyEqualFalse(t *testing.T) {
|
||||||
testFlow(t, "foo = false", []token{
|
testFlow(t, "foo = false", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenFalse, "false"},
|
{Position{1, 7}, tokenFalse, "false"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayNestedString(t *testing.T) {
|
func TestArrayNestedString(t *testing.T) {
|
||||||
testFlow(t, `a = [ ["hello", "world"] ]`, []token{
|
testFlow(t, `a = [ ["hello", "world"] ]`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenLeftBracket, "["},
|
{Position{1, 5}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 7}, tokenLeftBracket, "["},
|
{Position{1, 7}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 9}, tokenString, "hello"},
|
{Position{1, 9}, tokenString, "hello"},
|
||||||
token{Position{1, 15}, tokenComma, ","},
|
{Position{1, 15}, tokenComma, ","},
|
||||||
token{Position{1, 18}, tokenString, "world"},
|
{Position{1, 18}, tokenString, "world"},
|
||||||
token{Position{1, 24}, tokenRightBracket, "]"},
|
{Position{1, 24}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 26}, tokenRightBracket, "]"},
|
{Position{1, 26}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 27}, tokenEOF, ""},
|
{Position{1, 27}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayNestedInts(t *testing.T) {
|
func TestArrayNestedInts(t *testing.T) {
|
||||||
testFlow(t, "a = [ [42, 21], [10] ]", []token{
|
testFlow(t, "a = [ [42, 21], [10] ]", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenLeftBracket, "["},
|
{Position{1, 5}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 7}, tokenLeftBracket, "["},
|
{Position{1, 7}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 8}, tokenInteger, "42"},
|
{Position{1, 8}, tokenInteger, "42"},
|
||||||
token{Position{1, 10}, tokenComma, ","},
|
{Position{1, 10}, tokenComma, ","},
|
||||||
token{Position{1, 12}, tokenInteger, "21"},
|
{Position{1, 12}, tokenInteger, "21"},
|
||||||
token{Position{1, 14}, tokenRightBracket, "]"},
|
{Position{1, 14}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 15}, tokenComma, ","},
|
{Position{1, 15}, tokenComma, ","},
|
||||||
token{Position{1, 17}, tokenLeftBracket, "["},
|
{Position{1, 17}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 18}, tokenInteger, "10"},
|
{Position{1, 18}, tokenInteger, "10"},
|
||||||
token{Position{1, 20}, tokenRightBracket, "]"},
|
{Position{1, 20}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 22}, tokenRightBracket, "]"},
|
{Position{1, 22}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 23}, tokenEOF, ""},
|
{Position{1, 23}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayInts(t *testing.T) {
|
func TestArrayInts(t *testing.T) {
|
||||||
testFlow(t, "a = [ 42, 21, 10, ]", []token{
|
testFlow(t, "a = [ 42, 21, 10, ]", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenLeftBracket, "["},
|
{Position{1, 5}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 7}, tokenInteger, "42"},
|
{Position{1, 7}, tokenInteger, "42"},
|
||||||
token{Position{1, 9}, tokenComma, ","},
|
{Position{1, 9}, tokenComma, ","},
|
||||||
token{Position{1, 11}, tokenInteger, "21"},
|
{Position{1, 11}, tokenInteger, "21"},
|
||||||
token{Position{1, 13}, tokenComma, ","},
|
{Position{1, 13}, tokenComma, ","},
|
||||||
token{Position{1, 15}, tokenInteger, "10"},
|
{Position{1, 15}, tokenInteger, "10"},
|
||||||
token{Position{1, 17}, tokenComma, ","},
|
{Position{1, 17}, tokenComma, ","},
|
||||||
token{Position{1, 19}, tokenRightBracket, "]"},
|
{Position{1, 19}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 20}, tokenEOF, ""},
|
{Position{1, 20}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultilineArrayComments(t *testing.T) {
|
func TestMultilineArrayComments(t *testing.T) {
|
||||||
testFlow(t, "a = [1, # wow\n2, # such items\n3, # so array\n]", []token{
|
testFlow(t, "a = [1, # wow\n2, # such items\n3, # so array\n]", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenLeftBracket, "["},
|
{Position{1, 5}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 6}, tokenInteger, "1"},
|
{Position{1, 6}, tokenInteger, "1"},
|
||||||
token{Position{1, 7}, tokenComma, ","},
|
{Position{1, 7}, tokenComma, ","},
|
||||||
token{Position{2, 1}, tokenInteger, "2"},
|
{Position{2, 1}, tokenInteger, "2"},
|
||||||
token{Position{2, 2}, tokenComma, ","},
|
{Position{2, 2}, tokenComma, ","},
|
||||||
token{Position{3, 1}, tokenInteger, "3"},
|
{Position{3, 1}, tokenInteger, "3"},
|
||||||
token{Position{3, 2}, tokenComma, ","},
|
{Position{3, 2}, tokenComma, ","},
|
||||||
token{Position{4, 1}, tokenRightBracket, "]"},
|
{Position{4, 1}, tokenRightBracket, "]"},
|
||||||
token{Position{4, 2}, tokenEOF, ""},
|
{Position{4, 2}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualArrayBools(t *testing.T) {
|
func TestKeyEqualArrayBools(t *testing.T) {
|
||||||
testFlow(t, "foo = [true, false, true]", []token{
|
testFlow(t, "foo = [true, false, true]", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenLeftBracket, "["},
|
{Position{1, 7}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 8}, tokenTrue, "true"},
|
{Position{1, 8}, tokenTrue, "true"},
|
||||||
token{Position{1, 12}, tokenComma, ","},
|
{Position{1, 12}, tokenComma, ","},
|
||||||
token{Position{1, 14}, tokenFalse, "false"},
|
{Position{1, 14}, tokenFalse, "false"},
|
||||||
token{Position{1, 19}, tokenComma, ","},
|
{Position{1, 19}, tokenComma, ","},
|
||||||
token{Position{1, 21}, tokenTrue, "true"},
|
{Position{1, 21}, tokenTrue, "true"},
|
||||||
token{Position{1, 25}, tokenRightBracket, "]"},
|
{Position{1, 25}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 26}, tokenEOF, ""},
|
{Position{1, 26}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualArrayBoolsWithComments(t *testing.T) {
|
func TestKeyEqualArrayBoolsWithComments(t *testing.T) {
|
||||||
testFlow(t, "foo = [true, false, true] # YEAH", []token{
|
testFlow(t, "foo = [true, false, true] # YEAH", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenLeftBracket, "["},
|
{Position{1, 7}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 8}, tokenTrue, "true"},
|
{Position{1, 8}, tokenTrue, "true"},
|
||||||
token{Position{1, 12}, tokenComma, ","},
|
{Position{1, 12}, tokenComma, ","},
|
||||||
token{Position{1, 14}, tokenFalse, "false"},
|
{Position{1, 14}, tokenFalse, "false"},
|
||||||
token{Position{1, 19}, tokenComma, ","},
|
{Position{1, 19}, tokenComma, ","},
|
||||||
token{Position{1, 21}, tokenTrue, "true"},
|
{Position{1, 21}, tokenTrue, "true"},
|
||||||
token{Position{1, 25}, tokenRightBracket, "]"},
|
{Position{1, 25}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 33}, tokenEOF, ""},
|
{Position{1, 33}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,401 +308,401 @@ func TestDateRegexp(t *testing.T) {
|
|||||||
|
|
||||||
func TestKeyEqualDate(t *testing.T) {
|
func TestKeyEqualDate(t *testing.T) {
|
||||||
testFlow(t, "foo = 1979-05-27T07:32:00Z", []token{
|
testFlow(t, "foo = 1979-05-27T07:32:00Z", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenDate, "1979-05-27T07:32:00Z"},
|
{Position{1, 7}, tokenDate, "1979-05-27T07:32:00Z"},
|
||||||
token{Position{1, 27}, tokenEOF, ""},
|
{Position{1, 27}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, "foo = 1979-05-27T00:32:00-07:00", []token{
|
testFlow(t, "foo = 1979-05-27T00:32:00-07:00", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenDate, "1979-05-27T00:32:00-07:00"},
|
{Position{1, 7}, tokenDate, "1979-05-27T00:32:00-07:00"},
|
||||||
token{Position{1, 32}, tokenEOF, ""},
|
{Position{1, 32}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, "foo = 1979-05-27T00:32:00.999999-07:00", []token{
|
testFlow(t, "foo = 1979-05-27T00:32:00.999999-07:00", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenDate, "1979-05-27T00:32:00.999999-07:00"},
|
{Position{1, 7}, tokenDate, "1979-05-27T00:32:00.999999-07:00"},
|
||||||
token{Position{1, 39}, tokenEOF, ""},
|
{Position{1, 39}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatEndingWithDot(t *testing.T) {
|
func TestFloatEndingWithDot(t *testing.T) {
|
||||||
testFlow(t, "foo = 42.", []token{
|
testFlow(t, "foo = 42.", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenError, "float cannot end with a dot"},
|
{Position{1, 7}, tokenError, "float cannot end with a dot"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatWithTwoDots(t *testing.T) {
|
func TestFloatWithTwoDots(t *testing.T) {
|
||||||
testFlow(t, "foo = 4.2.", []token{
|
testFlow(t, "foo = 4.2.", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenError, "cannot have two dots in one float"},
|
{Position{1, 7}, tokenError, "cannot have two dots in one float"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatWithExponent1(t *testing.T) {
|
func TestFloatWithExponent1(t *testing.T) {
|
||||||
testFlow(t, "a = 5e+22", []token{
|
testFlow(t, "a = 5e+22", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenFloat, "5e+22"},
|
{Position{1, 5}, tokenFloat, "5e+22"},
|
||||||
token{Position{1, 10}, tokenEOF, ""},
|
{Position{1, 10}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatWithExponent2(t *testing.T) {
|
func TestFloatWithExponent2(t *testing.T) {
|
||||||
testFlow(t, "a = 5E+22", []token{
|
testFlow(t, "a = 5E+22", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenFloat, "5E+22"},
|
{Position{1, 5}, tokenFloat, "5E+22"},
|
||||||
token{Position{1, 10}, tokenEOF, ""},
|
{Position{1, 10}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatWithExponent3(t *testing.T) {
|
func TestFloatWithExponent3(t *testing.T) {
|
||||||
testFlow(t, "a = -5e+22", []token{
|
testFlow(t, "a = -5e+22", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenFloat, "-5e+22"},
|
{Position{1, 5}, tokenFloat, "-5e+22"},
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatWithExponent4(t *testing.T) {
|
func TestFloatWithExponent4(t *testing.T) {
|
||||||
testFlow(t, "a = -5e-22", []token{
|
testFlow(t, "a = -5e-22", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenFloat, "-5e-22"},
|
{Position{1, 5}, tokenFloat, "-5e-22"},
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatWithExponent5(t *testing.T) {
|
func TestFloatWithExponent5(t *testing.T) {
|
||||||
testFlow(t, "a = 6.626e-34", []token{
|
testFlow(t, "a = 6.626e-34", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenFloat, "6.626e-34"},
|
{Position{1, 5}, tokenFloat, "6.626e-34"},
|
||||||
token{Position{1, 14}, tokenEOF, ""},
|
{Position{1, 14}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "invalid escape sequence: \\x"},
|
{Position{1, 8}, tokenError, "invalid escape sequence: \\x"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNestedArrays(t *testing.T) {
|
func TestNestedArrays(t *testing.T) {
|
||||||
testFlow(t, "foo = [[[]]]", []token{
|
testFlow(t, "foo = [[[]]]", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenLeftBracket, "["},
|
{Position{1, 7}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 8}, tokenLeftBracket, "["},
|
{Position{1, 8}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 9}, tokenLeftBracket, "["},
|
{Position{1, 9}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 10}, tokenRightBracket, "]"},
|
{Position{1, 10}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 11}, tokenRightBracket, "]"},
|
{Position{1, 11}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 12}, tokenRightBracket, "]"},
|
{Position{1, 12}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 13}, tokenEOF, ""},
|
{Position{1, 13}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualNumber(t *testing.T) {
|
func TestKeyEqualNumber(t *testing.T) {
|
||||||
testFlow(t, "foo = 42", []token{
|
testFlow(t, "foo = 42", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "42"},
|
{Position{1, 7}, tokenInteger, "42"},
|
||||||
token{Position{1, 9}, tokenEOF, ""},
|
{Position{1, 9}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = +42", []token{
|
testFlow(t, "foo = +42", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "+42"},
|
{Position{1, 7}, tokenInteger, "+42"},
|
||||||
token{Position{1, 10}, tokenEOF, ""},
|
{Position{1, 10}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = -42", []token{
|
testFlow(t, "foo = -42", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "-42"},
|
{Position{1, 7}, tokenInteger, "-42"},
|
||||||
token{Position{1, 10}, tokenEOF, ""},
|
{Position{1, 10}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = 4.2", []token{
|
testFlow(t, "foo = 4.2", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenFloat, "4.2"},
|
{Position{1, 7}, tokenFloat, "4.2"},
|
||||||
token{Position{1, 10}, tokenEOF, ""},
|
{Position{1, 10}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = +4.2", []token{
|
testFlow(t, "foo = +4.2", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenFloat, "+4.2"},
|
{Position{1, 7}, tokenFloat, "+4.2"},
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = -4.2", []token{
|
testFlow(t, "foo = -4.2", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenFloat, "-4.2"},
|
{Position{1, 7}, tokenFloat, "-4.2"},
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = 1_000", []token{
|
testFlow(t, "foo = 1_000", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "1_000"},
|
{Position{1, 7}, tokenInteger, "1_000"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = 5_349_221", []token{
|
testFlow(t, "foo = 5_349_221", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "5_349_221"},
|
{Position{1, 7}, tokenInteger, "5_349_221"},
|
||||||
token{Position{1, 16}, tokenEOF, ""},
|
{Position{1, 16}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = 1_2_3_4_5", []token{
|
testFlow(t, "foo = 1_2_3_4_5", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "1_2_3_4_5"},
|
{Position{1, 7}, tokenInteger, "1_2_3_4_5"},
|
||||||
token{Position{1, 16}, tokenEOF, ""},
|
{Position{1, 16}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "flt8 = 9_224_617.445_991_228_313", []token{
|
testFlow(t, "flt8 = 9_224_617.445_991_228_313", []token{
|
||||||
token{Position{1, 1}, tokenKey, "flt8"},
|
{Position{1, 1}, tokenKey, "flt8"},
|
||||||
token{Position{1, 6}, tokenEqual, "="},
|
{Position{1, 6}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenFloat, "9_224_617.445_991_228_313"},
|
{Position{1, 8}, tokenFloat, "9_224_617.445_991_228_313"},
|
||||||
token{Position{1, 33}, tokenEOF, ""},
|
{Position{1, 33}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = +", []token{
|
testFlow(t, "foo = +", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenError, "no digit in that number"},
|
{Position{1, 7}, tokenError, "no digit in that number"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiline(t *testing.T) {
|
func TestMultiline(t *testing.T) {
|
||||||
testFlow(t, "foo = 42\nbar=21", []token{
|
testFlow(t, "foo = 42\nbar=21", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 7}, tokenInteger, "42"},
|
{Position{1, 7}, tokenInteger, "42"},
|
||||||
token{Position{2, 1}, tokenKey, "bar"},
|
{Position{2, 1}, tokenKey, "bar"},
|
||||||
token{Position{2, 4}, tokenEqual, "="},
|
{Position{2, 4}, tokenEqual, "="},
|
||||||
token{Position{2, 5}, tokenInteger, "21"},
|
{Position{2, 5}, tokenInteger, "21"},
|
||||||
token{Position{2, 7}, tokenEOF, ""},
|
{Position{2, 7}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualStringUnicodeEscape(t *testing.T) {
|
func TestKeyEqualStringUnicodeEscape(t *testing.T) {
|
||||||
testFlow(t, `foo = "hello \u2665"`, []token{
|
testFlow(t, `foo = "hello \u2665"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, "hello ♥"},
|
{Position{1, 8}, tokenString, "hello ♥"},
|
||||||
token{Position{1, 21}, tokenEOF, ""},
|
{Position{1, 21}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = "hello \U000003B4"`, []token{
|
testFlow(t, `foo = "hello \U000003B4"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, "hello δ"},
|
{Position{1, 8}, tokenString, "hello δ"},
|
||||||
token{Position{1, 25}, tokenEOF, ""},
|
{Position{1, 25}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = "\u2"`, []token{
|
testFlow(t, `foo = "\u2"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "unfinished unicode escape"},
|
{Position{1, 8}, tokenError, "unfinished unicode escape"},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = "\U2"`, []token{
|
testFlow(t, `foo = "\U2"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "unfinished unicode escape"},
|
{Position{1, 8}, tokenError, "unfinished unicode escape"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualStringNoEscape(t *testing.T) {
|
func TestKeyEqualStringNoEscape(t *testing.T) {
|
||||||
testFlow(t, "foo = \"hello \u0002\"", []token{
|
testFlow(t, "foo = \"hello \u0002\"", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "unescaped control character U+0002"},
|
{Position{1, 8}, tokenError, "unescaped control character U+0002"},
|
||||||
})
|
})
|
||||||
testFlow(t, "foo = \"hello \u001F\"", []token{
|
testFlow(t, "foo = \"hello \u001F\"", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "unescaped control character U+001F"},
|
{Position{1, 8}, tokenError, "unescaped control character U+001F"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLiteralString(t *testing.T) {
|
func TestLiteralString(t *testing.T) {
|
||||||
testFlow(t, `foo = 'C:\Users\nodejs\templates'`, []token{
|
testFlow(t, `foo = 'C:\Users\nodejs\templates'`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, `C:\Users\nodejs\templates`},
|
{Position{1, 8}, tokenString, `C:\Users\nodejs\templates`},
|
||||||
token{Position{1, 34}, tokenEOF, ""},
|
{Position{1, 34}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = '\\ServerX\admin$\system32\'`, []token{
|
testFlow(t, `foo = '\\ServerX\admin$\system32\'`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, `\\ServerX\admin$\system32\`},
|
{Position{1, 8}, tokenString, `\\ServerX\admin$\system32\`},
|
||||||
token{Position{1, 35}, tokenEOF, ""},
|
{Position{1, 35}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = 'Tom "Dubs" Preston-Werner'`, []token{
|
testFlow(t, `foo = 'Tom "Dubs" Preston-Werner'`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, `Tom "Dubs" Preston-Werner`},
|
{Position{1, 8}, tokenString, `Tom "Dubs" Preston-Werner`},
|
||||||
token{Position{1, 34}, tokenEOF, ""},
|
{Position{1, 34}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = '<\i\c*\s*>'`, []token{
|
testFlow(t, `foo = '<\i\c*\s*>'`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, `<\i\c*\s*>`},
|
{Position{1, 8}, tokenString, `<\i\c*\s*>`},
|
||||||
token{Position{1, 19}, tokenEOF, ""},
|
{Position{1, 19}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, `foo = 'C:\Users\nodejs\unfinis`, []token{
|
testFlow(t, `foo = 'C:\Users\nodejs\unfinis`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenError, "unclosed string"},
|
{Position{1, 8}, tokenError, "unclosed string"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultilineLiteralString(t *testing.T) {
|
func TestMultilineLiteralString(t *testing.T) {
|
||||||
testFlow(t, `foo = '''hello 'literal' world'''`, []token{
|
testFlow(t, `foo = '''hello 'literal' world'''`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 10}, tokenString, `hello 'literal' world`},
|
{Position{1, 10}, tokenString, `hello 'literal' world`},
|
||||||
token{Position{1, 34}, tokenEOF, ""},
|
{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"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{2, 1}, tokenString, "hello\n'literal'\nworld"},
|
{Position{2, 1}, tokenString, "hello\n'literal'\nworld"},
|
||||||
token{Position{4, 9}, tokenEOF, ""},
|
{Position{4, 9}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
testFlow(t, "foo = '''\r\nhello\r\n'literal'\r\nworld'''", []token{
|
testFlow(t, "foo = '''\r\nhello\r\n'literal'\r\nworld'''", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{2, 1}, tokenString, "hello\r\n'literal'\r\nworld"},
|
{Position{2, 1}, tokenString, "hello\r\n'literal'\r\nworld"},
|
||||||
token{Position{4, 9}, tokenEOF, ""},
|
{Position{4, 9}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultilineString(t *testing.T) {
|
func TestMultilineString(t *testing.T) {
|
||||||
testFlow(t, `foo = """hello "literal" world"""`, []token{
|
testFlow(t, `foo = """hello "literal" world"""`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 10}, tokenString, `hello "literal" world`},
|
{Position{1, 10}, tokenString, `hello "literal" world`},
|
||||||
token{Position{1, 34}, tokenEOF, ""},
|
{Position{1, 34}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "foo = \"\"\"\r\nhello\\\r\n\"literal\"\\\nworld\"\"\"", []token{
|
testFlow(t, "foo = \"\"\"\r\nhello\\\r\n\"literal\"\\\nworld\"\"\"", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{2, 1}, tokenString, "hello\"literal\"world"},
|
{Position{2, 1}, tokenString, "hello\"literal\"world"},
|
||||||
token{Position{4, 9}, tokenEOF, ""},
|
{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"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 10}, tokenString, "hellomultilineworld"},
|
{Position{1, 10}, tokenString, "hellomultilineworld"},
|
||||||
token{Position{6, 9}, tokenEOF, ""},
|
{Position{6, 9}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "key2 = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"", []token{
|
testFlow(t, "key2 = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"", []token{
|
||||||
token{Position{1, 1}, tokenKey, "key2"},
|
{Position{1, 1}, tokenKey, "key2"},
|
||||||
token{Position{1, 6}, tokenEqual, "="},
|
{Position{1, 6}, tokenEqual, "="},
|
||||||
token{Position{2, 1}, tokenString, "The quick brown fox jumps over the lazy dog."},
|
{Position{2, 1}, tokenString, "The quick brown fox jumps over the lazy dog."},
|
||||||
token{Position{6, 21}, tokenEOF, ""},
|
{Position{6, 21}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "key2 = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", []token{
|
testFlow(t, "key2 = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", []token{
|
||||||
token{Position{1, 1}, tokenKey, "key2"},
|
{Position{1, 1}, tokenKey, "key2"},
|
||||||
token{Position{1, 6}, tokenEqual, "="},
|
{Position{1, 6}, tokenEqual, "="},
|
||||||
token{Position{1, 11}, tokenString, "The quick brown fox jumps over the lazy dog."},
|
{Position{1, 11}, tokenString, "The quick brown fox jumps over the lazy dog."},
|
||||||
token{Position{5, 11}, tokenEOF, ""},
|
{Position{5, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, `key2 = "Roses are red\nViolets are blue"`, []token{
|
testFlow(t, `key2 = "Roses are red\nViolets are blue"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "key2"},
|
{Position{1, 1}, tokenKey, "key2"},
|
||||||
token{Position{1, 6}, tokenEqual, "="},
|
{Position{1, 6}, tokenEqual, "="},
|
||||||
token{Position{1, 9}, tokenString, "Roses are red\nViolets are blue"},
|
{Position{1, 9}, tokenString, "Roses are red\nViolets are blue"},
|
||||||
token{Position{1, 41}, tokenEOF, ""},
|
{Position{1, 41}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, "key2 = \"\"\"\nRoses are red\nViolets are blue\"\"\"", []token{
|
testFlow(t, "key2 = \"\"\"\nRoses are red\nViolets are blue\"\"\"", []token{
|
||||||
token{Position{1, 1}, tokenKey, "key2"},
|
{Position{1, 1}, tokenKey, "key2"},
|
||||||
token{Position{1, 6}, tokenEqual, "="},
|
{Position{1, 6}, tokenEqual, "="},
|
||||||
token{Position{2, 1}, tokenString, "Roses are red\nViolets are blue"},
|
{Position{2, 1}, tokenString, "Roses are red\nViolets are blue"},
|
||||||
token{Position{3, 20}, tokenEOF, ""},
|
{Position{3, 20}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnicodeString(t *testing.T) {
|
func TestUnicodeString(t *testing.T) {
|
||||||
testFlow(t, `foo = "hello ♥ world"`, []token{
|
testFlow(t, `foo = "hello ♥ world"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, "hello ♥ world"},
|
{Position{1, 8}, tokenString, "hello ♥ world"},
|
||||||
token{Position{1, 22}, tokenEOF, ""},
|
{Position{1, 22}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func TestEscapeInString(t *testing.T) {
|
func TestEscapeInString(t *testing.T) {
|
||||||
testFlow(t, `foo = "\b\f\/"`, []token{
|
testFlow(t, `foo = "\b\f\/"`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenEqual, "="},
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
token{Position{1, 8}, tokenString, "\b\f/"},
|
{Position{1, 8}, tokenString, "\b\f/"},
|
||||||
token{Position{1, 15}, tokenEOF, ""},
|
{Position{1, 15}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyGroupArray(t *testing.T) {
|
func TestKeyGroupArray(t *testing.T) {
|
||||||
testFlow(t, "[[foo]]", []token{
|
testFlow(t, "[[foo]]", []token{
|
||||||
token{Position{1, 1}, tokenDoubleLeftBracket, "[["},
|
{Position{1, 1}, tokenDoubleLeftBracket, "[["},
|
||||||
token{Position{1, 3}, tokenKeyGroupArray, "foo"},
|
{Position{1, 3}, tokenKeyGroupArray, "foo"},
|
||||||
token{Position{1, 6}, tokenDoubleRightBracket, "]]"},
|
{Position{1, 6}, tokenDoubleRightBracket, "]]"},
|
||||||
token{Position{1, 8}, tokenEOF, ""},
|
{Position{1, 8}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQuotedKey(t *testing.T) {
|
func TestQuotedKey(t *testing.T) {
|
||||||
testFlow(t, "\"a b\" = 42", []token{
|
testFlow(t, "\"a b\" = 42", []token{
|
||||||
token{Position{1, 1}, tokenKey, "\"a b\""},
|
{Position{1, 1}, tokenKey, "\"a b\""},
|
||||||
token{Position{1, 7}, tokenEqual, "="},
|
{Position{1, 7}, tokenEqual, "="},
|
||||||
token{Position{1, 9}, tokenInteger, "42"},
|
{Position{1, 9}, tokenInteger, "42"},
|
||||||
token{Position{1, 11}, tokenEOF, ""},
|
{Position{1, 11}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyNewline(t *testing.T) {
|
func TestKeyNewline(t *testing.T) {
|
||||||
testFlow(t, "a\n= 4", []token{
|
testFlow(t, "a\n= 4", []token{
|
||||||
token{Position{1, 1}, tokenError, "keys cannot contain new lines"},
|
{Position{1, 1}, tokenError, "keys cannot contain new lines"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidFloat(t *testing.T) {
|
func TestInvalidFloat(t *testing.T) {
|
||||||
testFlow(t, "a=7e1_", []token{
|
testFlow(t, "a=7e1_", []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 2}, tokenEqual, "="},
|
{Position{1, 2}, tokenEqual, "="},
|
||||||
token{Position{1, 3}, tokenFloat, "7e1_"},
|
{Position{1, 3}, tokenFloat, "7e1_"},
|
||||||
token{Position{1, 7}, tokenEOF, ""},
|
{Position{1, 7}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexUnknownRvalue(t *testing.T) {
|
func TestLexUnknownRvalue(t *testing.T) {
|
||||||
testFlow(t, `a = !b`, []token{
|
testFlow(t, `a = !b`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenError, "no value can start with !"},
|
{Position{1, 5}, tokenError, "no value can start with !"},
|
||||||
})
|
})
|
||||||
|
|
||||||
testFlow(t, `a = \b`, []token{
|
testFlow(t, `a = \b`, []token{
|
||||||
token{Position{1, 1}, tokenKey, "a"},
|
{Position{1, 1}, tokenKey, "a"},
|
||||||
token{Position{1, 3}, tokenEqual, "="},
|
{Position{1, 3}, tokenEqual, "="},
|
||||||
token{Position{1, 5}, tokenError, `no value can start with \`},
|
{Position{1, 5}, tokenError, `no value can start with \`},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ func (p *tomlParser) parseAssign() tomlParserStateFn {
|
|||||||
var toInsert interface{}
|
var toInsert interface{}
|
||||||
|
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
case *TomlTree:
|
case *TomlTree, []*TomlTree:
|
||||||
toInsert = value
|
toInsert = value
|
||||||
default:
|
default:
|
||||||
toInsert = &tomlValue{value, key.Position}
|
toInsert = &tomlValue{value, key.Position}
|
||||||
|
|||||||
+65
-22
@@ -177,6 +177,16 @@ func TestStringEscapables(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEmptyQuotedString(t *testing.T) {
|
||||||
|
tree, err := Load(`[""]
|
||||||
|
"" = 1`)
|
||||||
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
|
"": map[string]interface{}{
|
||||||
|
"": int64(1),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestBools(t *testing.T) {
|
func TestBools(t *testing.T) {
|
||||||
tree, err := Load("a = true\nb = false")
|
tree, err := Load("a = true\nb = false")
|
||||||
assertTree(t, tree, err, map[string]interface{}{
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
@@ -269,14 +279,14 @@ func TestArrayMultiline(t *testing.T) {
|
|||||||
func TestArrayNested(t *testing.T) {
|
func TestArrayNested(t *testing.T) {
|
||||||
tree, err := Load("a = [[42, 21], [10]]")
|
tree, err := Load("a = [[42, 21], [10]]")
|
||||||
assertTree(t, tree, err, map[string]interface{}{
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
"a": [][]int64{[]int64{int64(42), int64(21)}, []int64{int64(10)}},
|
"a": [][]int64{{int64(42), int64(21)}, {int64(10)}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNestedEmptyArrays(t *testing.T) {
|
func TestNestedEmptyArrays(t *testing.T) {
|
||||||
tree, err := Load("a = [[[]]]")
|
tree, err := Load("a = [[[]]]")
|
||||||
assertTree(t, tree, err, map[string]interface{}{
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
"a": [][][]interface{}{[][]interface{}{[]interface{}{}}},
|
"a": [][][]interface{}{{{}}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +305,7 @@ func TestArrayMixedTypes(t *testing.T) {
|
|||||||
func TestArrayNestedStrings(t *testing.T) {
|
func TestArrayNestedStrings(t *testing.T) {
|
||||||
tree, err := Load("data = [ [\"gamma\", \"delta\"], [\"Foo\"] ]")
|
tree, err := Load("data = [ [\"gamma\", \"delta\"], [\"Foo\"] ]")
|
||||||
assertTree(t, tree, err, map[string]interface{}{
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
"data": [][]string{[]string{"gamma", "delta"}, []string{"Foo"}},
|
"data": [][]string{{"gamma", "delta"}, {"Foo"}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +404,7 @@ func TestExampleInlineGroupInArray(t *testing.T) {
|
|||||||
tree, err := Load(`points = [{ x = 1, y = 2 }]`)
|
tree, err := Load(`points = [{ x = 1, y = 2 }]`)
|
||||||
assertTree(t, tree, err, map[string]interface{}{
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
"points": []map[string]interface{}{
|
"points": []map[string]interface{}{
|
||||||
map[string]interface{}{
|
{
|
||||||
"x": int64(1),
|
"x": int64(1),
|
||||||
"y": int64(2),
|
"y": int64(2),
|
||||||
},
|
},
|
||||||
@@ -446,7 +456,7 @@ func TestDuplicateKeys(t *testing.T) {
|
|||||||
|
|
||||||
func TestEmptyIntermediateTable(t *testing.T) {
|
func TestEmptyIntermediateTable(t *testing.T) {
|
||||||
_, err := Load("[foo..bar]")
|
_, err := Load("[foo..bar]")
|
||||||
if err.Error() != "(1, 2): empty intermediate table" {
|
if err.Error() != "(1, 2): invalid group array key: empty key group" {
|
||||||
t.Error("Bad error message:", err.Error())
|
t.Error("Bad error message:", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,7 +627,17 @@ func TestToTomlValue(t *testing.T) {
|
|||||||
Value interface{}
|
Value interface{}
|
||||||
Expect string
|
Expect string
|
||||||
}{
|
}{
|
||||||
|
{int(1), "1"},
|
||||||
|
{int8(2), "2"},
|
||||||
|
{int16(3), "3"},
|
||||||
|
{int32(4), "4"},
|
||||||
{int64(12345), "12345"},
|
{int64(12345), "12345"},
|
||||||
|
{uint(10), "10"},
|
||||||
|
{uint8(20), "20"},
|
||||||
|
{uint16(30), "30"},
|
||||||
|
{uint32(40), "40"},
|
||||||
|
{uint64(50), "50"},
|
||||||
|
{float32(12.456), "12.456"},
|
||||||
{float64(123.45), "123.45"},
|
{float64(123.45), "123.45"},
|
||||||
{bool(true), "true"},
|
{bool(true), "true"},
|
||||||
{"hello world", "\"hello world\""},
|
{"hello world", "\"hello world\""},
|
||||||
@@ -627,6 +647,7 @@ func TestToTomlValue(t *testing.T) {
|
|||||||
"1979-05-27T07:32:00Z"},
|
"1979-05-27T07:32:00Z"},
|
||||||
{[]interface{}{"gamma", "delta"},
|
{[]interface{}{"gamma", "delta"},
|
||||||
"[\n \"gamma\",\n \"delta\",\n]"},
|
"[\n \"gamma\",\n \"delta\",\n]"},
|
||||||
|
{nil, ""},
|
||||||
} {
|
} {
|
||||||
result := toTomlValue(item.Value, 0)
|
result := toTomlValue(item.Value, 0)
|
||||||
if result != item.Expect {
|
if result != item.Expect {
|
||||||
@@ -648,6 +669,28 @@ func TestToString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToStringMapStringString(t *testing.T) {
|
||||||
|
in := map[string]interface{}{"m": map[string]string{"v": "abc"}}
|
||||||
|
want := "\n[m]\n v = \"abc\"\n"
|
||||||
|
tree := TreeFromMap(in)
|
||||||
|
got := tree.String()
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("want:\n%q\ngot:\n%q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToStringMapInterfaceInterface(t *testing.T) {
|
||||||
|
in := map[string]interface{}{"m": map[interface{}]interface{}{"v": "abc"}}
|
||||||
|
want := "\n[m]\n v = \"abc\"\n"
|
||||||
|
tree := TreeFromMap(in)
|
||||||
|
got := tree.String()
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("want:\n%q\ngot:\n%q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func assertPosition(t *testing.T, text string, ref map[string]Position) {
|
func assertPosition(t *testing.T, text string, ref map[string]Position) {
|
||||||
tree, err := Load(text)
|
tree, err := Load(text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -668,10 +711,10 @@ func TestDocumentPositions(t *testing.T) {
|
|||||||
assertPosition(t,
|
assertPosition(t,
|
||||||
"[foo]\nbar=42\nbaz=69",
|
"[foo]\nbar=42\nbaz=69",
|
||||||
map[string]Position{
|
map[string]Position{
|
||||||
"": Position{1, 1},
|
"": {1, 1},
|
||||||
"foo": Position{1, 1},
|
"foo": {1, 1},
|
||||||
"foo.bar": Position{2, 1},
|
"foo.bar": {2, 1},
|
||||||
"foo.baz": Position{3, 1},
|
"foo.baz": {3, 1},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,10 +722,10 @@ func TestDocumentPositionsWithSpaces(t *testing.T) {
|
|||||||
assertPosition(t,
|
assertPosition(t,
|
||||||
" [foo]\n bar=42\n baz=69",
|
" [foo]\n bar=42\n baz=69",
|
||||||
map[string]Position{
|
map[string]Position{
|
||||||
"": Position{1, 1},
|
"": {1, 1},
|
||||||
"foo": Position{1, 3},
|
"foo": {1, 3},
|
||||||
"foo.bar": Position{2, 3},
|
"foo.bar": {2, 3},
|
||||||
"foo.baz": Position{3, 3},
|
"foo.baz": {3, 3},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,10 +733,10 @@ func TestDocumentPositionsWithGroupArray(t *testing.T) {
|
|||||||
assertPosition(t,
|
assertPosition(t,
|
||||||
"[[foo]]\nbar=42\nbaz=69",
|
"[[foo]]\nbar=42\nbaz=69",
|
||||||
map[string]Position{
|
map[string]Position{
|
||||||
"": Position{1, 1},
|
"": {1, 1},
|
||||||
"foo": Position{1, 1},
|
"foo": {1, 1},
|
||||||
"foo.bar": Position{2, 1},
|
"foo.bar": {2, 1},
|
||||||
"foo.baz": Position{3, 1},
|
"foo.baz": {3, 1},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,11 +744,11 @@ func TestNestedTreePosition(t *testing.T) {
|
|||||||
assertPosition(t,
|
assertPosition(t,
|
||||||
"[foo.bar]\na=42\nb=69",
|
"[foo.bar]\na=42\nb=69",
|
||||||
map[string]Position{
|
map[string]Position{
|
||||||
"": Position{1, 1},
|
"": {1, 1},
|
||||||
"foo": Position{1, 1},
|
"foo": {1, 1},
|
||||||
"foo.bar": Position{1, 1},
|
"foo.bar": {1, 1},
|
||||||
"foo.bar.a": Position{2, 1},
|
"foo.bar.a": {2, 1},
|
||||||
"foo.bar.b": Position{3, 1},
|
"foo.bar.b": {3, 1},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -18,12 +18,12 @@ type Position struct {
|
|||||||
|
|
||||||
// String representation of the position.
|
// String representation of the position.
|
||||||
// Displays 1-indexed line and column numbers.
|
// Displays 1-indexed line and column numbers.
|
||||||
func (p *Position) String() string {
|
func (p Position) String() string {
|
||||||
return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
|
return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid returns whether or not the position is valid (i.e. with negative or
|
// Invalid returns whether or not the position is valid (i.e. with negative or
|
||||||
// null values)
|
// null values)
|
||||||
func (p *Position) Invalid() bool {
|
func (p Position) Invalid() bool {
|
||||||
return p.Line <= 0 || p.Col <= 0
|
return p.Line <= 0 || p.Col <= 0
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -18,9 +18,9 @@ func TestPositionString(t *testing.T) {
|
|||||||
|
|
||||||
func TestInvalid(t *testing.T) {
|
func TestInvalid(t *testing.T) {
|
||||||
for i, v := range []Position{
|
for i, v := range []Position{
|
||||||
Position{0, 1234},
|
{0, 1234},
|
||||||
Position{1234, 0},
|
{1234, 0},
|
||||||
Position{0, 0},
|
{0, 0},
|
||||||
} {
|
} {
|
||||||
if !v.Invalid() {
|
if !v.Invalid() {
|
||||||
t.Errorf("Position at %v is valid: %v", i, v)
|
t.Errorf("Position at %v is valid: %v", i, v)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (r *QueryResult) appendResult(node interface{}, pos Position) {
|
|||||||
// Values is a set of values within a QueryResult. The order of values is not
|
// Values is a set of values within a QueryResult. The order of values is not
|
||||||
// guaranteed to be in document order, and may be different each time a query is
|
// guaranteed to be in document order, and may be different each time a query is
|
||||||
// executed.
|
// executed.
|
||||||
func (r *QueryResult) Values() []interface{} {
|
func (r QueryResult) Values() []interface{} {
|
||||||
values := make([]interface{}, len(r.items))
|
values := make([]interface{}, len(r.items))
|
||||||
for i, v := range r.items {
|
for i, v := range r.items {
|
||||||
o, ok := v.(*tomlValue)
|
o, ok := v.(*tomlValue)
|
||||||
@@ -45,7 +45,7 @@ func (r *QueryResult) Values() []interface{} {
|
|||||||
|
|
||||||
// Positions is a set of positions for values within a QueryResult. Each index
|
// Positions is a set of positions for values within a QueryResult. Each index
|
||||||
// in Positions() corresponds to the entry in Value() of the same index.
|
// in Positions() corresponds to the entry in Value() of the same index.
|
||||||
func (r *QueryResult) Positions() []Position {
|
func (r QueryResult) Positions() []Position {
|
||||||
return r.positions
|
return r.positions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -272,6 +272,23 @@ func (l *queryLexer) lexString() queryLexStateFn {
|
|||||||
return l.errorf("invalid unicode escape: \\u" + code)
|
return l.errorf("invalid unicode escape: \\u" + code)
|
||||||
}
|
}
|
||||||
growingString += string(rune(intcode))
|
growingString += string(rune(intcode))
|
||||||
|
} else if l.follow("\\U") {
|
||||||
|
l.pos += 2
|
||||||
|
code := ""
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
c := l.peek()
|
||||||
|
l.pos++
|
||||||
|
if !isHexDigit(c) {
|
||||||
|
return l.errorf("unfinished unicode escape")
|
||||||
|
}
|
||||||
|
code = code + string(c)
|
||||||
|
}
|
||||||
|
l.pos--
|
||||||
|
intcode, err := strconv.ParseInt(code, 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
return l.errorf("invalid unicode escape: \\u" + code)
|
||||||
|
}
|
||||||
|
growingString += string(rune(intcode))
|
||||||
} else if l.follow("\\") {
|
} else if l.follow("\\") {
|
||||||
l.pos++
|
l.pos++
|
||||||
return l.errorf("invalid escape sequence: \\" + string(l.peek()))
|
return l.errorf("invalid escape sequence: \\" + string(l.peek()))
|
||||||
|
|||||||
+115
-34
@@ -10,11 +10,13 @@ func testQLFlow(t *testing.T, input string, expectedFlow []token) {
|
|||||||
token := <-ch
|
token := <-ch
|
||||||
if token != expected {
|
if token != expected {
|
||||||
t.Log("While testing #", idx, ":", input)
|
t.Log("While testing #", idx, ":", input)
|
||||||
|
t.Log("compared (got)", token, "to (expected)", expected)
|
||||||
|
t.Log("\tvalue:", token.val, "<->", expected.val)
|
||||||
|
t.Log("\tvalue as bytes:", []byte(token.val), "<->", []byte(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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,64 +36,143 @@ func testQLFlow(t *testing.T, input string, expectedFlow []token) {
|
|||||||
|
|
||||||
func TestLexSpecialChars(t *testing.T) {
|
func TestLexSpecialChars(t *testing.T) {
|
||||||
testQLFlow(t, " .$[]..()?*", []token{
|
testQLFlow(t, " .$[]..()?*", []token{
|
||||||
token{Position{1, 2}, tokenDot, "."},
|
{Position{1, 2}, tokenDot, "."},
|
||||||
token{Position{1, 3}, tokenDollar, "$"},
|
{Position{1, 3}, tokenDollar, "$"},
|
||||||
token{Position{1, 4}, tokenLeftBracket, "["},
|
{Position{1, 4}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 5}, tokenRightBracket, "]"},
|
{Position{1, 5}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 6}, tokenDotDot, ".."},
|
{Position{1, 6}, tokenDotDot, ".."},
|
||||||
token{Position{1, 8}, tokenLeftParen, "("},
|
{Position{1, 8}, tokenLeftParen, "("},
|
||||||
token{Position{1, 9}, tokenRightParen, ")"},
|
{Position{1, 9}, tokenRightParen, ")"},
|
||||||
token{Position{1, 10}, tokenQuestion, "?"},
|
{Position{1, 10}, tokenQuestion, "?"},
|
||||||
token{Position{1, 11}, tokenStar, "*"},
|
{Position{1, 11}, tokenStar, "*"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexString(t *testing.T) {
|
func TestLexString(t *testing.T) {
|
||||||
testQLFlow(t, "'foo'", []token{
|
testQLFlow(t, "'foo\n'", []token{
|
||||||
token{Position{1, 2}, tokenString, "foo"},
|
{Position{1, 2}, tokenString, "foo\n"},
|
||||||
token{Position{1, 6}, tokenEOF, ""},
|
{Position{2, 2}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexDoubleString(t *testing.T) {
|
func TestLexDoubleString(t *testing.T) {
|
||||||
testQLFlow(t, `"bar"`, []token{
|
testQLFlow(t, `"bar"`, []token{
|
||||||
token{Position{1, 2}, tokenString, "bar"},
|
{Position{1, 2}, tokenString, "bar"},
|
||||||
token{Position{1, 6}, tokenEOF, ""},
|
{Position{1, 6}, tokenEOF, ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexStringEscapes(t *testing.T) {
|
||||||
|
testQLFlow(t, `"foo \" \' \b \f \/ \t \r \\ \u03A9 \U00012345 \n bar"`, []token{
|
||||||
|
{Position{1, 2}, tokenString, "foo \" ' \b \f / \t \r \\ \u03A9 \U00012345 \n bar"},
|
||||||
|
{Position{1, 55}, tokenEOF, ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexStringUnfinishedUnicode4(t *testing.T) {
|
||||||
|
testQLFlow(t, `"\u000"`, []token{
|
||||||
|
{Position{1, 2}, tokenError, "unfinished unicode escape"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexStringUnfinishedUnicode8(t *testing.T) {
|
||||||
|
testQLFlow(t, `"\U0000"`, []token{
|
||||||
|
{Position{1, 2}, tokenError, "unfinished unicode escape"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexStringInvalidEscape(t *testing.T) {
|
||||||
|
testQLFlow(t, `"\x"`, []token{
|
||||||
|
{Position{1, 2}, tokenError, "invalid escape sequence: \\x"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexStringUnfinished(t *testing.T) {
|
||||||
|
testQLFlow(t, `"bar`, []token{
|
||||||
|
{Position{1, 2}, tokenError, "unclosed string"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexKey(t *testing.T) {
|
func TestLexKey(t *testing.T) {
|
||||||
testQLFlow(t, "foo", []token{
|
testQLFlow(t, "foo", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 4}, tokenEOF, ""},
|
{Position{1, 4}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexRecurse(t *testing.T) {
|
func TestLexRecurse(t *testing.T) {
|
||||||
testQLFlow(t, "$..*", []token{
|
testQLFlow(t, "$..*", []token{
|
||||||
token{Position{1, 1}, tokenDollar, "$"},
|
{Position{1, 1}, tokenDollar, "$"},
|
||||||
token{Position{1, 2}, tokenDotDot, ".."},
|
{Position{1, 2}, tokenDotDot, ".."},
|
||||||
token{Position{1, 4}, tokenStar, "*"},
|
{Position{1, 4}, tokenStar, "*"},
|
||||||
token{Position{1, 5}, tokenEOF, ""},
|
{Position{1, 5}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexBracketKey(t *testing.T) {
|
func TestLexBracketKey(t *testing.T) {
|
||||||
testQLFlow(t, "$[foo]", []token{
|
testQLFlow(t, "$[foo]", []token{
|
||||||
token{Position{1, 1}, tokenDollar, "$"},
|
{Position{1, 1}, tokenDollar, "$"},
|
||||||
token{Position{1, 2}, tokenLeftBracket, "["},
|
{Position{1, 2}, tokenLeftBracket, "["},
|
||||||
token{Position{1, 3}, tokenKey, "foo"},
|
{Position{1, 3}, tokenKey, "foo"},
|
||||||
token{Position{1, 6}, tokenRightBracket, "]"},
|
{Position{1, 6}, tokenRightBracket, "]"},
|
||||||
token{Position{1, 7}, tokenEOF, ""},
|
{Position{1, 7}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLexSpace(t *testing.T) {
|
func TestLexSpace(t *testing.T) {
|
||||||
testQLFlow(t, "foo bar baz", []token{
|
testQLFlow(t, "foo bar baz", []token{
|
||||||
token{Position{1, 1}, tokenKey, "foo"},
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
token{Position{1, 5}, tokenKey, "bar"},
|
{Position{1, 5}, tokenKey, "bar"},
|
||||||
token{Position{1, 9}, tokenKey, "baz"},
|
{Position{1, 9}, tokenKey, "baz"},
|
||||||
token{Position{1, 12}, tokenEOF, ""},
|
{Position{1, 12}, tokenEOF, ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexInteger(t *testing.T) {
|
||||||
|
testQLFlow(t, "100 +200 -300", []token{
|
||||||
|
{Position{1, 1}, tokenInteger, "100"},
|
||||||
|
{Position{1, 5}, tokenInteger, "+200"},
|
||||||
|
{Position{1, 10}, tokenInteger, "-300"},
|
||||||
|
{Position{1, 14}, tokenEOF, ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexFloat(t *testing.T) {
|
||||||
|
testQLFlow(t, "100.0 +200.0 -300.0", []token{
|
||||||
|
{Position{1, 1}, tokenFloat, "100.0"},
|
||||||
|
{Position{1, 7}, tokenFloat, "+200.0"},
|
||||||
|
{Position{1, 14}, tokenFloat, "-300.0"},
|
||||||
|
{Position{1, 20}, tokenEOF, ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexFloatWithMultipleDots(t *testing.T) {
|
||||||
|
testQLFlow(t, "4.2.", []token{
|
||||||
|
{Position{1, 1}, tokenError, "cannot have two dots in one float"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexFloatLeadingDot(t *testing.T) {
|
||||||
|
testQLFlow(t, "+.1", []token{
|
||||||
|
{Position{1, 1}, tokenError, "cannot start float with a dot"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexFloatWithTrailingDot(t *testing.T) {
|
||||||
|
testQLFlow(t, "42.", []token{
|
||||||
|
{Position{1, 1}, tokenError, "float cannot end with a dot"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexNumberWithoutDigit(t *testing.T) {
|
||||||
|
testQLFlow(t, "+", []token{
|
||||||
|
{Position{1, 1}, tokenError, "no digit in that number"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLexUnknown(t *testing.T) {
|
||||||
|
testQLFlow(t, "^", []token{
|
||||||
|
{Position{1, 1}, tokenError, "unexpected char: '94'"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,12 @@ go build -o toml-test github.com/BurntSushi/toml-test
|
|||||||
# NOTE: this basically mocks an install without having to go back out to github for code
|
# NOTE: this basically mocks an install without having to go back out to github for code
|
||||||
mkdir -p src/github.com/pelletier/go-toml/cmd
|
mkdir -p src/github.com/pelletier/go-toml/cmd
|
||||||
cp *.go *.toml src/github.com/pelletier/go-toml
|
cp *.go *.toml src/github.com/pelletier/go-toml
|
||||||
cp cmd/*.go src/github.com/pelletier/go-toml/cmd
|
cp -R cmd/* src/github.com/pelletier/go-toml/cmd
|
||||||
go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go
|
go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go
|
||||||
|
|
||||||
# Run basic unit tests
|
# Run basic unit tests
|
||||||
go test -v github.com/pelletier/go-toml
|
go test github.com/pelletier/go-toml \
|
||||||
|
github.com/pelletier/go-toml/cmd/tomljson
|
||||||
|
|
||||||
# run the entire BurntSushi test suite
|
# run the entire BurntSushi test suite
|
||||||
if [[ $# -eq 0 ]] ; then
|
if [[ $# -eq 0 ]] ; then
|
||||||
|
|||||||
@@ -222,9 +222,6 @@ func (t *TomlTree) SetPath(keys []string, value interface{}) {
|
|||||||
func (t *TomlTree) createSubTree(keys []string, pos Position) error {
|
func (t *TomlTree) createSubTree(keys []string, pos Position) error {
|
||||||
subtree := t
|
subtree := t
|
||||||
for _, intermediateKey := range keys {
|
for _, intermediateKey := range keys {
|
||||||
if intermediateKey == "" {
|
|
||||||
return fmt.Errorf("empty intermediate table")
|
|
||||||
}
|
|
||||||
nextTree, exists := subtree.values[intermediateKey]
|
nextTree, exists := subtree.values[intermediateKey]
|
||||||
if !exists {
|
if !exists {
|
||||||
tree := newTomlTree()
|
tree := newTomlTree()
|
||||||
|
|||||||
+77
-14
@@ -1,6 +1,7 @@
|
|||||||
// Tools to convert a TomlTree to different representations
|
|
||||||
package toml
|
package toml
|
||||||
|
|
||||||
|
// Tools to convert a TomlTree to different representations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -44,8 +45,28 @@ func encodeTomlString(value string) string {
|
|||||||
func toTomlValue(item interface{}, indent int) string {
|
func toTomlValue(item interface{}, indent int) string {
|
||||||
tab := strings.Repeat(" ", indent)
|
tab := strings.Repeat(" ", indent)
|
||||||
switch value := item.(type) {
|
switch value := item.(type) {
|
||||||
|
case int:
|
||||||
|
return tab + strconv.FormatInt(int64(value), 10)
|
||||||
|
case int8:
|
||||||
|
return tab + strconv.FormatInt(int64(value), 10)
|
||||||
|
case int16:
|
||||||
|
return tab + strconv.FormatInt(int64(value), 10)
|
||||||
|
case int32:
|
||||||
|
return tab + strconv.FormatInt(int64(value), 10)
|
||||||
case int64:
|
case int64:
|
||||||
return tab + strconv.FormatInt(value, 10)
|
return tab + strconv.FormatInt(value, 10)
|
||||||
|
case uint:
|
||||||
|
return tab + strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint8:
|
||||||
|
return tab + strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint16:
|
||||||
|
return tab + strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint32:
|
||||||
|
return tab + strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint64:
|
||||||
|
return tab + strconv.FormatUint(value, 10)
|
||||||
|
case float32:
|
||||||
|
return tab + strconv.FormatFloat(float64(value), 'f', -1, 32)
|
||||||
case float64:
|
case float64:
|
||||||
return tab + strconv.FormatFloat(value, 'f', -1, 64)
|
return tab + strconv.FormatFloat(value, 'f', -1, 64)
|
||||||
case string:
|
case string:
|
||||||
@@ -63,47 +84,88 @@ func toTomlValue(item interface{}, indent int) string {
|
|||||||
result += toTomlValue(item, indent+2) + ",\n"
|
result += toTomlValue(item, indent+2) + ",\n"
|
||||||
}
|
}
|
||||||
return result + tab + "]"
|
return result + tab + "]"
|
||||||
|
case nil:
|
||||||
|
return ""
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unsupported value type: %v", value))
|
panic(fmt.Sprintf("unsupported value type %T: %v", value, value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursive support function for ToString()
|
// Recursive support function for ToString()
|
||||||
// Outputs a tree, using the provided keyspace to prefix group names
|
// Outputs a tree, using the provided keyspace to prefix group names
|
||||||
func (t *TomlTree) toToml(indent, keyspace string) string {
|
func (t *TomlTree) toToml(indent, keyspace string) string {
|
||||||
result := ""
|
resultChunks := []string{}
|
||||||
for k, v := range t.values {
|
for k, v := range t.values {
|
||||||
// figure out the keyspace
|
// figure out the keyspace
|
||||||
combinedKey := k
|
combinedKey := k
|
||||||
if keyspace != "" {
|
if keyspace != "" {
|
||||||
combinedKey = keyspace + "." + combinedKey
|
combinedKey = keyspace + "." + combinedKey
|
||||||
}
|
}
|
||||||
|
resultChunk := ""
|
||||||
// output based on type
|
// output based on type
|
||||||
switch node := v.(type) {
|
switch node := v.(type) {
|
||||||
case []*TomlTree:
|
case []*TomlTree:
|
||||||
for _, item := range node {
|
for _, item := range node {
|
||||||
if len(item.Keys()) > 0 {
|
if len(item.Keys()) > 0 {
|
||||||
result += fmt.Sprintf("\n%s[[%s]]\n", indent, combinedKey)
|
resultChunk += fmt.Sprintf("\n%s[[%s]]\n", indent, combinedKey)
|
||||||
}
|
}
|
||||||
result += item.toToml(indent+" ", combinedKey)
|
resultChunk += item.toToml(indent+" ", combinedKey)
|
||||||
}
|
}
|
||||||
|
resultChunks = append(resultChunks, resultChunk)
|
||||||
case *TomlTree:
|
case *TomlTree:
|
||||||
if len(node.Keys()) > 0 {
|
if len(node.Keys()) > 0 {
|
||||||
result += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
|
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
|
||||||
}
|
}
|
||||||
result += node.toToml(indent+" ", combinedKey)
|
resultChunk += node.toToml(indent+" ", combinedKey)
|
||||||
|
resultChunks = append(resultChunks, resultChunk)
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
sub := TreeFromMap(node)
|
sub := TreeFromMap(node)
|
||||||
|
|
||||||
if len(sub.Keys()) > 0 {
|
if len(sub.Keys()) > 0 {
|
||||||
result += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
|
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
|
||||||
}
|
}
|
||||||
result += sub.toToml(indent+" ", combinedKey)
|
resultChunk += sub.toToml(indent+" ", combinedKey)
|
||||||
|
resultChunks = append(resultChunks, resultChunk)
|
||||||
|
case map[string]string:
|
||||||
|
sub := TreeFromMap(convertMapStringString(node))
|
||||||
|
|
||||||
|
if len(sub.Keys()) > 0 {
|
||||||
|
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
|
||||||
|
}
|
||||||
|
resultChunk += sub.toToml(indent+" ", combinedKey)
|
||||||
|
resultChunks = append(resultChunks, resultChunk)
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
sub := TreeFromMap(convertMapInterfaceInterface(node))
|
||||||
|
|
||||||
|
if len(sub.Keys()) > 0 {
|
||||||
|
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
|
||||||
|
}
|
||||||
|
resultChunk += sub.toToml(indent+" ", combinedKey)
|
||||||
|
resultChunks = append(resultChunks, resultChunk)
|
||||||
case *tomlValue:
|
case *tomlValue:
|
||||||
result += fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(node.value, 0))
|
resultChunk = fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(node.value, 0))
|
||||||
|
resultChunks = append([]string{resultChunk}, resultChunks...)
|
||||||
default:
|
default:
|
||||||
result += fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(v, 0))
|
resultChunk = fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(v, 0))
|
||||||
|
resultChunks = append([]string{resultChunk}, resultChunks...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return strings.Join(resultChunks, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertMapStringString(in map[string]string) map[string]interface{} {
|
||||||
|
result := make(map[string]interface{}, len(in))
|
||||||
|
for k, v := range in {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertMapInterfaceInterface(in map[interface{}]interface{}) map[string]interface{} {
|
||||||
|
result := make(map[string]interface{}, len(in))
|
||||||
|
for k, v := range in {
|
||||||
|
result[k.(string)] = v
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -113,7 +175,7 @@ func (t *TomlTree) ToString() string {
|
|||||||
return t.String()
|
return t.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToString generates a human-readable representation of the current tree.
|
// String generates a human-readable representation of the current tree.
|
||||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser
|
// Output spans multiple lines, and is suitable for ingest by a TOML parser
|
||||||
func (t *TomlTree) String() string {
|
func (t *TomlTree) String() string {
|
||||||
return t.toToml("", "")
|
return t.toToml("", "")
|
||||||
@@ -126,10 +188,11 @@ func (t *TomlTree) ToMap() map[string]interface{} {
|
|||||||
for k, v := range t.values {
|
for k, v := range t.values {
|
||||||
switch node := v.(type) {
|
switch node := v.(type) {
|
||||||
case []*TomlTree:
|
case []*TomlTree:
|
||||||
result[k] = make([]interface{}, 0)
|
var array []interface{}
|
||||||
for _, item := range node {
|
for _, item := range node {
|
||||||
result[k] = item.ToMap()
|
array = append(array, item.ToMap())
|
||||||
}
|
}
|
||||||
|
result[k] = array
|
||||||
case *TomlTree:
|
case *TomlTree:
|
||||||
result[k] = node.ToMap()
|
result[k] = node.ToMap()
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTomlTreeConversionToString(t *testing.T) {
|
func TestTomlTreeConversionToString(t *testing.T) {
|
||||||
@@ -28,6 +29,41 @@ points = { x = 1, y = 2 }`)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTomlTreeConversionToStringKeysOrders(t *testing.T) {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
tree, _ := Load(`
|
||||||
|
foobar = true
|
||||||
|
bar = "baz"
|
||||||
|
foo = 1
|
||||||
|
[qux]
|
||||||
|
foo = 1
|
||||||
|
bar = "baz2"`)
|
||||||
|
|
||||||
|
stringRepr := tree.ToString()
|
||||||
|
|
||||||
|
t.Log("Intermediate string representation:")
|
||||||
|
t.Log(stringRepr)
|
||||||
|
|
||||||
|
r := strings.NewReader(stringRepr)
|
||||||
|
toml, err := LoadReader(r)
|
||||||
|
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Unexpected error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTree(t, toml, err, map[string]interface{}{
|
||||||
|
"foobar": true,
|
||||||
|
"bar": "baz",
|
||||||
|
"foo": 1,
|
||||||
|
"qux": map[string]interface{}{
|
||||||
|
"foo": 1,
|
||||||
|
"bar": "baz2",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testMaps(t *testing.T, actual, expected map[string]interface{}) {
|
func testMaps(t *testing.T, actual, expected map[string]interface{}) {
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
t.Fatal("trees aren't equal.\n", "Expected:\n", expected, "\nActual:\n", actual)
|
t.Fatal("trees aren't equal.\n", "Expected:\n", expected, "\nActual:\n", actual)
|
||||||
@@ -80,3 +116,56 @@ func TestTomlTreeConversionToMapExampleFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
testMaps(t, tree.ToMap(), expected)
|
testMaps(t, tree.ToMap(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTomlTreeConversionToMapWithTablesInMultipleChunks(t *testing.T) {
|
||||||
|
tree, _ := Load(`
|
||||||
|
[[menu.main]]
|
||||||
|
a = "menu 1"
|
||||||
|
b = "menu 2"
|
||||||
|
[[menu.main]]
|
||||||
|
c = "menu 3"
|
||||||
|
d = "menu 4"`)
|
||||||
|
expected := map[string]interface{}{
|
||||||
|
"menu": map[string]interface{}{
|
||||||
|
"main": []interface{}{
|
||||||
|
map[string]interface{}{"a": "menu 1", "b": "menu 2"},
|
||||||
|
map[string]interface{}{"c": "menu 3", "d": "menu 4"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
treeMap := tree.ToMap()
|
||||||
|
|
||||||
|
testMaps(t, treeMap, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTomlTreeConversionToMapWithArrayOfInlineTables(t *testing.T) {
|
||||||
|
tree, _ := Load(`
|
||||||
|
[params]
|
||||||
|
language_tabs = [
|
||||||
|
{ key = "shell", name = "Shell" },
|
||||||
|
{ key = "ruby", name = "Ruby" },
|
||||||
|
{ key = "python", name = "Python" }
|
||||||
|
]`)
|
||||||
|
|
||||||
|
expected := map[string]interface{}{
|
||||||
|
"params": map[string]interface{}{
|
||||||
|
"language_tabs": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"key": "shell",
|
||||||
|
"name": "Shell",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"key": "ruby",
|
||||||
|
"name": "Ruby",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"key": "python",
|
||||||
|
"name": "Python",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
treeMap := tree.ToMap()
|
||||||
|
testMaps(t, treeMap, expected)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user