pqarrays
1:ce9c92fc81ab Browse Files
Fix bug parsing empty arrays, make golint and go vet happy. Add comments to make golint happy. Also, because comments are a good thing to have. Turn += 1 and -= 1 into ++ and --, respectively, so golint will be happy. Fix an improperly formated errorf, where a rune was being treated as a string. Thanks, go vet! Fix whitespace parsing, returning the parse functions again instead of just skipping the one character. Now if we have more than one whitespace character in a row, they'll all be skipped. Add a parseStringOrNullOrEnd parse function that will be called after the tokenArrayStart character, to fix a bug where empty arrays were expecting a string or null and getting the array end character. This is only valid after tokenArrayStart, however; in other places where parseSeparatorOrDelim is used, it wouldn't be appropriate. Add a parser test for an empty array.
array.go lexer.go parser.go parser_test.go
1.1 --- a/array.go Sun Apr 19 23:47:36 2015 -0400 1.2 +++ b/array.go Thu Feb 25 23:52:05 2016 -0800 1.3 @@ -8,11 +8,15 @@ 1.4 ) 1.5 1.6 var ( 1.7 + // ErrUnexpectedValueType is returned when the passed value is not a string or []byte. 1.8 ErrUnexpectedValueType = errors.New("expected value to be a string or []byte") 1.9 ) 1.10 1.11 +// StringArray represents a Postgres array as a []string. 1.12 type StringArray []string 1.13 1.14 +// Value implements the Valuer interface for StringArray, allowing it to be transparently 1.15 +// stored in Postgres databases using the database/sql package. 1.16 func (s StringArray) Value() (driver.Value, error) { 1.17 output := make([]string, 0, len(s)) 1.18 for _, item := range s { 1.19 @@ -23,6 +27,9 @@ 1.20 return []byte(`{` + strings.Join(output, ",") + `}`), nil 1.21 } 1.22 1.23 +// Scan implements the Scanner interface for StringArray, allowing it to be transparently 1.24 +// retrieved from Postgres databases using the database/sql package. It expects `value` to 1.25 +// be a string or []byte, and throws ErrUnexpectedValueType when any other type is encountered. 1.26 func (s *StringArray) Scan(value interface{}) error { 1.27 *s = (*s)[:0] 1.28 var input string
2.1 --- a/lexer.go Sun Apr 19 23:47:36 2015 -0400 2.2 +++ b/lexer.go Thu Feb 25 23:52:05 2016 -0800 2.3 @@ -178,14 +178,14 @@ 2.4 func lexLeftDelim(l *lexer) stateFunc { 2.5 l.pos += len(leftDelim) 2.6 l.emit(tokenArrayStart) 2.7 - l.arrayDepth += 1 2.8 + l.arrayDepth++ 2.9 return lexItem 2.10 } 2.11 2.12 func lexRightDelim(l *lexer) stateFunc { 2.13 l.pos += len(rightDelim) 2.14 l.emit(tokenArrayEnd) 2.15 - l.arrayDepth -= 1 2.16 + l.arrayDepth-- 2.17 return lexSeparator 2.18 } 2.19 2.20 @@ -293,6 +293,6 @@ 2.21 return nil 2.22 } else { 2.23 l.backup() 2.24 - return l.errorf("expected %s, none found before %s\n", separator, l.input[l.pos:]) 2.25 + return l.errorf("expected %s, none found before %s\n", string(separator), l.input[l.pos:]) 2.26 } 2.27 }
3.1 --- a/parser.go Sun Apr 19 23:47:36 2015 -0400 3.2 +++ b/parser.go Thu Feb 25 23:52:05 2016 -0800 3.3 @@ -43,7 +43,7 @@ 3.4 func parseEOF(l *lexer, parsed chan *string) (parseFunc, error) { 3.5 tok := l.nextToken() 3.6 if tok.typ == tokenWhitespace { 3.7 - tok = l.nextToken() 3.8 + return parseEOF, nil 3.9 } 3.10 if tok.typ != tokenEOF { 3.11 return nil, errors.New("expected EOF, got " + tok.typ.String()) 3.12 @@ -54,7 +54,7 @@ 3.13 func parseStringOrNull(l *lexer, parsed chan *string) (parseFunc, error) { 3.14 tok := l.nextToken() 3.15 if tok.typ == tokenWhitespace { 3.16 - tok = l.nextToken() 3.17 + return parseStringOrNull, nil 3.18 } else if tok.typ == tokenString { 3.19 parsed <- &tok.val 3.20 return parseSeparatorOrDelim, nil 3.21 @@ -65,6 +65,22 @@ 3.22 return nil, errors.New("expected string, got " + tok.typ.String()) 3.23 } 3.24 3.25 +func parseStringOrNullOrEnd(l *lexer, parsed chan *string) (parseFunc, error) { 3.26 + tok := l.nextToken() 3.27 + if tok.typ == tokenWhitespace { 3.28 + return parseStringOrNullOrEnd, nil 3.29 + } else if tok.typ == tokenString { 3.30 + parsed <- &tok.val 3.31 + return parseSeparatorOrDelim, nil 3.32 + } else if tok.typ == tokenNull { 3.33 + parsed <- nil 3.34 + return parseSeparatorOrDelim, nil 3.35 + } else if tok.typ == tokenArrayEnd { 3.36 + return parseEOF, nil 3.37 + } 3.38 + return nil, errors.New("Expected string or end, got " + tok.typ.String()) 3.39 +} 3.40 + 3.41 func parseSeparatorOrDelim(l *lexer, parsed chan *string) (parseFunc, error) { 3.42 tok := l.nextToken() 3.43 if tok.typ == tokenWhitespace { 3.44 @@ -82,7 +98,7 @@ 3.45 if tok.typ == tokenWhitespace { 3.46 return parseStart, nil 3.47 } else if tok.typ == tokenArrayStart { 3.48 - return parseStringOrNull, nil 3.49 + return parseStringOrNullOrEnd, nil 3.50 } 3.51 return nil, errors.New("expected separator or delim, got " + tok.typ.String()) 3.52 }
4.1 --- a/parser_test.go Sun Apr 19 23:47:36 2015 -0400 4.2 +++ b/parser_test.go Thu Feb 25 23:52:05 2016 -0800 4.3 @@ -9,6 +9,7 @@ 4.4 } 4.5 4.6 var parseTestInputs = map[string][]*string{ 4.7 + `{}`: []*string{}, 4.8 `{lions}`: []*string{strPtr("lions")}, 4.9 `{lions,tigers}`: []*string{strPtr("lions"), strPtr("tigers")}, 4.10 `{lions,tigers,NULL}`: []*string{strPtr("lions"), strPtr("tigers"), nil},