parse_line breaks on quoted fields

Done

/home/devdi/devswarm/evals/tasks/csv_quoted_fields/repo · 2026-11-09 16:10:50 UTC · took 46.7s

  1. Plan (done)
  2. Code (done)
  3. Test (done)
  4. Review (done)
  5. Finish (done)
Cost
$0.0058
Tests
Passed
Iterations
1
Re-plans
0
LLM calls
11
Prompt read from cache
75%

Usage by agent

Cost per agent
planner: $0.0016 planner planner, Cost: $0.0016 $0.0016 coder: $0.0023 coder coder, Cost: $0.0023 $0.0023 tester: $0.0019 tester tester, Cost: $0.0019 $0.0019
Tokens per agent
  • Cache read
  • Cache write
  • Input
  • Output
planner: 12.9K planner planner, Cache read: 8,255 planner, Cache write: 2,073 planner, Output: 2,606 12.9K coder: 22.3K coder coder, Cache read: 14.6K coder, Cache write: 4,472 coder, Output: 3,172 22.3K tester: 19.8K tester tester, Cache read: 12.5K tester, Cache write: 5,080 tester, Output: 2,211 19.8K
View as table
AgentCostCache readCache writeInputOutput
planner$0.00168,2552,073122,606
coder$0.002314,6254,472103,172
tester$0.001912,4655,08082,211

Timeline

  1. planner iteration 1 Finished 4 calls · 12.9K tokens · $0.0016 · 12.6s

    Loading…

  2. coder iteration 1 Finished 4 calls · 22.3K tokens · $0.0023 · 17.1s

    Loading…

  3. tester iteration 1 Finished 3 calls · 19.8K tokens · $0.0019 · 15.9s

    Loading…

  4. 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"']