From d1b5bc1f05624614f43ef85597f4aa9d7a166d23 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 29 Jan 2023 17:40:13 -0700 Subject: parse: Add an example of how the stack works for arrays, add tests --- internal/parse.go | 38 ++++++++++++++++++------ internal/parse_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 internal/parse_test.go diff --git a/internal/parse.go b/internal/parse.go index bb849e7..121857b 100644 --- a/internal/parse.go +++ b/internal/parse.go @@ -268,6 +268,8 @@ type Parser struct { // // Within each element type, the stack item is replaced, not pushed. // + // (Keep each of these examples in-sync with parse_test.go.) + // // For example, given the input string // // {"x":"y","a":"b"} @@ -293,9 +295,34 @@ type Parser struct { // o" {"x":"y","a":"b // o {"x":"y","a":"b" // {"x":"y","a":"b"} + // + // Or, given the input string + // + // ["x","y"] + // + // The stack would be + // + // stack processed + // ? + // [ [ + // a" [" + // a" ["x + // a ["x" + // ] ["x", + // a" ["x"," + // a" ["x","y + // a ["x","y" + // ["x","y"] stack []RuneType } +func (par *Parser) init() { + if !par.initialized { + par.initialized = true + par.pushState(runeTypeAny) + } +} + func (par *Parser) pushState(state RuneType) RuneType { par.stack = append(par.stack, state) return state @@ -311,6 +338,7 @@ func (par *Parser) popState() { } func (par *Parser) stackString() string { + par.init() var buf strings.Builder for _, s := range par.stack { buf.WriteString(s.String()) @@ -351,10 +379,7 @@ func (par *Parser) HandleEOF() (RuneType, error) { if par.err != nil { return RuneTypeError, par.err } - if !par.initialized { - par.initialized = true - par.pushState(runeTypeAny) - } + par.init() switch len(par.stack) { case 0: return RuneTypeEOF, nil @@ -395,10 +420,7 @@ func (par *Parser) HandleRune(c rune) (RuneType, error) { if par.err != nil { return RuneTypeError, par.err } - if !par.initialized { - par.initialized = true - par.pushState(runeTypeAny) - } + par.init() if len(par.stack) == 0 { switch c { case 0x0020, 0x000A, 0x000D, 0x0009: diff --git a/internal/parse_test.go b/internal/parse_test.go new file mode 100644 index 0000000..91cd277 --- /dev/null +++ b/internal/parse_test.go @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package internal + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParserHandleRune(t *testing.T) { + t.Parallel() + type testcase struct { + Input string + ExpStack []string + } + testcases := map[string]testcase{ + // Keep these test-cases in-sync with the examples in parse.go. + "object": { + Input: `{"x":"y","a":"b"}`, + ExpStack: []string{ + // st,// processed + `?`, + `{`, // { + `»"`, // {" + `»"`, // {"x + `»`, // {"x" + `o?`, // {"x": + `o"`, // {"x":" + `o"`, // {"x":"y + `o`, // {"x":"y" + `{`, // {"x":"y", + `»"`, // {"x":"y"," + `»"`, // {"x":"y","a + `»`, // {"x":"y","a" + `o?`, // {"x":"y","a": + `o"`, // {"x":"y","a":" + `o"`, // {"x":"y","a":"b + `o`, // {"x":"y","a":"b" + ``, // {"x":"y","a":"b"} + }, + }, + "array": { + Input: `["x","y"]`, + ExpStack: []string{ + // st,// processed + `?`, + `[`, // [ + `a"`, // [" + `a"`, // ["x + `a`, // ["x" + `]`, // ["x", + `a"`, // ["x"," + `a"`, // ["x","y + `a`, // ["x","y" + ``, // ["x","y"] + }, + }, + } + for tcName, tc := range testcases { + tc := tc + t.Run(tcName, func(t *testing.T) { + t.Parallel() + var par Parser + if !assert.Equal(t, len(tc.Input)+1, len(tc.ExpStack)) { + return + } + for i, r := range tc.Input { + assert.Equal(t, tc.ExpStack[i], par.stackString()) + _, err := par.HandleRune(r) + assert.NoError(t, err) + assert.Equal(t, tc.ExpStack[i+1], par.stackString()) + } + }) + } +} -- cgit v1.1-4-g5e80