sourcedemos/rosetta/rosetta.xtl
1⍝!/usr/bin/env xetal
2⍝ The Rosetta stone (docs/rosetta.md): how languages write the same
3⍝ idiom, on an impossible object. The whole stone rolls through the
4⍝ idioms; its top half turns through the languages; its bottom half
5⍝ turns, on its own, through the languages compared with. The data is
6⍝ demos/rosetta/data.toml, read as aligned arrays over the two axes;
7⍝ the state machine is Comparison.xtl, the geometry Stone.xtl. The
8⍝ program runs on events ([]E_VENT): each tick eases the axes and
9⍝ shows a frame; keys turn the axes (Up/Down the idioms, Left/Right
10⍝ the top language, a/d the bottom; space, s, w pause a half or the
11⍝ drum); the end of the events prints the state and stops.
12ᶜᵐ⁼u̲se< "Comparison"
13ˢᵗ⁼u̲se< "Stone"
14ᵛ⁼u̲se< "Svg"
15
16data ← "demos/rosetta/data.toml"
17idioms ← data ⎕L̲IST "idioms"
18idiomNames ← data ⎕L̲IST "idiom_names"
19languages ← data ⎕L̲IST "languages"
20languageNames ← data ⎕L̲IST "language_names"
21ᵘt̲able ← { name → ((e̲nclose data) c̲at e̲nclose name) ⎕T̲ABLE "idioms" "languages" }
22source ← ᵘt̲able "source"
23output ← ᵘt̲able "output"
24notes ← ᵘt̲able "notes"
25spans ← ᵘt̲able "spans"
26
27⍝ The colors of code on a face, by class: X_eTaL's classes as []V_IEW
28⍝ names them, and the same names for the spans other languages' cells
29⍝ may carry in data.toml.
30ᵘc̲olor ← { class →
31 class m̲atch "builtin" ? "#1d4ed8"
32 class m̲atch "userfunc" ? "#7c3aed"
33 class m̲atch "libfunc" ? "#7c3aed"
34 class m̲atch "macro" ? "#7c3aed"
35 class m̲atch "number" ? "#b45309"
36 class m̲atch "string" ? "#15803d"
37 class m̲atch "symbol" ? "#111827"
38 class m̲atch "comment" ? "#6b7280"
39 class m̲atch "lambdaarg" ? "#0f766e"
40 class m̲atch "keyword" ? "#1d4ed8"
41 class m̲atch "function" ? "#1d4ed8"
42 class m̲atch "operator" ? "#111827"
43 class m̲atch "name" ? "#000000"
44 "#000000"
45}
46
47⍝ Runs of classified text (an n x 2 matrix of boxed texts, text then
48⍝ class) as colored markup for a face.
49ᵘr̲uns ← { m → ᵘj̲oin '{ k → (ᵘc̲olor d̲isclose 2 s̲elect k s̲elect m) ᵛs̲pan d̲isclose 1 s̲elect k s̲elect m } m̲ap r̲ange 1 s̲elect s̲hape m }
50
51⍝ The classes of a cell that has no spans of its own, guessed from its
52⍝ characters, as the same n x 2 matrix: digits are
53⍝ numbers, letters and the underscore names, spaces plain, and
54⍝ everything else a built-in, which in an array language's glyphs is
55⍝ nearly always right. Runs of one class are one span; the work is
56⍝ all primitives (one lambda per run, not per character).
57ᵘg̲uess ← { src →
58 cls ← (src m̲ember? "0123456789") + (2 × src m̲ember? "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_") + 3 × src = f̲irst " "
59 starts ← 1 c̲at (1 d̲rop cls) ≠ -1 d̲rop cls
60 runs ← ('+ s̲\ starts) p̲artition src
61 kinds ← (e̲nclose "builtin") c̲at (e̲nclose "number") c̲at (e̲nclose "name") c̲at e̲nclose "plain"
62 classes ← (1 + (w̲here starts) s̲elect cls) s̲elect kinds
63 o̲\ (2 c̲at t̲ally runs) r̲eshape runs c̲at classes
64}
65
66⍝ A cell's spans from data.toml, "class text|class text|...", as the
67⍝ same n x 2 matrix; a cell without spans has its classes guessed.
68ᵘs̲pans ← { src spans →
69 0 = t̲ally spans ? ᵘg̲uess src
70 pieces ← (n̲ot spans = f̲irst "|") p̲artition spans
71 o̲ne ← { p → t ← d̲isclose p◆ cut ← t i̲ndexOf f̲irst " "◆ (e̲nclose cut d̲rop t) c̲at e̲nclose (cut − 1) t̲ake t }
72 ((t̲ally pieces) c̲at 2) r̲eshape d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at d̲isclose y } r̲/ 'o̲ne m̲ap pieces
73}
74
75⍝ The source of a cell as colored markup: X_eTaL source through
76⍝ []V_IEW (so the coloring is the language's own), any other through
77⍝ the cell's spans when it has them.
78ᵘc̲olored ← { il src →
79 i ← 1 s̲elect il
80 l ← 2 s̲elect il
81 (d̲isclose l s̲elect languages) m̲atch "xetal" ? ᵘr̲uns ⎕V̲IEW src
82 ᵘr̲uns src ᵘs̲pans d̲isclose l s̲elect i s̲elect spans
83}
84
85⍝ How many characters a line of code fits at full size (the code is a
86⍝ tenth of the face high and about 0.6 as wide), and the smallest the
87⍝ code may get.
88fits ← 15
89smallest ← 0.05
90
91⍝ A line of markup of n visible characters, shrunk to fit the face when
92⍝ it is longer than fits characters (never below smallest).
93ᵘf̲itted ← { n markup →
94 n ≤ fits ? markup
95 size ← 0.1 × (f̲loat fits) ÷ f̲loat n
96 (size m̲ax smallest) ᵛs̲ized markup
97}
98
99⍝ Source too long for one line split in two at the space nearest its
100⍝ middle, as a box of one or two texts (a text without spaces stays
101⍝ whole, however long).
102ᵘs̲plit ← { src →
103 n ← t̲ally src
104 n ≤ fits ? e̲nclose src
105 spaces ← w̲here src = f̲irst " "
106 0 = t̲ally spaces ? e̲nclose src
107 away ← a̲bs (2 × spaces) − n
108 cut ← (away i̲ndexOf 'm̲in r̲/ away) s̲elect spaces
109 (e̲nclose (cut − 1) t̲ake src) c̲at e̲nclose cut d̲rop src
110}
111
112⍝ Words (a box of texts) as lines of at most width characters, as a box
113⍝ of texts; a word longer than the width is a line of its own.
114ᵘl̲ines ← { width ws →
115 0 = t̲ally ws ? ws
116 lens ← '{ b → t̲ally d̲isclose b } e̲ach ws
117 room ← width ≥ (('+ s̲\ lens) + r̲ange t̲ally ws) − 1
118 k ← 1 m̲ax '+ r̲/ room
119 line ← d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at " " c̲at d̲isclose y } r̲/ k t̲ake ws
120 (e̲nclose line) c̲at width ᵘl̲ines k d̲rop ws
121}
122
123⍝ A text wrapped at its spaces into lines of at most width characters.
124ᵘw̲rap ← { width t → width ᵘl̲ines (n̲ot t = f̲irst " ") p̲artition t }
125
126⍝ A result's lines as one, " / " between them (a matrix, or a language's
127⍝ own box drawing of one, on a face's single result line).
128ᵘo̲neLine ← { out → d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at " / " c̲at d̲isclose y } r̲/ (n̲ot out = f̲irst "\n") p̲artition out }
129
130⍝ The lines of a language's face for an idiom: its name, the source
131⍝ colored (on two lines when it is long, each shrunk to fit), and the
132⍝ output when there is one; or the note for a missing source, wrapped.
133⍝ Each line is markup.
134ᵘf̲ace ← { i l →
135 name ← ᵛe̲scape d̲isclose l s̲elect languageNames
136 src ← d̲isclose l s̲elect i s̲elect source
137 out ← d̲isclose l s̲elect i s̲elect output
138 note ← d̲isclose l s̲elect i s̲elect notes
139 missing ← "0 = t_ally note" i̲f< "\"(no concise idiom)\"; note"
140 result ← "0 = t_ally out" i̲f< "\"\"; \"-> \" c_at u:o_neLine out"
141 n̲oteLine ← { b → 0.07 ᵛs̲ized "#4b5563" ᵛs̲pan d̲isclose b }
142 0 = t̲ally src ? (e̲nclose name) c̲at 'n̲oteLine m̲ap 18 ᵘw̲rap missing
143 c̲odeLine ← { b → (t̲ally d̲isclose b) ᵘf̲itted (i c̲at l) ᵘc̲olored d̲isclose b }
144 code ← 'c̲odeLine m̲ap ᵘs̲plit src
145 (e̲nclose name) c̲at code c̲at e̲nclose (t̲ally result) ᵘf̲itted "#4b5563" ᵛs̲pan result
146}
147
148⍝ The item an axis's side shows: the one its turn started from plus
149⍝ the side's offset, wrapped. (axis offset) ᵘi̲tem state.
150ᵘi̲tem ← { ao state →
151 axis ← 1 s̲elect ao
152 n ← f̲loor 0.5 + (axis c̲at ᶜᵐCOUNT) ᶜᵐa̲t state
153 n ᶜᵐw̲rap 1 + (f̲loor 2 s̲elect ao) + ˢᵗb̲ase (axis c̲at ᶜᵐANGLE) ᶜᵐa̲t state
154}
155
156⍝ The stone as a picture of a state: the two halves' sides show the
157⍝ languages for the idiom the stone stands on, the top half's lid and
158⍝ the bottom half's floor name the neighboring idioms, the caps between
159⍝ the halves are bare; everything is painted far to near as one solid.
160⍝ Faces 1 to 6 are the top half's (front, right, back, left, lid,
161⍝ floor), 7 to 12 the bottom half's.
162ᵘs̲tone ← { state →
163 i ← (ᶜᵐIDIOM c̲at 0.0) ᵘi̲tem state
164 roll ← (ᶜᵐIDIOM c̲at ᶜᵐANGLE) ᶜᵐa̲t state
165 top ← 1 ˢᵗr̲ing ((ᶜᵐTOP c̲at ᶜᵐANGLE) ᶜᵐa̲t state) c̲at roll
166 bottom ← -1 ˢᵗr̲ing ((ᶜᵐBOTTOM c̲at ᶜᵐANGLE) ᶜᵐa̲t state) c̲at roll
167 all ← top c̲at bottom
168 c̲apName ← { k → ᵛe̲scape d̲isclose ((ᶜᵐIDIOM c̲at f̲loat k s̲elect ˢᵗcapOffsets) ᵘi̲tem state) s̲elect idiomNames }
169 c̲apText ← { k →
170 k = 5 ? c̲apName 1
171 k = 12 ? c̲apName 2
172 ""
173 }
174 c̲ap ← { k → (e̲nclose "#e9e4db") c̲at (e̲nclose "") c̲at e̲nclose c̲apText k }
175 t̲opPanel ← { k → (e̲nclose "#fdfcfa") c̲at i ᵘf̲ace (ᶜᵐTOP c̲at f̲loat k s̲elect ˢᵗringOffsets) ᵘi̲tem state }
176 b̲ottomPanel ← { k → (e̲nclose "#f4f1ec") c̲at i ᵘf̲ace (ᶜᵐBOTTOM c̲at f̲loat k s̲elect ˢᵗringOffsets) ᵘi̲tem state }
177 l̲ines ← { k →
178 k ≤ 4 ? t̲opPanel k
179 k ≤ 6 ? c̲ap k
180 k ≤ 10 ? b̲ottomPanel k − 6
181 c̲ap k
182 }
183 p̲anel ← { k → (l̲ines k) ˢᵗp̲anel k s̲elect all }
184 painted ← ᵘj̲oin 'p̲anel m̲ap ˢᵗp̲ainting all
185 caption ← ((ᵛa̲t (ˢᵗsize ÷ 2) c̲at 28) c̲at ("font-size" ᵛa̲ttr "18") c̲at ("text-anchor" ᵛa̲ttr "middle") c̲at (ᵛf̲ill "#374151") c̲at "font-family" ᵛa̲ttr "ui-sans-serif, system-ui, sans-serif") ᵛt̲ext d̲isclose i s̲elect idiomNames
186 (ˢᵗsize c̲at ˢᵗsize) ᵛp̲icture caption c̲at painted
187}
188
189ᵘj̲oin ← { b → d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at d̲isclose y } r̲/ b }
190
191⍝ Where the stone stands, for the host: "at IDIOM TOP BOTTOM" by their
192⍝ keys, printed whenever it changes (the page reads it for its controls
193⍝ and the address bar). It names what the faces show, which changes
194⍝ when a turn lands, not what an axis is turning toward (its steps),
195⍝ so the page's lists never run ahead of the picture.
196ᵘw̲here ← { s →
197 i ← d̲isclose ((ᶜᵐIDIOM c̲at 0.0) ᵘi̲tem s) s̲elect idioms
198 t ← d̲isclose ((ᶜᵐTOP c̲at 0.0) ᵘi̲tem s) s̲elect languages
199 b ← d̲isclose ((ᶜᵐBOTTOM c̲at 0.0) ᵘi̲tem s) s̲elect languages
200 "at " c̲at i c̲at " " c̲at t c̲at " " c̲at b
201}
202
203⍝ The loop: an event, the state after it, a frame on each tick, the
204⍝ position and the tour's mode when they change ("at IDIOM TOP
205⍝ BOTTOM", "mode touring|holding|paused..."); the state when the
206⍝ events end.
207ᵘl̲oop ← { state →
208 e ← ⎕E̲VENT @
209 kind ← ⎕E̲KIND e
210 kind m̲atch "end" ? state
211 next ← state ᶜᵐu̲pdate e
212 "(u:w_here state) m_atch u:w_here next" u̲nless< "p_rint! u:w_here next"
213 "(cm:m_ode state) m_atch cm:m_ode next" u̲nless< "p_rint! \"mode \" c_at cm:m_ode next"
214 "n_ot kind m_atch \"tick\"" u̲nless< "[]S_HOW u:s_tone next"
215 ᵘl̲oop next
216}
217
218⍝ Start at the beginning of the tour: the first idiom, the first
219⍝ language on top and the second below, at rest, every axis playing
220⍝ (the attract mode begins after the first dwell).
221start ← ᶜᵐr̲esume ᶜᵐs̲ettle (ᶜᵐBOTTOM c̲at 2.0) ᶜᵐc̲hoose (t̲ally idioms) ᶜᵐs̲tart t̲ally languages
222shown ← p̲rint! ᵘw̲here start
223mode ← p̲rint! "mode " c̲at ᶜᵐm̲ode start
224ᵘl̲oop start
u̲nless< expands to
{ @ → ((ᶜᵐm̲ode state) m̲atch ᶜᵐm̲ode next) ? @◆ p̲rint! "mode " c̲at ᶜᵐm̲ode next◆ @ } @