sourcelibs/Csv/src/Csv.xtlm
1⍝# Csv: its macro library -- c_olumns<, a table's columns as named,
2⍝# typed variables.
3⍝# Imported with the functions, under one alias: "cs:" u_se< "Csv".
4⍝# A macro is a function from the source text written left and right of
5⍝# its call to the source that replaces the call, run before the program
6⍝# is compiled (X_eTaL MC10). Names with m: are macros; those under h: are
7⍝# private to this file.
8
9ᵗ⁼u̲se< "Strings"
10
11lowers ← "abcdefghijklmnopqrstuvwxyz"
12letters ← lowers c̲at ⎕A c̲at ⎕D
13
14⍝## Parsing
15
16⍝# Whether a text can be a variable's name.
17ʰn̲ame? ← { n → (0 < t̲ally n) ∧ ((f̲irst n) m̲ember? lowers) ∧ '∧ r̲/ n m̲ember? letters }
18
19⍝# A column's name and kind, from "name:kind".
20ʰc̲ut ← { c → (f̲irst w̲here c = f̲irst ":") − 1 }
21ʰc̲olName ← { c → (ʰc̲ut c) t̲ake c }
22ʰc̲olKind ← { c → (1 + ʰc̲ut c) d̲rop c }
23
24⍝# Whether a column is "name:text" or "name:number".
25ʰc̲olumn? ← { c →
26 1 ≠ t̲ally w̲here c = f̲irst ":" ? 0 = 1
27 (ʰn̲ame? ʰc̲olName c) ∧ ("text" m̲atch ʰc̲olKind c) ∨ "number" m̲atch ʰc̲olKind c
28}
29
30⍝# The code for one column of table tb: its texts, found by header, the
31⍝# header left out; numbers read from them when the kind is number.
32ʰc̲ode ← { tb c →
33 n ← ʰc̲olName c
34 texts ← "(1 d_rop ((f_irst " c̲at tb c̲at ") i_ndexOf e_nclose \"" c̲at n c̲at "\") s_elect_2 " c̲at tb c̲at ")"
35 "text" m̲atch ʰc̲olKind c ? n c̲at " := " c̲at texts
36 n c̲at " := n_umbers d_isclose '{ e_nclose (d_isclose _l) c_at \" \" c_at d_isclose _r } r_/ " c̲at texts
37}
38
39⍝## The macro
40
41⍝# "city:text population:number" cs:c_olumns< "t": the columns of table
42⍝# t (a table as cs:r_ows gives it, its first row the header) as
43⍝# variables named after them: city the texts of column "city",
44⍝# population its numbers (Floats). When the program is compiled the
45⍝# schema is checked and a definition written per column, so the rest
46⍝# of the program uses the columns by name and their kinds are
47⍝# type-checked like any variable (population c_at "x" is a type
48⍝# error). A malformed schema or table name stops the compiler:
49⍝# error[bad-columns]. Columns are found by their header when the
50⍝# program runs.
51ᵐc̲olumns< ← { schema tb →
52 cs ← ᵗw̲ords schema
53 0 = t̲ally cs ? "bad-columns left" ⎕R̲EJECT "no columns: write name:text or name:number"
54 bad ← (n̲ot '{ ʰc̲olumn? d̲isclose ⍵ } e̲ach cs) r̲eplicate cs
55 0 < t̲ally bad ? "bad-columns left" ⎕R̲EJECT (d̲isclose f̲irst bad) c̲at " is not a column: write name:text or name:number (a lowercase name)"
56 n̲ot ʰn̲ame? ᵗt̲rim tb ? "bad-columns right" ⎕R̲EJECT tb c̲at " is not a table's name"
57 "\n" ᵗj̲oin '{ c → (ᵗt̲rim tb) ʰc̲ode d̲isclose c } m̲ap cs
58}