pqarrays

Paddy 2016-02-25 Parent:bfe2a4af6bdf Child:9a415db0346a

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},