1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
|
# The Markdown function is a markdown to html converter for a "good
# enough" subset of Github-flavored markdown, as specified at
# https://github.github.com/gfm/ .
#
# Additionally, it highlights code sections as BQN, and executes
# sections that are doubly indented (eight spaces), placing their
# results below them.
# Not supported:
# - Thematic breaks like *** or ---
# - Setext headings (underlined with ==== or ----)
# - Fenced code blocks (marked off with ``` or ~~~)
# - HTML blocks
# - Link reference definitions (who uses these?)
# - Block quotes (start with >)
# - Task lists
# Here, a markdown file is represented as a list of its lines, which are
# strings (they don't include any line ending character).
# The html file is constructed directly as a string, using Html.
################################
# Utilities
# Shift cells π¨ into array π©, maintaining its total length
Shl β β ββ’ β βΎ # From the left
Shr β -ββ ββ’ β βΎΛ # From the right
# Index of first zero, or number of leading 1s in a boolean list
Lead β β ββ0
# π¨ is a list of lists. Find the first of these lists each cell of π©
# belongs to.
FindGroup β {
i β (βΎπ¨) β π© # Index in all cells of π¨
e β +`β Β¨π¨ # Index past the end of each group of π¨
e β i # How many end-indices does each element pass?
}
# Count the number of consecutive true values up to the current element.
# To do this, subtract the index of the last false character from the
# current index.
CountRuns β { (1+ββ π©) (β£ - β`βΓ) Β¬π© }
# π© is a string; return a mask of the characters that are escaped, that
# is, preceded by an odd number of backslashes (since a backslash can
# escape another backslash).
IsEscaped β {
0 Shl 2 | CountRuns π© = '\'
}
# Remove leading (β§`) and trailing (β§`βΎβ½) spaces
Trim β { π© /Λ Β¬ (β§` β¨ β§`βΎβ½) ' '=π© }
# Find whether π¨ was true at the last index where π© was false, in each
# position.
PrecedesGroup β {
# We prepend a 0 to π¨, so that 0 is the "before start" index, with a
# false value, and normal indices are increased by 1.
π¨ βΎΛβ© 0
inds β 1 + ββ π©
# Zero out indices where x was true, and find the greatest index so
# far at each position.
last β β` inds Γ Β¬π©
last β π¨
}
# π¨ is a list of possible expression start indices in any order and π© is
# the corresponding endpoints. The expressions are mutually exclusive
# and do not nest, and are enabled in index order. Return a shape Β·βΏ2
# array where the rows give the start and end of each enabled expression
# in index order.
Trace β {
# π¨ is a list with one index for each possible start, giving a later
# start that is known to be enabled if that one is.
# π© is a mask of all starts known to be enabled.
# A "stop" position that follows all expressions tells when to stop.
# At each step the distance from a start to its successor in π¨ is
# doubled, so the maximum number of steps is about 2ββΌβ π©.
En β {
π© β© 1Β¨βΎ((π©/π¨)βΈβ)π© # Starts following from an enabled one are enabled
π¨ β© βΛ π¨ # Double the number of steps in π¨
π¨ En π© # Repeat
}β{0=Β―1βπ©} # until the stop is enabled
g β βπ¨ # Order expressions by starting index
start β gβπ¨
end β gβπ©
next β start β end # An expression's successor starts after it ends
next βΎβ© β next # The stop node is its own successor
enabled β Β―1 β next En (β next)β1 # Search and remove the stop
enabled / startβΛend # List of enabled starts and ends
}
# Join lines with newline characters. Include a trailing newline.
JoinLines β βΎ βΎβlfΒ¨
# Create an html node from a tag name and interior text.
Html β {open π contents:
close β (βopenβ" ") β open
βΎ β¨"<",open,">" , contents , "</",close,">"β©
}
# Insert and remove things from the list π©:
# - include is the mask of elements to keep in π©
# - add is a list of lists to be inserted
# - pos is the list of positions where they should start
# Elements are added just after the given position in π©, in the order
# they appear in βΎadd.
Modify β { β¨include,add,posβ©ππ©:
((/include)βΎ(β Β¨add)/pos) ββΈβ (include/π©)βΎβΎadd
}
################################
Markdown β {filenameππ©:
extensions β filename β’ 0
path β extensionsβΆ""βΏ(β’/ΛΒ·β¨`βΎβ½'/'βΈ=) filename
######
# First we classify each line based on the type of block it can start.
ClassifyLine β (0<β )βΆ(0βΏ0)βΏ{
ind β β lineChars FindGroup βπ©
getLen β ind β lineClasβΎβ¨0Λβ©
l β GetLen π©
β¨ind β§ l>0 β lβ©
}
# Character entity escaping
# In order to use this with other modifications such as highlighting,
# CharEntities returns a mask of characters to be escaped, and their
# corresponding escapes.
CharEntities β {1Β¨βΈππ©; # π¨ gives characters to potentially escape
# The string gives escapes and their names, separated by spaces.
# First split it on the first character.
ce β (1-ΛΒ¬Γ+`)β=βββΈβ " ""quot & <lt >gt"
# Characters to escape are given first
chars β βΒ¨ce
# HTML character entities start with & and end with ;
entities β ("&"βΎβΎβ";")Β¨ 1βΒ¨ce
# Replace a character if π¨ is not set and it's on our list.
ind β chars β π©
useEntity β π¨ β§ ind < β chars
β¨Β¬ useEntity , entities βΛ useEntity/ind , /useEntityβ©
}
# Non-empty lines in code blocks have 4 leading spaces
ProcCode β {
# Strip the leading spaces
π© β© 4 βΒ¨ π©
code β JoinLines π©
# Highlight and unescape html-unsafe characters
cβΏci β extensionsβΆ(2β₯<β¨β©)βΏGetHighlights code
emβΏeβΏei β CharEntities code
# If every line is indented by at least 4 additional spaces, we will
# execute each one and insert the results.
addRslt β β§Β΄ ' ' = βΎ 4 (βββ β β’)Β¨ π©
# Don't show assignment results by default
ShowRslt β {
depth β +` "(){}β¨β©" (β£(β βΈ>ΓΒ―1β2|β’)β) π©
π© /Λβ© Β¬ β¨`βΎβ½ (0=depth) β§ π©β"β," # Just the last statement
g β π©β"ββ©"
(βgβ1) (<β(β g))βΆβ¨1,Β¬(" "βΎβΎidChars)β§Β΄ββΛββ© π©
}
rβΏri β addRsltβΆ(2β₯<β¨β©)βΏ{
β¨ (ShowRslt β£βΆβ¨"",(β₯βΎβlfβ1)βFmtββ’β© CodeExec)β(0<β )Β¨ π©
1 -Λ +` 1 + β Β¨ π© β© # Don't forget the trailing newline
} π©
mod β β¨em,eβΎcβΎr,eiβΎciβΎriβ© Modify code
"pre" Html "code" Htmlβ(Β¬extensions) mod
}
CodeExec β X # dzaima+reference exec. Should be {β}
# Headings start with #, and require 1-6 #s followed by a space.
# Any trailing #s are ignored.
LenHeading β {
n β Lead π©='#'
l β (0<n) β§ (6β₯n)
s β n (<ββ )βΆβ¨1,' '=ββ© π© # Character after hashes must be a space, if any
n Γ l β§ s
}
ProcHeading β {
tag β "h" βΎ π¨ββ’d # h3 for 3 hashes, etc.
π© βΛβ© π¨+1
trsp β β§`βΎβ½ π©=' '
tail β β§`βΎβ½ trspβ¨π©='#' # Mask of trailing hashes
f β tail < 0 Shr tail # Character before trailing hashes
π© /Λβ© Β¬ f (ββ¨"\"," ",""β©β<f/π©)βΆβ¨β£,β’,β’,0Β¨β’β© tail
# Add an id, containing only a-z, digits, and hyphens
Slugify β {
π© β© '-'Β¨βΎ((π©=' ')βΈ/) π© # Replace spaces with dashes
bounds β β₯ +ββ0βΏ26βΎβ’UCS "Aa" # Of the upper and lowercase alphabet
# Lowercase alphabetic characters and remove special characters
b β bounds β π©
((2|b)β¨ββ("-"βΎβ’d))βΈ/ +β(32Γ1=b)βΎβ’UCS π©
}
extensions { tag βΎβ© " id="βΎ""""(βΎβΎβ£) Slugify π© }ββ£ π©
tag Html ProcInline Trim π©
}ββ
# List items start with a bullet (unordered) or number (ordered).
LenBullet β +βΓ Β·β€β4βΈΓ Β·Lead ' '=1βΈβ
ProcBullet β {
"ul" Html lf βΎ JoinLines ("li" Html ProcInline)Β¨ π¨ βΒ¨ π©
}
LenListNum β { # Not used yet
n β Lead π©ββ’d
l β (1β€n) β§ (9β₯n)
' ' = n β π©
t β nβ(n+2)βπ©
l β§ (" " β‘ 1βt) β§ β(")." βΛ 1βt)
}
# Table detection handled specially because the spec is... special
CutTableRow β {
b β '|' = π© # Mask of bars
o β (Β¬b) β βLead ' '=π© # Leading | omitted
r β b > 0 Shl '\' = π© # Non-escaped bars
1 -Λ (Β¬rβ¨1β½b>r) Γ o + +` r
}
ProcTable β {
rows β (TrimΒ¨ CutTableRowβΈβ)Β¨ π©
inc β Β¬ rule β β§Β΄ββΎΒ¨'-'=rows
rows β© ProcInline¨¨βΎ(incβΈ/) rows
rows β© (βrows) (β’ βΎ β¨""β© /Λ 0β-ββ )Β¨ rows
rowType β inc / +` rule # Head or body
DoRow β { lf βΎ JoinLines π¨βΈHtmlΒ¨ π© }
rows β© (rowType β "th"βΏ"td") DoRowΒ¨ inc/rows
rowGroups β Β―1 β rowType ββ(βΎβ2) "tr"βΈHtmlΒ¨ rows
sections β "thead"βΏ"tbody" Htmlβ(lf βΎ JoinLines)Β¨ rowGroups
"table" Html lf βΎ JoinLines (0 < β Β¨rowGroups) / sections
}
# Paragraphs
ProcParagraph β {
"p" Html ProcInline Β―1 β JoinLines TrimβΎ(Β―1βΈβ) (Lead ' 'βΈ=)βΈβΒ¨ π©
}
# HTML blocks
# Lazy rule: if it starts with < and contains >, it's probably HTML
IsHtmlBlock β β ">"βΈβ
ProcHtmlBlock β {
codeMask β "<code>" Β―6βΈβ½βΈ(>β(β`(1+βββ )βΈΓ))β(β·βπ© βΎ 0β₯Λ1-Λβ ) "</code>"
(1Β¨ <βΈβΎ codeMaskβΈGetMultiHighlights)βΈModify π©
}βextensionsβJoinLines
lineCharsβΏlineClasβΏprocFns β <Λβ>β¨
"" βΏ (!β0) βΏ ProcParagraph
"#" βΏ LenHeading βΏ ProcHeading
"" βΏ 0 βΏ ProcCode
"" βΏ 0 βΏ ProcTable
"-+*" βΏ LenBullet βΏ ProcBullet
# β’d βΏ LenListNum βΏ ProcListNum
"<" βΏ IsHtmlBlock βΏ ProcHtmlBlock
β©
######
# Inline elements
ProcInline β {
I2M β (β π©) β /βΌ # Index to mask
punc β π© β "!""#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
actual β Β¬ punc β§ IsEscaped π© # backtick or *actual* backtick?
# Code spans
tick β π© = '`'
tend β / (β’ > 0βΈShr) tick
tcount β CountRuns tick
# π¨ are tick lengths and π© are positions, both sorted by length
MatchTicks β {
# Tick runs other than the last of each length
notLast β (β’=0βΈShr) π¨
# Ticks preceded by backslashes can't start code blocks, but can
# end them. This approach is wrong for multiple ticks with a
# leading backslash in front, which are excluded but should just
# be treated as one shorter when leading.
filter β notLast / (π©Β¬π¨) β actual
# For leading ticks, filter by not-last; for trailing ones, rotate
# by Β―1 to filter by not-first.
(filter / β½βnotLast / π©Λ)Β¨ 0βΏΒ―1
}
tlen β tend β tcount
c β TraceΒ΄ tlen MatchTicksβ((βtlen)βΈβ) tend
cl β (βΛc) β tcount
ctInds β β₯Λ 1 + c -βΛ clΓβ1βΏ0
codeMask β β ` I2M β₯ codeBounds β 1βΏ2βΈβΛ ctInds
π© β© ' 'Β¨βΎ((codeMaskβ§π©=lf)βΈ/) π©
# If span has both a leading and a trailing space, they are removed.
remSpace β I2M β₯ ((1<-ΛΛΛ)β§Β·β§ΛΛ' '=ββπ©)βΈ/ -β0βΏ1Λ codeBounds
codeMask β§β© Β¬ remSpace
β¨code,codePosβ© β codeMask extensionsβΆ(2β₯<β¨β©)βΏGetMultiHighlights π©
include β Β¬ remSpace β¨ β ` I2M β₯ ctInds
codeBounds β© β₯ -β1βΏ0Λ codeBounds
unused β actual β§ include β§ Β¬ codeMask
# Links
ghPath β "https://github.com/mlochbaum/BQN/blob/master/"βΎpath
ReplaceMDSub β { Β―2 ("md"β‘β)βΆ(ghPathβΈβΎ)βΏ("README"β’_r_"index"βββΎ"html"Λ)β(':'β§Β΄ββ β’) π© }
ReplaceMD β { ReplaceMDSubβ(0<β )βΎ((βπ©β"#")βΈβ) π© }
ProcLink β { βΎβ¨"<a href=""",(ReplaceMD π©),""">",ProcInline π¨,"</a>"β© }
# Find matched-depth [] and () pairs, then join adjacent ones
brak β (unused β§ π©βΈ=)Β¨ 2βΏ2β₯"[]()"
depth β (+`-0ββ’)β(-Β΄)Λ brak
FindPairs β β¨β©βΏ2 β₯ 1βΈβ /Λ 2βΈβ β (Β―ββΈShlβΈ=(β§+β’)ββΈShrβΈ=)ββ
pairs β depth <β(FindPairsβ(0<β ))βββ(β§(βΛβΛβ’)ββΎβΎΛΒ·/β Β¨ββ’)Λ /Β¨brak
JoinPairs β {
eβ1+1βΛπ¨ β bββΛπ© β mβ(β b)>iβbβe
(m/π¨) βΎΛ (m/i)βπ©
}
lInds β β§β(0<β )βΆ(0βΏ4β₯0)βΏJoinPairsΒ΄ pairs
linkPos β βΛ lInds
lInds +β1β© 1βΏ0βΏ1βΏ0
unused β§β© include β§β© Β¬ β ` I2M β₯ (Β―1βΏ1+0βΏ3βΈβ)Λ lInds
linkGroup β 1 -Λ (1βΏ0β₯Λβ’)βΈ(/ (β£Γ>)β(+`I2M) Β¬βΈ/) β₯lInds
links β <βProcLinkΒ΄Λ (lIndsβ βΈβΎ2) (β£β₯ΓΒ΄βΈβ) linkGroup β π©
# Emphasis (still rudimentary)
eMasks β (unused β§ π©βΈ=)Β¨ "*_"
eMasks β© 0βΈShrβΈβ§Β¨βΈ(β£βΎΛ0βΈShlβΈβ¨βΈ<Β¨) eMasks
eInds β (β’-2|β’)ββ βΈββ/Β¨ eMasks
include β§β© Β¬ I2M β§ βΎ eIndsβΎ1+2βeInds
eInds βΎβ© β¨codeBoundsβ©
eTags β βΎ eInds β βΈβ₯Β¨ 2βΏ2βΏ1 / ("<"βΏ"</"βΎΒ¨Β·<βΎβ">")Β¨ "em"βΏ"strong"βΏ"code"
eInds β© βΎ eInds
# Remove backslashes used for escaping
include β§β© codeMask β¨ 1 β½ actual
emβΏentβΏei β include CharEntities π©
include β§β© em
add β βΎβ¨eTags,ent,code,linksβ© # Text to be added
pos β βΎβ¨eInds,ei,codePos,linkPosβ© # Where to add it
β¨include,add,posβ© Modify π©
}
######
# Create the block structure using line classifications.
# First remove the html link line: the output *is* the html file.
π© β© 2βΈββ("*View this file" (β£ β‘ βββ ββ’) β) π©
lengths β β Β¨ π© # Length of each line
blanks β (Lead ' 'βΈ=)Β¨ π© # Number of leading blanks
nonEmptyMask β blanks < lengths # Empty ββ all leading blanks
# Get line classifications: type of line, and data to be passed into
# the line processor. Note that leading blanks aren't passed in.
lineTypeβΏlineDat β <Λβ > ClassifyLineΒ¨ blanks βΒ¨ π©
# Empty lines have type Β―1.
lineType β© Β―1Β¨βΎ((Β¬nonEmptyMask)βΈ/) lineType
# Lines that could be included in code blocks (will be refined)
codeMask β nonEmptyMask β§ blanks β₯ 4
paragraphMask β 0 = lineType
# A header can't have 4 spaces of indentation. If it doesn't become
# part of a code block, it will be included in a paragraph.
lineType -β© codeMask β§ 1 = lineType
# Tables are made up of rows that would otherwise be paragraph rows.
# They are indicated by the delimiter row, consisting of only a few
# allowed characters, preceded (!) by a header row with the same
# number of cells.
IsTD β (β§Β΄ β βΎ β£ βΛ 2ββ’)β"-|: "
tableMask β (0βΎβ nonEmptyMask) β§ paragraphMask β§Β¬ codeMask
tableDelimMask β { π© IsTDΒ¨ββ£βΎ(π¨βΈ/) π¨ }βπ© tableMask
delimValid β (β’ =β(β βββCutTableRowΒ¨ ββπ©) -β1) / tableDelimMask
headerMask β 1 β½ delimValidβΎ(tableDelimMaskβΈ/) 0Β¨π©
tableMask β© headerMask (β’ β§ β£ β¨ β£ PrecedesGroup <) tableMask
lineType β© 3Β¨βΎ(tableMaskβΈ/) lineType
# Code blocks consist of indented lines, possibly with blank lines
# in between. They must be separated from paragraphs by blank lines.
codeMask β§β© Β¬ paragraphMask PrecedesGroup codeMask
codeMask β¨β© codeMask (β’ β§ PrecedesGroup β§ PrecedesGroupβΎβ½) lineType < 0
lineType β© 2Β¨βΎ(codeMaskβΈ/) lineType
# Lines continue blocks if they are part of the same multi-line
# type as the previous line, and otherwise start new ones.
# Headers (type 1) always start new blocks.
blockStart β nonEmptyMask β§ (1 = lineType) β¨ Β―1βΈShlβΈβ lineType
# Headers and paragraphs ignore leading blanks.
drop β blanks Γ lineType < 2
# Group blocks based on blockStart, with type Β―1 lines excluded.
blocks β (1 -Λ (lineType β₯ 0) Γ +`blockStart) β drop βΒ¨ π©
# To process a block, pick the appropriate function from procFns.
ProcBlock β {tβΏl G b: fβtβprocFns β l F βb }
JoinLines (blockStart / lineTypeβΛlineDat) <βProcBlockΛ blocks
}
################################
# Testing
# Uses the test cases at https://spec.commonmark.org/0.29/spec.json
# since Github doesn't seem to have published theirs
TestSections β {
tests β Β―2 βΛ 8βΈ(Γ·Λββ βΎβ£)βΈβ₯2ββ’LNS β’pathβΎ"spec.json"
tests β© ((β2+ββ':')Β¨ββ ((-','=Β―1ββ’)ββ)Β¨β1 β’) tests
testSection β (1βΒ―1ββ’)Β¨ 5βΛtests
UnEscape β {
EscapeChar β { ("\""tn"βπ©) β "\"""βΎβ’UCS 9βΏ10 }
esc β IsEscaped π©
(Β¬1β½esc) / EscapeCharβΎ(escβΈ/) π©
}
RunTest β {
inβΏexp β UnEscapeβ(1βΒ―1ββ’)Β¨2βπ©
out β 0 Markdown (β’UCS 10) ((β’-ΛΒ¬Γ+`)β=ββ’) in
β¨expβ‘out,in,exp,out,2βπ©β©
}
ignore β (2 βΛ tests) β β¨"47","85"β©
res β 1 βΛ (Β¬βΛ)βΈ/ RunTestΛ tests /Λ ignore < testSection β π©
res
}
################################
# Syntax highlighting
idChars β β¨
β’dβΎ"Β―.Οβ"
' '+βΎβ’UCSβ’a
β’a
"_"
β©
GetHighlights β {
classesβΏchars β <Λ β 2βΈ(Γ·Λββ βΎβ£)βΈβ₯β¨
0 , " "βΎβ’UCS 9βΏ10
"Value" , Β―1βΛ5βΏ2β₯"π¨π©πππ€"
"Function" , "+-ΓΓ·ββββ|Β¬β§β¨<>β =β€β₯β‘β’β£β’β₯βΎβββββ½β/ββββββββ·β!"βΎΒ―1βΛ5βΏ2β₯"πππ½πΎπ"
"Modifier" , "ΛΛΒ¨ββΌΒ΄Λ`"
"Modifier2" , "βββΈββΎββΆβββ"
"Number" , βΎidChars
"Gets" , "ββ©β"
"Paren" , "()"
"Bracket" , "β¨β©"
"Brace" , "{}"
"Ligature" , "βΏ"
"Nothing" , "Β·"
"Separator" , "β,"
"Comment" , "#"
"String" , "'"""
β©
classTag β ""βΏ""βΎ>{β¨"<span class='"βΎπ©βΎ"'>","</span>"β©}Β¨1βclasses
rβπ©='#'βsβ/(β β2βΈβ)βΈβ§π©='''βdβ/π©='"'
bββ¨sβΒ―1βdβ/rβ© TraceββΎ β¨2+sβ1βdβ(β’-Β―1β0βΎβ’)βββ(0βΎ+`r)βΈ//(π©=lf)βΎ1β©
scβ+Β΄(1βΏ2-Λβ classes)Γ(β `β¨β’)β((β π©)β/βΌββΎ)Β¨2β((βΛb)βr)ββ(βΎβ2)<Λb
colβscβ14|chars FindGroup π©
wβ(β β0βΎβ’)βΈ<idβcol=5
idcβ1+5|1-Λ(idChars FindGroup w/π©)+'_'=((1ββΎβ0)βΈ<id)/π©
colβ©((id/+`w)β0βΎidc)βΎ(idβΈ/)col
colβ©(1β½col)β£βΎ((π©=β"π©")βΈ/)col
bdβ(β βΒ―1βΎβ’)βΈβ col
bcβbd/col
(β₯(0<bc)βΈ/)Β¨β¨bcβclassTag,2β1-Λ/bdβΎ1β©
}
# Return highlights for areas in π© where π¨ is true.
GetMultiHighlights β {
start β 0βΈShlβΈ< π¨
groups β (1 -Λ π¨ Γ +` start) β π©
<ββΎΛ β ((β βΎ2Λ) β₯ Β·> (/start) {π¨βΈ+βΎ(1βΈβ)π©}βGetHighlightsΒ¨ β’) groups
}
################################
# Format an array to a character matrix
# Won't work on functions until we can catch errors
Fmt β {
# Vertical padding for arrays of rank greater than 2
PadV β {
# Leading shape
ls β Β―1ββ’π©
# Empty lines after each row: 1 if it's at the end of a 2-cell, plus
# 1 if it's at the end of a 2-cell and a 3-cell, and so on
p β β₯ +βΒ―1βΏβΒ΄ ΓβΛ`βΎβ½ (-1βls)βΒ¨1
# But none at the very end
p β© 0βΎ(Β―1βΈβ) p
Pad β {iβ/1+π¨ β (Β―1Β¨βΎ((Β¬βi)βΈ/)i) β π©βΎ(Β―1ββ’π©)β₯" "}
p (β0βls)βΆβ¨Pad,+Β΄βΈββ© ((ΓΒ΄ls)βΎΒ―1ββ’π©) β₯ π©
}β(2 < =)
# Horizontal padding: just some spaces on either side
PadH β {
(π¨/" ") (βΎβ1βΎβ1β£) π©
}
Pad β PadHβPadV
Enframe β {(1β π¨)β¨(1β β π©)β¨β2β+`-Λ"β¨β©"=ββπ©}βΆ{
β"β¨"βΎ(Β―1β1ββπ©)βΎ"β©"
}βΏ{
l β Β―1 β β’π©
βΎ β¨ # "βΌβββͺ"
1βΏlββΎβ¨"β",(5βΈ<)βΆβ¨β₯"Β·β"βΛ1ββ’,ββ©π¨β©
((4β0βπ¨-1)β"Β·β΅βββ")βΎβ π©
(1βΎ-l)β"β"
β©
}
FmtEmpty β (0βΏ0β’β’)βΆ("ββ"β"ββ")βΏ(((2β =)β¨0=β )βΆ{
'β'βΎ(0βΏΒ―1βΈβ) 2 Enframe 1 PadH " "Β¨π©
}βΏ{
β"β¨β©"βΎΛ(1<β )βΆβ¨"",'β₯'βΎ(Β―1βΈβ)Β·βΎΒ·βΎβ"βΏ"Β¨βΒ¨β©β’π©
})
PaddingJoin β {1ππ©;
s β β’Β¨ π©
w β βΛβ(=-1Λ)1βΒ¨s
h β βΛβ1 βΒ¨s
βΎβ2 ββ(0β2-=) (h βΎβ π¨ΓwΒ¬(-π¨Γβ w)β1) βΒ¨ π©
}
FmtMixed β {
(=π©) Enframe 2 Pad π¨ PaddingJoin FΒ¨π©
}
F β (2ββ‘)βΆ(ββ€βββΆβ¨"'"βΈ(βΎβΎβ£),ββ©)βΏ{
num β π©β€β β r β =π©
((β (0βΈ<+β€)+Β΄)β₯num)βΆ{
# All characters
k β -β c β Β―1ββ’π©
(r Enframe 1 PadH PadV)β(1β r) β (cβ'"') βΎβk π© βΎβk β½cβ'"'
}βΏ{
# Not homogeneous, or empty
(β¨Β΄0=β’)βΆFmtMixedβΏFmtEmpty π©
}βΏ{
# All numbers
Β―1 FmtMixed π©
} π©
}βΏFmtMixed
F π©
}
################################
# Creating HTML files
Head β "<head><link href="""βΎ("../"/Λ"/"ββββ’)βΎ"style.css"" rel=""stylesheet""/></head>"βΎlfΛ
nav β "<div class=""nav""><a href=""https://github.com/mlochbaum/BQN"">BQN</a></div>"βΎlf
ConvertFile β Head βΎ nav βΎ Markdownβ(β’LNS β’pathβΎβ’)
|