parse_line breaks on quoted fields
Done- Plan (done)
- Code (done)
- Test (done)
- Review (done)
- Finish (done)
Cost
$0.0058
Tests
Passed
Iterations
1
Re-plans
0
LLM calls
11
Prompt read from cache
75%
Usage by agent
- Cache read
- Cache write
- Input
- Output
View as table
| Agent | Cost | Cache read | Cache write | Input | Output |
|---|---|---|---|---|---|
| planner | $0.0016 | 8,255 | 2,073 | 12 | 2,606 |
| coder | $0.0023 | 14,625 | 4,472 | 10 | 3,172 |
| tester | $0.0019 | 12,465 | 5,080 | 8 | 2,211 |
Timeline
-
planner iteration 1 Finished 4 calls · 12.9K tokens · $0.0016 · 12.6s
Loading…
-
coder iteration 1 Finished 4 calls · 22.3K tokens · $0.0023 · 17.1s
Loading…
-
tester iteration 1 Finished 3 calls · 19.8K tokens · $0.0019 · 15.9s
Loading…
-
verify iteration 1 Passed 0.5s
............. [100%] 13 passed in 0.03s
Final diff
diff --git a/csvline.py b/csvline.py index c00ae65..0e7bb97 100644 --- a/csvline.py +++ b/csvline.py @@ -1,2 +1,40 @@ def parse_line(line: str) -> list[str]: - return line.split(",") + """Split one CSV line into fields. + + Commas separate fields unless they are inside a quoted field. A quote + opens quoted mode only as the first character of a field, and a doubled + quote inside quoted mode is a literal quote. Text after a closing quote is + appended literally up to the next comma. An unterminated quoted field runs + to the end of the line. Empty fields are kept, so n commas give n + 1 fields. + """ + fields: list[str] = [] + current: list[str] = [] + in_quotes = False + at_field_start = True + i = 0 + n = len(line) + while i < n: + ch = line[i] + if in_quotes: + if ch == '"': + if i + 1 < n and line[i + 1] == '"': + current.append('"') + i += 2 + continue + in_quotes = False + else: + current.append(ch) + elif ch == ",": + fields.append("".join(current)) + current = [] + at_field_start = True + i += 1 + continue + elif ch == '"' and at_field_start: + in_quotes = True + else: + current.append(ch) + at_field_start = False + i += 1 + fields.append("".join(current)) + return fields diff --git a/test_csvline.py b/test_csvline.py index 3dff251..c4283d4 100644 --- a/test_csvline.py +++ b/test_csvline.py @@ -3,3 +3,53 @@ from csvline import parse_line def test_simple(): assert parse_line("a,b,c") == ["a", "b", "c"] + + +def test_quoted_comma(): + assert parse_line('a,"b,c",d') == ["a", "b,c", "d"] + + +def test_doubled_quote_is_literal(): + assert parse_line('"say ""hi""",x') == ['say "hi"', "x"] + + +def test_empty_fields_kept(): + assert parse_line("a,,b") == ["a", "", "b"] + assert parse_line(",a,") == ["", "a", ""] + + +def test_empty_string(): + assert parse_line("") == [""] + + +def test_quoted_empty_field(): + assert parse_line('"",x') == ["", "x"] + + +def test_quote_mid_field_is_literal(): + assert parse_line('ab"c,d') == ['ab"c', "d"] + + +def test_text_after_closing_quote_is_literal(): + assert parse_line('"ab"cd,x') == ["abcd", "x"] + + +def test_unterminated_quote_runs_to_end_of_line(): + assert parse_line('"abc,d') == ["abc,d"] + + +def test_quoted_single_quote_char(): + assert parse_line('""""') == ['"'] + + +def test_quoted_field_wrapped_in_quotes(): + assert parse_line('"""a"""') == ['"a"'] + + +def test_only_commas(): + assert parse_line(",") == ["", ""] + assert parse_line(",,") == ["", "", ""] + + +def test_quote_after_leading_space_is_literal(): + assert parse_line(' "a,b"') == [' "a', 'b"']