librarylibs/Csv/src/Csv.xtl

Csv: comma-separated values -- lines to fields (quoted fields kept whole), a table of texts, columns by number or name, numbers, and back to text. Import it with an alias of your choice: "cs:" u_se< "Csv". Put libs/Csv/src on XETAL_PATH ("just path"); the reference is libs/Csv/docs. Names with l: are exported; those under h: are private to this file.

A table is a matrix of texts (Box Char), a row per line, its first row the header. Fields follow RFC 4180: a field in double quotes may hold commas, and a doubled quote inside stands for one.

source · imports t: libs/Strings/src/Strings.xtl; q: libs/Lists/src/Lists.xtl

dq : Char

value (private) · line 15
dq ← "\""                                     ⍝ a double quote, as text

Parsing

ʰu̲nquote : Char -> Char

function (private) · line 20

A field as written to its value: outer quotes off, "" to ".

ʰu̲nquote ← { f →
  2 > t̲ally f ? f
  n̲ot (dq m̲atch 1 t̲ake f) ∧ dq m̲atch -1 t̲ake f ? f
  ((e̲nclose dq c̲at dq) c̲at e̲nclose dq) ᵗr̲eplace 1 d̲rop -1 d̲rop f
}
Used in: ˡf̲ields

ˡf̲ields : Char -> Box Char

function · line 29

f_ields line: the fields of one line. A comma separates fields unless an odd number of quotes comes before it (it is inside a quoted field).

ˡf̲ields ← { line →
  inside ← 1 = ('+ s̲\ 0 + line = f̲irst dq) m̲od 2
  cut ← w̲here (line = f̲irst ",") ∧ n̲ot inside
  starts ← 1 c̲at cut + 1
  ends ← cut c̲at 1 + t̲ally line
  '{ i → ʰu̲nquote ((f̲irst i s̲elect ends) − f̲irst i s̲elect starts) t̲ake ((f̲irst i s̲elect starts) − 1) d̲rop line } m̲ap r̲ange t̲ally starts
}
Used in: ˡr̲ows

ˡr̲ows : Char -> Box Char

function · line 39

r_ows text: the table of a whole text, a row per line (every line has the same number of fields).

ˡr̲ows ← { text →
  lines ← ᵗl̲ines text
  rows ← '{ l → ˡf̲ields d̲isclose l } m̲ap lines
  ((t̲ally lines) c̲at t̲ally d̲isclose f̲irst rows) r̲eshape q:r̲aze rows
}
Used in: t, t

Columns

ˡc̲olumn : Int -> a -> a

function · line 48

n c_olumn table: the texts of column n, the header left out.

ˡc̲olumn ← { n table → 1 d̲rop n s̲elect₂ table }
Used in: ˡf̲ield

ˡf̲ield : Eq a => a -> Box a -> Box a

function · line 51

name f_ield table: the texts of the column whose header is name.

ˡf̲ield ← { name table → (f̲irst (f̲irst table) i̲ndexOf e̲nclose name) ˡc̲olumn table }

ˡn̲umbers : Char -> Box Char -> Float

function · line 54

name n_umbers table: the column whose header is name, as numbers.

ˡn̲umbers ← { name table → n̲umbers " " ᵗj̲oin name ˡf̲ield table }
Used in: pop, area

Writing

ʰq̲uote : Char -> Char

function (private) · line 60

A value as a field: quoted (quotes doubled) when it holds a comma, a quote or a newline.

ʰq̲uote ← { v →
  n̲ot '∨ r̲/ v m̲ember? ",\n" c̲at dq ? v
  dq c̲at (((e̲nclose dq) c̲at e̲nclose dq c̲at dq) ᵗr̲eplace v) c̲at dq
}
Used in: ˡt̲ext

ˡt̲ext : Box Char -> Char

function · line 66

t_ext table: the table as comma-separated text, a line per row.

ˡt̲ext ← { table →
  "\n" ᵗj̲oin '{ i → "," ᵗj̲oin '{ f → ʰq̲uote d̲isclose f } m̲ap i s̲elect table } m̲ap r̲ange t̲ally table
}