From b8644ed82ed955c94919efa4eeab335436b2047c Mon Sep 17 00:00:00 2001 From: gurkankaymak Date: Mon, 19 Dec 2022 20:13:09 +0300 Subject: [PATCH] fixed parsing unquoted strings inside the arrays, fixes #33 --- config.go | 2 +- parser.go | 47 ++++++++++++++++++++++++++++++++++++++-- parser_test.go | 58 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index 50d4d07..3d5d91d 100644 --- a/config.go +++ b/config.go @@ -414,7 +414,7 @@ type Int int // Type Number func (i Int) Type() Type { return NumberType } func (i Int) String() string { return strconv.Itoa(int(i)) } -func (i Int) isConcatenable() bool { return false } +func (i Int) isConcatenable() bool { return true } // Float32 represents a Float32 value type Float32 float32 diff --git a/parser.go b/parser.go index 7187e65..c0b7e5c 100644 --- a/parser.go +++ b/parser.go @@ -533,6 +533,25 @@ func (p *parser) checkAndConcatenate(object Object, key string) (bool, error) { return false, nil } +func (p *parser) checkConcatenation(lastValue Value) (Value, error) { + if lastValue.isConcatenable() && p.isTokenConcatenable(p.scanner.TokenText(), p.scanner.Peek()) { + lastConsumedWhitespaces := p.lastConsumedWhitespaces + + value, err := p.extractValue() + if err != nil { + return nil, err + } + + if lastValue.Type() == ConcatenationType { + return append(lastValue.(concatenation), String(lastConsumedWhitespaces), value), nil + } else { + return concatenation{lastValue, String(lastConsumedWhitespaces), value}, nil + } + } + + return nil, nil +} + func (p *parser) extractArray() (Array, error) { if firstToken := p.scanner.TokenText(); firstToken != arrayStartToken { return nil, invalidArrayError(fmt.Sprintf("%q is not an array start token", firstToken), p.scanner.Line, p.scanner.Column) @@ -563,11 +582,35 @@ func (p *parser) extractArray() (Array, error) { return nil, err } - array = append(array, value) + //array = append(array, value) token = p.scanner.TokenText() if p.scanner.Line == lastRow && token != commaToken && token != arrayEndToken { - return nil, missingCommaError(p.scanner.Line, p.scanner.Column) + if isUnquotedString(token) { + concatenatedValue, err := p.checkConcatenation(value) + if err != nil { + return nil, err + } + lastValue := concatenatedValue + token = p.scanner.TokenText() + for concatenatedValue != nil && isUnquotedString(token) && token != commaToken && token != arrayEndToken { + concatenatedValue, err = p.checkConcatenation(lastValue) + if err != nil { + return nil, err + } + if concatenatedValue != nil { + lastValue = concatenatedValue + } else { + break + } + token = p.scanner.TokenText() + } + array = append(array, lastValue) + } else { + return nil, missingCommaError(p.scanner.Line, p.scanner.Column) + } + } else { + array = append(array, value) } if p.scanner.TokenText() == commaToken { diff --git a/parser_test.go b/parser_test.go index 55d0134..979f2d4 100644 --- a/parser_test.go +++ b/parser_test.go @@ -528,7 +528,7 @@ func TestExtractObject(t *testing.T) { t.Run("return missingCommaError if there is no comma or ASCII newline between the object elements", func(t *testing.T) { parser := newParser(strings.NewReader("{a:1 b:2}")) parser.advance() - expectedError := missingCommaError(1, 6) + expectedError := missingCommaError(1, 7) got, err := parser.extractObject() assertError(t, err, expectedError) assertNil(t, got) @@ -1054,6 +1054,14 @@ func TestExtractArray(t *testing.T) { assertNil(t, got) }) + t.Run("extract the array successfully if it contains an unquoted string value", func(t *testing.T) { + parser := newParser(strings.NewReader("[example.com]")) + parser.advance() + got, err := parser.extractArray() + assertNoError(t, err) + assertDeepEqual(t, got, Array{concatenation{String("example"), String(""), String("."), String(""), String("com")}}) + }) + t.Run("return invalidArrayError if the closing parenthesis is missing", func(t *testing.T) { parser := newParser(strings.NewReader("[1")) parser.advance() @@ -1063,10 +1071,10 @@ func TestExtractArray(t *testing.T) { assertNil(t, got) }) - t.Run("return missingCommaError if there is no comma or ASCII newline between the array elements", func(t *testing.T) { - parser := newParser(strings.NewReader("[1 2]")) + t.Run("return missingCommaError if there is no comma or ASCII newline between the array elements and elements separated with a forbidden character", func(t *testing.T) { + parser := newParser(strings.NewReader("[1@2]")) parser.advance() - expectedError := missingCommaError(1, 4) + expectedError := missingCommaError(1, 3) got, err := parser.extractArray() assertError(t, err, expectedError) assertNil(t, got) @@ -1489,9 +1497,9 @@ func TestCheckAndConcatenate(t *testing.T) { }) t.Run("return false if the value with the given is not concatenable", func(t *testing.T) { - parser := newParser(strings.NewReader("a:1 bb")) + parser := newParser(strings.NewReader("a:1s bb")) advanceScanner(t, parser, "bb") - got, err := parser.checkAndConcatenate(Object{"a": Int(1)}, "a") + got, err := parser.checkAndConcatenate(Object{"a": Duration(1)}, "a") assertNoError(t, err) assertEquals(t, got, false) }) @@ -1536,3 +1544,41 @@ func TestCheckAndConcatenate(t *testing.T) { assertEquals(t, object.String(), expected.String()) }) } + +func TestCheckConcatenation(t *testing.T) { + t.Run("return nil if the value with the given is not concatenable", func(t *testing.T) { + parser := newParser(strings.NewReader("[1s bb]")) + advanceScanner(t, parser, "bb") + got, err := parser.checkConcatenation(Array{Duration(1)}) + assertNoError(t, err) + assertNil(t, got) + }) + + t.Run("return nil if the current token is not concatenable", func(t *testing.T) { + parser := newParser(strings.NewReader("[abc 1s]")) + advanceScanner(t, parser, "1") + got, err := parser.checkConcatenation(Array{String("abc")}) + assertNoError(t, err) + assertNil(t, got) + }) + + t.Run("concatenate the value to the previous value if the previous one is a concatenation", func(t *testing.T) { + parser := newParser(strings.NewReader("[aa bb cc]")) + advanceScanner(t, parser, "cc") + whitespace := parser.lastConsumedWhitespaces + a := concatenation{String("aa"), String(whitespace), String("bb")} + got, err := parser.checkConcatenation(a) + assertNoError(t, err) + expected := concatenation{String("aa"), String(whitespace), String("bb"), String(whitespace), String("cc")} + assertDeepEqual(t, got, expected) + }) + + t.Run("create a concatenation with the value and the previous value if the previous one is not a concatenation", func(t *testing.T) { + parser := newParser(strings.NewReader("[aa bb]")) + advanceScanner(t, parser, "bb") + got, err := parser.checkConcatenation(String("aa")) + assertNoError(t, err) + expected := concatenation{String("aa"), String(" "), String("bb")} + assertEquals(t, got.String(), expected.String()) + }) +}