Skip to content

Commit faf8b4e

Browse files
committed
1.4.0
1 parent a75bfa3 commit faf8b4e

File tree

6 files changed

+61
-40
lines changed

6 files changed

+61
-40
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
## v1.4.0 - 29 March 2024
6+
- Fix bug where trailing comma was causing error
7+
58
## v1.3.1 - 1 February 2024
69
- Update to gleam_stdlib = "~> 0.34 or ~> 1.0" in preparation for 1.0
710

gleam.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "gsv"
2-
version = "1.3.1"
2+
version = "1.4.0"
33
gleam = ">= 0.32.0"
44
description = "A simple csv parser and generator written in gleam "
55

src/gsv.gleam

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import gsv/internal/ast.{ParseError}
2-
import gsv/internal/token.{Location}
1+
import gleam/int
32
import gleam/list
4-
import gleam/string
53
import gleam/result
6-
import gleam/int
4+
import gleam/string
5+
import gsv/internal/ast.{ParseError}
6+
import gsv/internal/token.{Location}
77

88
/// Parses a csv string to a list of lists of strings.
99
/// Automatically handles Windows and Unix line endings.

src/gsv/internal/ast.gleam

+34-24
Original file line numberDiff line numberDiff line change
@@ -92,33 +92,46 @@ fn parse_p(
9292

9393
// If we just parsed a comma, we're expecting an Escaped or Non-Escaped string, or another comma
9494
// (indicating an empty string)
95-
[#(Textdata(str), _), ..remaining_tokens], JustParsedComma, [
96-
curr_line,
97-
..previously_parsed_lines
98-
] ->
95+
[#(Textdata(str), _), ..remaining_tokens],
96+
JustParsedComma,
97+
[curr_line, ..previously_parsed_lines] ->
9998
parse_p(remaining_tokens, JustParsedField, [
10099
[str, ..curr_line],
101100
..previously_parsed_lines
102101
])
103102

104-
[#(Doublequote, _), ..remaining_tokens], JustParsedComma, [
105-
curr_line,
106-
..previously_parsed_lines
107-
] ->
103+
[#(Doublequote, _), ..remaining_tokens],
104+
JustParsedComma,
105+
[curr_line, ..previously_parsed_lines] ->
108106
parse_p(remaining_tokens, InsideEscapedString, [
109107
["", ..curr_line],
110108
..previously_parsed_lines
111109
])
112110

113-
[#(Comma, _), ..remaining_tokens], JustParsedComma, [
114-
curr_line,
115-
..previously_parsed_lines
116-
] ->
111+
[#(Comma, _), ..remaining_tokens],
112+
JustParsedComma,
113+
[curr_line, ..previously_parsed_lines] ->
117114
parse_p(remaining_tokens, JustParsedComma, [
118115
["", ..curr_line],
119116
..previously_parsed_lines
120117
])
121118

119+
[#(CR, _), ..remaining_tokens],
120+
JustParsedComma,
121+
[curr_line, ..previously_parsed_lines] ->
122+
parse_p(remaining_tokens, JustParsedCR, [
123+
["", ..curr_line],
124+
..previously_parsed_lines
125+
])
126+
127+
[#(LF, _), ..remaining_tokens],
128+
JustParsedComma,
129+
[curr_line, ..previously_parsed_lines] ->
130+
parse_p(remaining_tokens, JustParsedNewline, [
131+
["", ..curr_line],
132+
..previously_parsed_lines
133+
])
134+
122135
[#(tok, loc), ..], JustParsedComma, _ ->
123136
Error(ParseError(
124137
loc,
@@ -130,10 +143,9 @@ fn parse_p(
130143
[#(Textdata(str), _), ..remaining_tokens], JustParsedNewline, llf ->
131144
parse_p(remaining_tokens, JustParsedField, [[str], ..llf])
132145

133-
[#(Doublequote, _), ..remaining_tokens], JustParsedNewline, [
134-
curr_line,
135-
..previously_parsed_lines
136-
] ->
146+
[#(Doublequote, _), ..remaining_tokens],
147+
JustParsedNewline,
148+
[curr_line, ..previously_parsed_lines] ->
137149
parse_p(remaining_tokens, InsideEscapedString, [
138150
["", ..curr_line],
139151
..previously_parsed_lines
@@ -148,10 +160,9 @@ fn parse_p(
148160

149161
// If we're inside an escaped string, we can take anything until we get a double quote,
150162
// but a double double quote "" escapes the double quote and we keep parsing
151-
[#(Doublequote, _), #(Doublequote, _), ..remaining_tokens], InsideEscapedString, [
152-
[str, ..rest_curr_line],
153-
..previously_parsed_lines
154-
] ->
163+
[#(Doublequote, _), #(Doublequote, _), ..remaining_tokens],
164+
InsideEscapedString,
165+
[[str, ..rest_curr_line], ..previously_parsed_lines] ->
155166
parse_p(remaining_tokens, InsideEscapedString, [
156167
[str <> "\"", ..rest_curr_line],
157168
..previously_parsed_lines
@@ -160,10 +171,9 @@ fn parse_p(
160171
[#(Doublequote, _), ..remaining_tokens], InsideEscapedString, llf ->
161172
parse_p(remaining_tokens, JustParsedField, llf)
162173

163-
[#(other_token, _), ..remaining_tokens], InsideEscapedString, [
164-
[str, ..rest_curr_line],
165-
..previously_parsed_lines
166-
] ->
174+
[#(other_token, _), ..remaining_tokens],
175+
InsideEscapedString,
176+
[[str, ..rest_curr_line], ..previously_parsed_lines] ->
167177
parse_p(remaining_tokens, InsideEscapedString, [
168178
[str <> token.to_lexeme(other_token), ..rest_curr_line],
169179
..previously_parsed_lines

src/gsv/internal/token.gleam

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
//// escaped = DQUOTE *(TEXTDATA / COMMA / CR / LF / 2DQUOTE) DQUOTE
99
//// non-escaped = *TEXTDATA
1010

11-
import gleam/string
1211
import gleam/list
12+
import gleam/string
1313

1414
pub type CsvToken {
1515
Comma

test/gsv_test.gleam

+18-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
import gleam/int
2+
import gleam/list
3+
import gleam/result
4+
import gleam/string
15
import gleeunit
26
import gleeunit/should
7+
import gsv.{Unix, Windows}
8+
import gsv/internal/ast.{ParseError, parse}
39
import gsv/internal/token.{
410
CR, Comma, Doublequote, LF, Location, Textdata, scan, with_location,
511
}
6-
import gsv/internal/ast.{ParseError, parse}
7-
import gsv.{Unix, Windows}
8-
import gleam/list
9-
import gleam/result
10-
import gleam/int
11-
import gleam/string
1212

1313
pub fn main() {
1414
gleeunit.main()
@@ -161,13 +161,13 @@ pub fn error_cases_test() {
161161

162162
produce_error("Ben, 25,\n, TRUE")
163163
|> should.equal(#(
164-
Location(1, 9),
165-
"Expected escaped or non-escaped string after comma, found: \n",
164+
Location(2, 1),
165+
"Expected escaped or non-escaped string after newline, found: ,",
166166
))
167167
produce_error("Austin, 25, FALSE\n\"Ben Peinhardt\", 25,\n, TRUE")
168168
|> should.equal(#(
169-
Location(2, 21),
170-
"Expected escaped or non-escaped string after comma, found: \n",
169+
Location(3, 1),
170+
"Expected escaped or non-escaped string after newline, found: ,",
171171
))
172172
}
173173

@@ -180,3 +180,11 @@ pub fn totally_errors_test() {
180180
|> gsv.to_lists_or_error
181181
|> should.equal(Ok([["Ben", " 25", "", " TRUE"]]))
182182
}
183+
184+
pub fn trailing_commas_fine_test() {
185+
"Ben, 25, TRUE, Hello\nAustin, 25,\n"
186+
|> gsv.to_lists
187+
|> should.equal(
188+
Ok([["Ben", " 25", " TRUE", " Hello"], ["Austin", " 25", ""]]),
189+
)
190+
}

0 commit comments

Comments
 (0)