CSVabstractParse and generate CSV, RFC 4180 tabular text.
CSV is untyped, so parsing always yields Strings (convert fields yourself), and generating stringifies every field via its .s. Rows come in two shapes: positional (parse: / generate: — Lists of Lists) and header-keyed (parseWithHeaders: / generateWithHeaders: — Lists of Maps). Quoting and escaping are handled per RFC 4180.
CSV.parse:'a,b\n1,2' "* -> #(#(a b) #(1 2)) CSV.generate:#( #('id' 'n') #(1 2) ) "* -> 'id,n\n1,2\n'
Generate CSV text from a List of rows (each a List of fields). Fields are stringified via their .s, so numbers and booleans work directly; fields containing commas, quotes, or newlines are quoted per RFC 4180.
CSV.generate:#( #('id' 'n') #(1 2) ) "* -> 'id,n\n1,2\n'
Generate CSV text from a List of Maps, writing a header row first. The header comes from the first row's keys, in order; a key missing from a later row becomes an empty field. Values are stringified via .s.
CSV.generateWithHeaders:#( #{'id': 1 'n': 2} ) "* -> 'id,n\n1,2\n'
Parse CSV text into a List of rows, each row a List of String fields (every field is a String — CSV is untyped). There is no header handling here; use parseWithHeaders: when the first row names the columns. Malformed input throws a ParseError.
CSV.parse:'a,b\n1,2' "* -> #(#(a b) #(1 2))
Parse CSV whose first row is a header, yielding a List of Maps — one per data row, keyed by the header fields, in column order (Maps are insertion-ordered). Every value is a String.
CSV.parseWithHeaders:'name,age\nAlice,30' "* -> #(#{'name': 'Alice' 'age': '30'})