markdown.hoon 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934
  1. /= m /common/markdown/types
  2. ::
  3. => |%
  4. :: Set label for collapsed / shortcut reference links
  5. ++ backfill-ref-link
  6. |= [a=link:inline:m]
  7. ^- link:inline:m
  8. =/ t target.a
  9. ?+ -.t a :: only reference links
  10. %ref
  11. ?: =(%full type.t) a :: only collapsed / shortcut links
  12. =/ node=element:inline.m (head contents.a)
  13. ?+ -.node a :: ...and it's a %text node
  14. %text
  15. %_ a
  16. target %_ t
  17. label text.node
  18. ==
  19. ==
  20. ==
  21. ==
  22. ::
  23. ++ whitespace (mask " \09\0d\0a") :: whitespace: space, tab, or newline
  24. ::
  25. ++ all-link-ref-definitions :: Recursively get link ref definitions
  26. =< process-nodes
  27. |%
  28. ++ process-nodes
  29. |= [nodes=markdown:m]
  30. ^- (map @t urlt:ln:m)
  31. ?~ nodes ~
  32. %- %~(uni by (process-node (head nodes)))
  33. $(nodes +.nodes)
  34. ::
  35. ++ process-nodeses
  36. |= [nodeses=(list markdown:m)]
  37. ^- (map @t urlt:ln:m)
  38. ?~ nodeses ~
  39. %- %~(uni by (process-nodes (head nodeses)))
  40. $(nodeses +.nodeses)
  41. ::
  42. ++ process-node
  43. |= [node=node:markdown:m]
  44. ^- (map @t urlt:ln:m)
  45. =/ result *(map @t urlt:ln:m)
  46. ?- -.node
  47. %leaf :: Leaf node: check if it's a link ref def
  48. =/ leaf=node:leaf:m +.node
  49. ?+ -.leaf result
  50. %link-ref-definition (~(put by result) label.leaf urlt.leaf)
  51. ==
  52. ::
  53. %container
  54. =/ container=node:container:m +.node
  55. ?- -.container
  56. %block-quote (process-nodes markdown.container)
  57. %ol (process-nodeses contents.container)
  58. %ul (process-nodeses contents.container)
  59. %tl (process-nodeses (turn contents.container |=([is-checked=? =markdown:m] markdown)))
  60. ==
  61. ==
  62. --
  63. ::
  64. :: Get a preview-image for a document
  65. ++ get-preview-img
  66. =< |= [doc=markdown:m]
  67. ^- (unit @t)
  68. =/ ref-links (all-link-ref-definitions doc)
  69. (~(process-nodes . ref-links) doc)
  70. |_ [reference-links=(map @t urlt:ln:m)]
  71. ++ get-direct-link :: DUPE: get-direct-link
  72. |= [=target:ln:m]
  73. ^- urlt:ln:m
  74. ?- -.target
  75. %direct urlt.target :: Direct link; use it
  76. %ref :: Ref link; look it up
  77. ~| "reflink not found: {<label.target>}"
  78. (~(got by reference-links) label.target)
  79. ==
  80. ++ process-nodes
  81. |= [nodes=markdown:m]
  82. ^- (unit @t)
  83. ?~ nodes ~
  84. ?^ a=(process-node (head nodes)) a
  85. $(nodes +.nodes)
  86. ::
  87. ++ process-nodeses
  88. |= [nodeses=(list markdown:m)]
  89. ^- (unit @t)
  90. ?~ nodeses ~
  91. ?^ a=(process-nodes (head nodeses)) a
  92. $(nodeses +.nodeses)
  93. ::
  94. ++ process-contents
  95. |= [=contents:inline:m]
  96. ^- (unit @t)
  97. ?~ contents ~
  98. ?^ a=(process-inline (head contents)) a
  99. $(contents +.contents)
  100. ++ process-inline
  101. |= [el=element:inline:m]
  102. ^- (unit @t)
  103. ?+ -.el ~
  104. %image [~ text.url:(get-direct-link target.el)]
  105. %emphasis (process-contents contents.el)
  106. %strong (process-contents contents.el)
  107. %strikethru (process-contents contents.el)
  108. %link (process-contents contents.el)
  109. ==
  110. ++ process-node
  111. |= [node=node:markdown:m]
  112. ^- (unit @t)
  113. ?- -.node
  114. %leaf :: Leaf node: check if it's a link ref def
  115. =/ leaf=node:leaf:m +.node
  116. ?+ -.leaf ~
  117. %heading (process-contents contents.leaf)
  118. %paragraph (process-contents contents.leaf)
  119. ==
  120. ::
  121. %container
  122. =/ container=node:container:m +.node
  123. ?- -.container
  124. %block-quote (process-nodes markdown.container)
  125. %ol (process-nodeses contents.container)
  126. %ul (process-nodeses contents.container)
  127. %tl (process-nodeses (turn contents.container |=([is-checked=? =markdown:m] markdown)))
  128. ==
  129. ==
  130. --
  131. ++ get-headers
  132. =< process-doc
  133. |%
  134. ++ process-doc
  135. |= [doc=markdown:m]
  136. ^- (list [lvl=@ txt=tape])
  137. %+ turn
  138. (skim doc |=(a=node:markdown:m ?=([%leaf %heading *] a)))
  139. |= [h=node:markdown:m]
  140. ?> ?=([%leaf %heading *] h)
  141. ^- [lvl=@ txt=tape]
  142. [level.h (process-contents contents.h)]
  143. ++ process-contents
  144. |= [=contents:inline:m]
  145. ^- tape
  146. ?~ contents ~
  147. %+ welp (process-inline (head contents))
  148. $(contents +.contents)
  149. ++ process-inline
  150. |= [el=element:inline:m]
  151. ^- tape
  152. ?+ -.el ~
  153. %text (trip text.el)
  154. %code-span (trip text.el)
  155. %autolink (trip text.el)
  156. %escape (trip char.el)
  157. %entity `tape`~[code.el] :: cheat
  158. %emphasis (process-contents contents.el)
  159. %strong (process-contents contents.el)
  160. %strikethru (process-contents contents.el)
  161. %link (process-contents contents.el)
  162. ==
  163. --
  164. --
  165. ::
  166. :: Parse to and from Markdown text format
  167. |%
  168. ++ de :: de:md Deserialize (parse)
  169. =<
  170. |= [t=@t]
  171. ^- (unit markdown:m)
  172. (rush t markdown)
  173. |%
  174. ++ escaped
  175. |= [char=@t]
  176. (cold char (jest (crip (snoc "\\" char))))
  177. ::
  178. ++ newline
  179. %+ cold '\0a' :: EOL, with or without carriage return '\0d'
  180. ;~(pfix ;~(pose (just '\0d') (easy ~)) (just '\0a'))
  181. ++ line-end :: Either EOL or EOF
  182. %+ cold '\0a'
  183. ;~(pose newline (full (easy ~)))
  184. ::
  185. ++ ln :: Links and urls
  186. |%
  187. ++ url
  188. =< %+ cook |=(a=url:ln:m a) :: Cast
  189. ;~(pose with-triangles without-triangles)
  190. |%
  191. ++ with-triangles
  192. ;~ plug
  193. %+ cook crip
  194. %+ ifix [gal gar]
  195. %- star
  196. ;~ pose
  197. (escaped '<')
  198. (escaped '>')
  199. ;~(less gal gar line-end prn) :: Anything except '<', '>' or newline
  200. ==
  201. (easy %.y) :: "yes triangles"
  202. ==
  203. ++ without-triangles
  204. ;~ plug
  205. %+ cook crip
  206. ;~ less
  207. gal :: Doesn't start with '<'
  208. %- plus :: Non-empty
  209. ;~ less
  210. whitespace :: No whitespace allowed
  211. ;~ pose
  212. (escaped '(')
  213. (escaped ')')
  214. ;~(less pal par line-end prn) :: Anything except '(', ')' or newline
  215. ==
  216. ==
  217. ==
  218. (easy %.n) :: "no triangles"
  219. ==
  220. --
  221. ::
  222. ++ urlt
  223. %+ cook |=(a=urlt:ln:m a) :: Cast
  224. ;~ plug
  225. url
  226. %- punt :: Optional title-text
  227. ;~ pfix (plus whitespace) :: Separated by some whitespace
  228. %+ cook crip ;~ pose :: Enclosed in single quote, double quote, or '(...)'
  229. (ifix [soq soq] (star ;~(pose (escaped '\'') ;~(less soq prn))))
  230. (ifix [doq doq] (star ;~(pose (escaped '"') ;~(less doq prn))))
  231. (ifix [pal par] (star ;~(pose (escaped '(') (escaped ')') ;~(less pal par prn))))
  232. ==
  233. ==
  234. ==
  235. ::
  236. :: Labels are used in inline link targets and in a block-level element (labeled link references)
  237. ++ label
  238. %+ cook crip
  239. %+ ifix [sel ser] :: Enclosed in '[...]'
  240. %+ ifix :- (star whitespace) :: Strip leading and trailing whitespapce
  241. (star whitespace)
  242. %- plus ;~ pose :: Non-empty
  243. (escaped '[')
  244. (escaped ']')
  245. ;~(less sel ser prn) :: Anything except '[', ']' (must be escaped)
  246. ==
  247. ::
  248. ++ target :: Link target, either reference or direct
  249. =< %+ cook |=(a=target:ln:m a)
  250. ;~(pose target-direct target-ref)
  251. |%
  252. ++ target-direct
  253. %+ cook |=(a=target:ln:m a)
  254. %+ stag %direct
  255. %+ ifix [pal par] :: Direct links are enclosed in '(...)'
  256. %+ ifix :- (star whitespace) :: Strip leading and trailing whitespace
  257. (star whitespace)
  258. urlt :: Just the target
  259. ++ target-ref
  260. %+ cook |=(a=target:ln:m a)
  261. %+ stag %ref
  262. ;~ pose
  263. %+ stag %full label
  264. %+ stag %collapsed (cold '' (jest '[]'))
  265. %+ stag %shortcut (easy '')
  266. ==
  267. --
  268. --
  269. ++ inline :: Inline elements
  270. |%
  271. ++ contents (cook |=(a=contents:inline:m a) (star element)) :: Element sequence
  272. ++ element :: Any element
  273. %+ cook |=(a=element:inline:m a)
  274. ;~ pose
  275. escape
  276. entity
  277. strong
  278. emphasis
  279. strikethru
  280. code
  281. link
  282. image
  283. autolink
  284. text
  285. softbrk
  286. hardbrk
  287. ==
  288. ::
  289. ++ text
  290. %+ knee *text:inline:m |. ~+ :: recurse
  291. %+ cook |=(a=text:inline:m a)
  292. %+ stag %text
  293. %+ cook crip
  294. %- plus :: At least one character
  295. ;~ less :: ...which doesn't match any other inline rule
  296. escape
  297. entity
  298. link
  299. image
  300. autolink
  301. emphasis
  302. strong
  303. strikethru
  304. code
  305. softbrk
  306. hardbrk
  307. :: ...etc
  308. prn
  309. ==
  310. ::
  311. ++ escape
  312. %+ cook |=(a=escape:inline:m a)
  313. %+ stag %escape
  314. ;~ pose
  315. :: \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
  316. (escaped '[') (escaped ']') (escaped '(') (escaped ')')
  317. (escaped '!') (escaped '*') (escaped '*') (escaped '_')
  318. (escaped '&') (escaped '\\')
  319. :: etc
  320. ==
  321. ++ entity
  322. %+ cook |=(a=entity:inline:m a)
  323. %+ stag %entity
  324. %+ ifix [pam mic]
  325. %+ cook crip
  326. ;~ pose
  327. ;~(plug hax (stun [1 7] nud)) :: '#' and one to seven digits
  328. (plus alf) :: Named entity
  329. ==
  330. ::
  331. ++ softbrk :: Newline
  332. %+ cook |=(a=softbrk:inline:m a)
  333. %+ stag %soft-line-break
  334. (cold ~ newline)
  335. ::
  336. ++ hardbrk
  337. %+ cook |=(a=hardbrk:inline:m a)
  338. %+ stag %line-break
  339. %+ cold ~
  340. ;~ pose
  341. ;~(plug (jest ' ') (star ace) newline) :: Two or more spaces before a newline
  342. ;~(plug (just '\\') newline) :: An escaped newline
  343. ==
  344. ++ link
  345. %+ knee *link:inline:m |. ~+ :: recurse
  346. %+ cook backfill-ref-link
  347. %+ stag %link
  348. ;~ plug
  349. %+ ifix [sel ser] :: Display text is wrapped in '[...]'
  350. %- star ;~ pose :: Display text can contain various contents
  351. escape
  352. entity
  353. emphasis
  354. strong
  355. strikethru
  356. code
  357. image
  358. :: Text: =>
  359. %+ knee *text:inline:m |. ~+ :: recurse
  360. %+ cook |=(a=text:inline:m a)
  361. %+ stag %text
  362. %+ cook crip
  363. %- plus :: At least one character
  364. ;~ less :: ...which doesn't match any other inline rule
  365. escape
  366. entity
  367. emphasis
  368. strong
  369. strikethru
  370. code
  371. ser :: No closing ']'
  372. prn
  373. ==
  374. ==
  375. target:ln
  376. ==
  377. ::
  378. ++ image
  379. %+ cook |=(a=image:inline:m a)
  380. %+ stag %image
  381. ;~ plug
  382. %+ ifix [(jest '![') (just ']')] :: alt-text is wrapped in '![...]'
  383. %+ cook crip
  384. %- star ;~ pose
  385. (escaped ']')
  386. ;~(less ser prn)
  387. ==
  388. target:ln
  389. ==
  390. ::
  391. ++ autolink
  392. %+ cook |=(a=autolink:inline:m a)
  393. %+ stag %autolink
  394. %+ ifix [gal gar] :: Enclosed in '<...>'
  395. %+ cook crip
  396. %- star ;~ pose
  397. ;~(less ace gar prn) :: Spaces are not allowed; neither are backslash-escapes
  398. ==
  399. ::
  400. ++ emphasis
  401. %+ knee *emphasis:inline:m |. ~+ :: recurse
  402. %+ cook |=(a=emphasis:inline:m a)
  403. %+ stag %emphasis
  404. ;~ pose
  405. %+ ifix [tar tar]
  406. ;~ plug
  407. (easy '*')
  408. %- plus ;~ pose :: Display text can contain various contents
  409. escape
  410. entity
  411. strong
  412. strikethru
  413. link
  414. autolink
  415. code
  416. image
  417. link
  418. softbrk
  419. hardbrk
  420. %+ knee *text:inline:m |. ~+ :: recurse
  421. %+ cook |=(a=text:inline:m a)
  422. %+ stag %text
  423. %+ cook crip
  424. %- plus :: At least one character
  425. ;~ less :: ...which doesn't match any other inline rule
  426. escape
  427. entity
  428. strong
  429. strikethru
  430. link
  431. autolink
  432. code
  433. image
  434. link
  435. softbrk
  436. hardbrk
  437. ::
  438. tar :: If a '*', then it's the end of the `emphasis`
  439. ::
  440. prn
  441. ==
  442. ==
  443. ==
  444. %+ ifix [cab cab]
  445. ;~ plug
  446. (easy '_')
  447. %- plus ;~ pose :: Display text can contain various contents
  448. escape
  449. entity
  450. strong
  451. strikethru
  452. link
  453. autolink
  454. code
  455. image
  456. link
  457. softbrk
  458. hardbrk
  459. %+ knee *text:inline:m |. ~+ :: recurse
  460. %+ cook |=(a=text:inline:m a)
  461. %+ stag %text
  462. %+ cook crip
  463. %- plus :: At least one character
  464. ;~ less :: ...which doesn't match any other inline rule
  465. escape
  466. entity
  467. strong
  468. strikethru
  469. link
  470. autolink
  471. code
  472. image
  473. link
  474. softbrk
  475. hardbrk
  476. ::
  477. cab :: If a '*', then it's the end of the `emphasis`
  478. ::
  479. prn
  480. ==
  481. ==
  482. ==
  483. ==
  484. ::
  485. ++ strong
  486. %+ knee *strong:inline:m |. ~+ :: recurse
  487. %+ cook |=(a=strong:inline:m a)
  488. %+ stag %strong
  489. ;~ pose :: Either tars '**' or cabs '__'
  490. %+ ifix [(jest '**') (jest '**')]
  491. ;~ plug
  492. (easy '*') :: Note down the emphasis char
  493. %- plus ;~ pose :: Display text can contain various contents
  494. escape
  495. emphasis
  496. strikethru
  497. link
  498. autolink
  499. code
  500. image
  501. link
  502. softbrk
  503. hardbrk
  504. %+ knee *text:inline:m |. ~+ :: recurse
  505. %+ cook |=(a=text:inline:m a)
  506. %+ stag %text
  507. %+ cook crip
  508. %- plus :: At least one character
  509. ;~ less :: ...which doesn't match any other inline rule
  510. escape
  511. emphasis
  512. strikethru
  513. link
  514. autolink
  515. code
  516. image
  517. link
  518. softbrk
  519. hardbrk
  520. :: ...etc
  521. (jest '**') :: If a '**', then it's the end of the `strong`
  522. prn
  523. ==
  524. ==
  525. ==
  526. %+ ifix [(jest '__') (jest '__')]
  527. ;~ plug (easy '_') :: Note down the emphasis char
  528. %- plus ;~ pose :: Display text can contain various contents
  529. escape
  530. emphasis
  531. strikethru
  532. link
  533. autolink
  534. code
  535. image
  536. link
  537. softbrk
  538. hardbrk
  539. %+ knee *text:inline:m |. ~+ :: recurse
  540. %+ cook |=(a=text:inline:m a)
  541. %+ stag %text
  542. %+ cook crip
  543. %- plus :: At least one character
  544. ;~ less :: ...which doesn't match any other inline rule
  545. escape
  546. emphasis
  547. strikethru
  548. link
  549. autolink
  550. code
  551. image
  552. link
  553. softbrk
  554. hardbrk
  555. ::
  556. (jest '__') :: If a '**', then it's the end of the `strong`
  557. prn
  558. ==
  559. ==
  560. ==
  561. ==
  562. ::
  563. ++ strikethru
  564. %+ knee *strikethru:inline:m |. ~+ :: recurse
  565. %+ cook |=(a=strikethru:inline:m a)
  566. %+ stag %strikethru
  567. ;~ pose :: Either 1 sig '~' or 2 '~~'
  568. %+ ifix [sig sig]
  569. ;~ plug
  570. (easy 1) :: Enclosed in just 1 sig '~'
  571. %- plus ;~ pose :: Display text can contain various contents
  572. escape
  573. emphasis
  574. strong
  575. link
  576. autolink
  577. code
  578. image
  579. link
  580. softbrk
  581. hardbrk
  582. %+ knee *text:inline:m |. ~+ :: recurse
  583. %+ cook |=(a=text:inline:m a)
  584. %+ stag %text
  585. %+ cook crip
  586. %- plus :: At least one character
  587. ;~ less :: ...which doesn't match any other inline rule
  588. escape
  589. emphasis
  590. strong
  591. link
  592. autolink
  593. code
  594. image
  595. link
  596. softbrk
  597. hardbrk
  598. :: ...etc
  599. sig :: If a '~', then it's the end of the `strikethru`
  600. prn
  601. ==
  602. ==
  603. ==
  604. %+ ifix [(jest '~~') (jest '~~')]
  605. ;~ plug
  606. (easy 2) :: Enclosed in 2 sigs '~~'
  607. %- plus ;~ pose :: Display text can contain various contents
  608. escape
  609. emphasis
  610. strong
  611. link
  612. autolink
  613. code
  614. image
  615. link
  616. softbrk
  617. hardbrk
  618. %+ knee *text:inline:m |. ~+ :: recurse
  619. %+ cook |=(a=text:inline:m a)
  620. %+ stag %text
  621. %+ cook crip
  622. %- plus :: At least one character
  623. ;~ less :: ...which doesn't match any other inline rule
  624. escape
  625. emphasis
  626. strong
  627. link
  628. autolink
  629. code
  630. image
  631. link
  632. softbrk
  633. hardbrk
  634. :: ...etc
  635. (jest '~~') :: If a '~~', then it's the end of the `strikethru`
  636. prn
  637. ==
  638. ==
  639. ==
  640. ==
  641. ::
  642. ++ code
  643. =< %+ cook |=(a=code:inline:m a)
  644. %+ stag %code-span
  645. inner-parser
  646. |%
  647. ++ inner-parser
  648. |= =nail
  649. =/ vex ((plus tic) nail) :: Read the first backtick string
  650. ?~ q.vex vex :: If no vex is found, fail
  651. =/ tic-sequence ^- tape p:(need q.vex)
  652. %.
  653. q:(need q.vex)
  654. %+ cook |= [a=tape] :: Attach the backtick length to it
  655. [(lent tic-sequence) (crip a)]
  656. ;~ sfix
  657. %+ cook
  658. |= [a=(list tape)]
  659. ^- tape
  660. (zing a)
  661. %- star ;~ pose
  662. %+ cook trip ;~(less tic prn) :: Any character other than a backtick
  663. %+ sear :: A backtick string that doesn't match the opener
  664. |= [a=tape]
  665. ^- (unit tape)
  666. ?: =((lent a) (lent tic-sequence))
  667. ~
  668. `a
  669. (plus tic)
  670. ==
  671. (jest (crip tic-sequence)) :: Followed by a closing backtick string
  672. ==
  673. --
  674. --
  675. ::
  676. ++ leaf
  677. |%
  678. ++ node
  679. %+ cook |=(a=node:leaf:m a)
  680. ;~ pose
  681. blank-line
  682. break
  683. heading
  684. codeblk-indent
  685. codeblk-fenced
  686. link-ref-def
  687. :: ...etc
  688. table
  689. paragraph
  690. ==
  691. ++ blank-line
  692. %+ cook |=(a=blank-line:leaf:m a)
  693. %+ stag %blank-line
  694. (cold ~ newline)
  695. ++ heading
  696. =< %+ cook |=(a=heading:leaf:m a)
  697. %+ stag %heading
  698. ;~(pose atx setext)
  699. |%
  700. ++ atx
  701. =/ atx-eol ;~ pose
  702. line-end :: just end-of-line
  703. ;~ plug
  704. (plus ace)
  705. (star hax)
  706. (star ace)
  707. line-end
  708. ==
  709. ==
  710. %+ stag %atx
  711. %+ cook :: Parse heading inline content
  712. |= [level=@ text=tape]
  713. [level (scan text contents:inline)]
  714. ;~ pfix
  715. (stun [0 3] ace) :: Ignore up to 3 leading spaces
  716. ;~ plug
  717. (cook |=(a=tape (lent a)) (stun [1 6] hax)) :: Heading level
  718. %+ ifix [(plus ace) atx-eol] :: One leading space is required; rest is ignored
  719. %- star
  720. ;~(less atx-eol prn) :: Trailing haxes/spaces are ignored
  721. ==
  722. ==
  723. ++ setext
  724. %+ stag %setext
  725. %+ cook
  726. |= [text=tape level=@]
  727. [level (scan text contents:inline)]
  728. ;~ plug :: Wow this is a mess
  729. %+ ifix [(stun [0 3] ace) (star ace)] :: Strip up to 3 spaces, and trailing space
  730. (star ;~(less ;~(pfix (star ace) newline) prn)) :: Any text...
  731. ;~ pfix
  732. newline :: ...followed by newline...
  733. (stun [0 3] ace) :: ...up to 3 spaces (stripped)...
  734. ;~ sfix
  735. ;~ pose :: ...and an underline
  736. (cold 1 (plus (just '-'))) :: Underlined by '-' means heading lvl 1
  737. (cold 2 (plus (just '='))) :: Underlined by '=' means heading lvl 2
  738. ==
  739. ;~(plug (star ace) line-end)
  740. ==
  741. ==
  742. ==
  743. --
  744. ++ break
  745. %+ cook |=(a=break:leaf:m a)
  746. %+ stag %break
  747. %+ cook
  748. |= [first-2=@t trailing=tape]
  749. [(head trailing) (add 2 (lent trailing))]
  750. %+ ifix :- (stun [0 3] ace) :: Strip indent and trailing space
  751. ;~ plug
  752. (star (mask " \09"))
  753. line-end :: No other chars allowed on the line
  754. ==
  755. ;~ pose
  756. ;~(plug (jest '**') (plus tar)) :: At least 3, but can be more
  757. ;~(plug (jest '--') (plus hep))
  758. ;~(plug (jest '__') (plus cab))
  759. ==
  760. ::
  761. ++ codeblk-indent
  762. => |%
  763. ++ indented-chunk :: A block of indented code, delimited by newlines
  764. %+ cook |=(a=(list tape) (zing a))
  765. %- plus :: 1 or more lines
  766. ;~ pfix (jest ' ') :: with 4 leading spaces
  767. %+ cook snoc ;~ plug
  768. (star ;~(less line-end prn))
  769. line-end
  770. ==
  771. ==
  772. --
  773. %+ cook |=(a=codeblk-indent:leaf:m a)
  774. %+ stag %indent-codeblock
  775. %+ cook |=([a=tape b=(list tape)] (crip (welp a (zing b))))
  776. ;~ plug :: 1 or more chunks
  777. indented-chunk
  778. %- star
  779. %+ cook |=([newlines=tape chunk=tape] `tape`(welp newlines chunk))
  780. ;~ plug
  781. (star newline) :: separated by zero or more blank lines
  782. indented-chunk
  783. ==
  784. ==
  785. ::
  786. ++ codeblk-fenced
  787. =+ |%
  788. :: Returns a 3-tuple:
  789. :: - indent size
  790. :: - char type
  791. :: - fence length
  792. ++ code-fence
  793. ;~ plug
  794. %+ cook |=(a=tape (lent a)) (stun [0 3] ace)
  795. %+ cook |=(a=tape [(head a) (lent a)]) :: Get code fence char and length
  796. ;~ pose
  797. (stun [3 999.999.999] sig)
  798. (stun [3 999.999.999] tic)
  799. ==
  800. ==
  801. ::
  802. ++ info-string
  803. %+ cook crip
  804. %+ ifix [(star ace) line-end] :: Strip leading whitespace
  805. (star ;~(less line-end tic prn)) :: No backticks in a code fence
  806. --
  807. |* =nail
  808. :: Get the marker and indent size
  809. =/ vex (code-fence nail)
  810. ?~ q.vex vex :: If no match found, fail
  811. =/ [indent=@ char=@t len=@] p:(need q.vex)
  812. =/ closing-fence
  813. ;~ plug
  814. (star ace)
  815. (stun [len 999.999.999] (just char)) :: Closing fence must be at least as long as opener
  816. (star ace) :: ...and cannot have any following text except space
  817. line-end
  818. ==
  819. :: Read the rest of the list item block
  820. %.
  821. q:(need q.vex)
  822. %+ cook |=(a=codeblk-fenced:leaf:m a)
  823. %+ stag %fenced-codeblock
  824. ;~ plug
  825. %+ cook |=(a=@t a) (easy char)
  826. (easy len)
  827. %+ cook |=(a=@t a) info-string
  828. (easy indent)
  829. %+ cook |=(a=(list tape) (crip (zing a)))
  830. ;~ sfix
  831. %- star :: Any amount of lines
  832. ;~ less closing-fence :: ...until the closing code fence
  833. ;~ pfix (stun [0 indent] ace) :: Strip indent up to that of the opening fence
  834. %+ cook |=(a=tape a)
  835. ;~ pose :: Avoid infinite loop at EOF
  836. %+ cook trip newline :: A line is either a blank line...
  837. %+ cook snoc
  838. ;~ plug :: Or a non-blank line
  839. (plus ;~(less line-end prn))
  840. line-end
  841. ==
  842. ==
  843. ==
  844. ==
  845. ;~(pose closing-fence (full (easy ~)))
  846. ==
  847. ==
  848. ::
  849. ++ link-ref-def
  850. %+ cook |=(a=link-ref-def:leaf:m a)
  851. %+ stag %link-ref-definition
  852. %+ ifix [(stun [0 3] ace) line-end] :: Strip leading space
  853. ;~ plug
  854. ;~(sfix label:ln col) :: Label (enclosed in "[...]"), followed by col ":"
  855. ;~ pfix :: Optional whitespace, including up to 1 newline
  856. (star ace)
  857. (stun [0 1] newline)
  858. (star ace)
  859. urlt:ln
  860. ==
  861. ==
  862. ::
  863. ++ paragraph
  864. %+ cook |=(a=paragraph:leaf:m a)
  865. %+ stag %paragraph
  866. %+ cook :: Reparse the paragraph text as elements
  867. |= [a=(list tape)]
  868. (scan (zing a) contents:inline)
  869. %- plus :: Read lines until a non-paragraph object is found
  870. ;~ less
  871. heading
  872. break
  873. node:container :: Block quotes and lists can interrupt paragraphs
  874. :: TODO: fenced code blocks can interrupt paragraphs
  875. %+ cook snoc ;~ plug
  876. %- plus ;~(less line-end prn) :: Lines must be non-empty
  877. line-end
  878. ==
  879. ==
  880. ::
  881. ++ table
  882. => |%
  883. +$ cell-t [len=@ =contents:inline:m]
  884. ++ row
  885. ;~ pfix bar :: A bar in front...
  886. %- star
  887. %+ cook :: compute the length and parse inlines
  888. |= [pfx=@ stuff=tape sfx=@]
  889. [;:(add pfx (lent stuff) sfx) (scan stuff contents:inline)] :: inline elements...
  890. ;~ plug
  891. (cook lent (star ace))
  892. (star ;~(less newline ;~(plug (star ace) bar) prn))
  893. (cook lent ;~(sfix (star ace) bar))
  894. ==
  895. ==
  896. ++ delimiter-row
  897. ;~ pfix bar :: A bar in front...
  898. %- star
  899. %+ cook
  900. |= [pfx=@ lal=? heps=@ ral=? sfx=@]
  901. :- ;:(add pfx ?:(ral 1 0) heps ?:(lal 1 0) sfx)
  902. ?:(ral ?:(lal %c %r) ?:(lal %l %n))
  903. ;~ plug
  904. (cook lent (star ace)) :: Delimiter: leading space...
  905. (cook |=(a=tape .?(a)) (stun [0 1] col)) :: maybe a ':'...
  906. (cook lent (plus hep)) :: a bunch of '-'...
  907. (cook |=(a=tape .?(a)) (stun [0 1] col)) :: maybe another ':'...
  908. (cook lent ;~(sfix (star ace) bar)) :: ..and a bar as a terminator
  909. ==
  910. ==
  911. --
  912. |* =nail :: Make it a (redundant) gate so I can use `=>` to add a helper core
  913. %. nail :: apply the following parser
  914. %+ cook
  915. |= [hdr=(list cell-t) del=(list [len=@ al=?(%c %r %l %n)]) bdy=(list (list cell-t))]
  916. ^- table:leaf:m
  917. =/ widths=(list @) (turn del |=([len=@ al=*] len))
  918. =/ rows=(list (list cell-t)) (snoc bdy hdr) :: since they're the same data type
  919. =/ computed-widths
  920. |-
  921. ?~ rows widths
  922. %= $
  923. rows (tail rows)
  924. widths =/ row=(list cell-t) (head rows)
  925. |-
  926. ?~ row ~
  927. :- (max (head widths) len:(head row))
  928. %= $
  929. widths (tail widths)
  930. row (tail row)
  931. ==
  932. ==
  933. :* %table
  934. computed-widths
  935. (turn hdr |=(cell=cell-t contents.cell))
  936. (turn del |=([len=@ al=?(%c %r %l %n)] al))
  937. (turn bdy |=(row=(list cell-t) (turn row |=(cell=cell-t contents.cell))))
  938. ==
  939. ;~ plug
  940. ;~(sfix row line-end)
  941. ;~(sfix delimiter-row line-end)
  942. (star ;~(sfix row line-end))
  943. ==
  944. --
  945. ::
  946. ++ container
  947. =+ |%
  948. ::
  949. ++ line :: Read a line of plain text
  950. %+ cook |=([a=tape b=tape c=tape] ;:(weld a b c))
  951. ;~ plug
  952. (star ;~(less line-end prn))
  953. (cook trip line-end)
  954. (star newline) :: Can have blank lines in a list item
  955. ==
  956. ++ block-quote-marker
  957. ;~ plug :: Single char '>'
  958. (stun [0 3] ace) :: Indented up to 3 spaces
  959. gar
  960. (stun [0 1] ace) :: Optionally followed by a space
  961. ==
  962. ++ block-quote-line
  963. %+ cook snoc
  964. ;~ plug :: Single line...
  965. ;~ pfix block-quote-marker :: ...starting with ">..."
  966. (star ;~(less line-end prn)) :: can be empty
  967. ==
  968. line-end
  969. ==
  970. ::
  971. +$ ul-marker-t [indent=@ char=@t len=@]
  972. ++ ul-marker
  973. %+ cook :: Compute the length of the whole thing
  974. |= [prefix=tape bullet=@t suffix=tape]
  975. ^- ul-marker-t
  976. :* (lent prefix)
  977. bullet
  978. ;:(add 1 (lent prefix) (lent suffix))
  979. ==
  980. ;~ plug
  981. (stun [0 3] ace)
  982. ;~(pose hep lus tar) :: Bullet char
  983. (stun [1 4] ace)
  984. ==
  985. ::
  986. :: Produces a 3-tuple:
  987. :: - bullet char (*, +, or -)
  988. :: - indent level (number of spaces before the bullet)
  989. :: - item contents (markdown)
  990. +$ ul-item-t [char=@t indent=@ =markdown:m]
  991. ++ ul-item
  992. |* =nail
  993. :: Get the marker and indent size
  994. =/ vex (ul-marker nail)
  995. ?~ q.vex vex :: If no match found, fail
  996. =/ mrkr=ul-marker-t p:(need q.vex)
  997. :: Read the rest of the list item block
  998. %.
  999. q:(need q.vex)
  1000. %+ cook
  1001. |= [a=(list tape)]
  1002. ^- ul-item-t
  1003. :* char.mrkr
  1004. indent.mrkr
  1005. (scan (zing a) markdown)
  1006. ==
  1007. ;~ plug
  1008. line :: First line
  1009. %- star ;~ pfix
  1010. (stun [len.mrkr len.mrkr] ace) :: Subsequent lines must have the same indent
  1011. line :: the line
  1012. ==
  1013. ==
  1014. ::
  1015. +$ ol-marker-t [indent=@ char=@t number=@ len=@]
  1016. ++ ol-marker
  1017. %+ cook :: Compute the length of the whole thing
  1018. |= [prefix=tape number=@ char=@t suffix=tape]
  1019. ^- ol-marker-t
  1020. :* (lent prefix)
  1021. char
  1022. number
  1023. ;:(add 1 (lent (a-co:co number)) (lent prefix) (lent suffix))
  1024. ==
  1025. ;~ plug
  1026. (stun [0 3] ace)
  1027. dem
  1028. ;~(pose dot par) :: Bullet char
  1029. (stun [1 4] ace)
  1030. ==
  1031. ::
  1032. :: Produces a 4-tuple:
  1033. :: - delimiter char: either dot '.' or par ')'
  1034. :: - list item number
  1035. :: - indent level (number of spaces before the number)
  1036. :: - item contents (markdown)
  1037. +$ ol-item-t [char=@t number=@ indent=@ =markdown:m]
  1038. ++ ol-item
  1039. |* =nail
  1040. ::^- edge
  1041. :: Get the marker and indent size
  1042. =/ vex (ol-marker nail)
  1043. ?~ q.vex vex :: If no match found, fail
  1044. =/ mrkr=ol-marker-t p:(need q.vex)
  1045. :: Read the rest of the list item block
  1046. %.
  1047. q:(need q.vex)
  1048. %+ cook
  1049. |= [a=(list tape)]
  1050. ^- ol-item-t
  1051. :* char.mrkr
  1052. number.mrkr
  1053. indent.mrkr
  1054. (scan (zing a) markdown)
  1055. ==
  1056. ;~ plug
  1057. line :: First line
  1058. %- star ;~ pfix :: Subsequent lines must have the same indent
  1059. (stun [len.mrkr len.mrkr] ace) :: the indent
  1060. line :: the line
  1061. ==
  1062. ==
  1063. ::
  1064. ++ tl-checkbox
  1065. ;~ pose
  1066. %+ cold %.y (jest '[x]')
  1067. %+ cold %.n (jest '[ ]')
  1068. ==
  1069. ::
  1070. :: Produces a 4-tuple:
  1071. :: - bullet char (*, +, or -)
  1072. :: - indent level (number of spaces before the bullet)
  1073. :: - is-checked
  1074. :: - item contents (markdown)
  1075. +$ tl-item-t [char=@t indent=@ is-checked=? =markdown:m]
  1076. ++ tl-item
  1077. |* =nail
  1078. :: Get the marker and indent size
  1079. =/ vex (;~(plug ul-marker ;~(sfix tl-checkbox ace)) nail)
  1080. ?~ q.vex vex :: If no match found, fail
  1081. =/ [mrkr=ul-marker-t is-checked=?] p:(need q.vex)
  1082. :: Read the rest of the list item block
  1083. %.
  1084. q:(need q.vex)
  1085. %+ cook
  1086. |= [a=(list tape)]
  1087. ^- tl-item-t
  1088. :* char.mrkr
  1089. indent.mrkr
  1090. is-checked
  1091. (scan (zing a) markdown)
  1092. ==
  1093. ;~ plug
  1094. line :: First line
  1095. %- star ;~ pfix :: Subsequent lines must have the same indent
  1096. (stun [len.mrkr len.mrkr] ace) :: the indent
  1097. line :: the line
  1098. ==
  1099. ==
  1100. --
  1101. |%
  1102. ++ node
  1103. %+ cook |=(a=node:container:m a)
  1104. ;~ pose
  1105. block-quote
  1106. tl
  1107. ul
  1108. ol
  1109. ==
  1110. ::
  1111. ++ block-quote
  1112. %+ cook |=(a=block-quote:container:m a)
  1113. %+ stag %block-quote
  1114. %+ cook |= [a=(list tape)]
  1115. (scan (zing a) markdown)
  1116. ;~ plug
  1117. block-quote-line
  1118. %- star :: At least one line
  1119. ;~ pose
  1120. block-quote-line
  1121. %+ cook zing %- plus :: Paragraph continuation (copied from `paragraph` above)
  1122. ;~ less :: ...basically just text that doesn't matchZ anything else
  1123. heading:leaf
  1124. break:leaf
  1125. :: ol
  1126. :: ul
  1127. block-quote-marker :: Can't start with ">"
  1128. line-end :: Can't be blank
  1129. %+ cook snoc ;~ plug
  1130. %- star ;~(less line-end prn)
  1131. line-end
  1132. ==
  1133. ==
  1134. ==
  1135. ==
  1136. ::
  1137. ++ ul
  1138. |* =nail
  1139. :: Start by finding the type of the first bullet (indent level and bullet char)
  1140. =/ vex (ul-item nail)
  1141. ?~ q.vex vex :: Fail if it doesn't match a list item
  1142. =/ first-item=ul-item-t p:(need q.vex)
  1143. :: Check for more list items
  1144. %.
  1145. q:(need q.vex)
  1146. %+ cook |=(a=ul:container:m a)
  1147. %+ stag %ul
  1148. ;~ plug :: Give the first item, first
  1149. (easy indent.first-item)
  1150. (easy char.first-item)
  1151. (easy markdown.first-item)
  1152. %- star
  1153. %+ sear :: Reject items that don't have the same bullet char
  1154. |= [item=ul-item-t]
  1155. ^- (unit markdown:m)
  1156. ?. =(char.item char.first-item)
  1157. ~
  1158. `markdown.item
  1159. ul-item
  1160. ==
  1161. ::
  1162. ++ ol
  1163. |* =nail
  1164. :: Start by finding the first number, char, and indent level
  1165. =/ vex (ol-item nail)
  1166. ?~ q.vex vex :: Fail if it doesn't match a list item
  1167. =/ first-item=ol-item-t p:(need q.vex)
  1168. :: Check for more list items
  1169. %.
  1170. q:(need q.vex)
  1171. %+ cook |=(a=ol:container:m a)
  1172. %+ stag %ol
  1173. ;~ plug :: Give the first item, first
  1174. (easy indent.first-item)
  1175. (easy char.first-item)
  1176. (easy number.first-item)
  1177. (easy markdown.first-item)
  1178. %- star
  1179. %+ sear :: Reject items that don't have the same delimiter
  1180. |= [item=ol-item-t]
  1181. ^- (unit markdown:m)
  1182. ?. =(char.item char.first-item)
  1183. ~
  1184. `markdown.item
  1185. ol-item
  1186. ==
  1187. ::
  1188. ++ tl
  1189. |* =nail
  1190. :: Start by finding the type of the first bullet (indent level and bullet char)
  1191. =/ vex (tl-item nail)
  1192. ?~ q.vex vex :: Fail if it doesn't match a list item
  1193. =/ first-item=tl-item-t p:(need q.vex)
  1194. :: Check for more list items
  1195. %.
  1196. q:(need q.vex)
  1197. %+ cook |=(a=tl:container:m a)
  1198. %+ stag %tl
  1199. ;~ plug :: Give the first item, first
  1200. (easy indent.first-item)
  1201. (easy char.first-item)
  1202. (easy [is-checked.first-item markdown.first-item])
  1203. %- star
  1204. %+ sear :: Reject items that don't have the same bullet char
  1205. |= [item=tl-item-t]
  1206. ^- (unit [is-checked=? markdown:m])
  1207. ?. =(char.item char.first-item)
  1208. ~
  1209. `[is-checked.item markdown.item]
  1210. tl-item
  1211. ==
  1212. --
  1213. ::
  1214. ++ markdown
  1215. %+ cook |=(a=markdown:m a)
  1216. %- star ;~ pose
  1217. (stag %container node:container)
  1218. (stag %leaf node:leaf)
  1219. ==
  1220. --
  1221. ::
  1222. :: Enserialize (write out as text)
  1223. ++ en
  1224. =< markdown
  1225. |%
  1226. ++ escape-chars
  1227. |= [text=@t chars=(list @t)]
  1228. ^- tape
  1229. %+ rash text
  1230. %+ cook
  1231. |=(a=(list tape) `tape`(zing a))
  1232. %- star ;~ pose
  1233. (cook |=(a=@t `tape`~['\\' a]) (mask chars))
  1234. (cook trip prn)
  1235. ==
  1236. ::
  1237. ++ ln
  1238. |%
  1239. ++ url
  1240. =< |= [u=url:ln:m]
  1241. ^- tape
  1242. ?: has-triangle-brackets.u
  1243. (with-triangles text.u)
  1244. (without-triangles text.u)
  1245. |%
  1246. ++ with-triangles
  1247. |= [text=@t]
  1248. ;: weld
  1249. "<" :: Put it inside triangle brackets
  1250. (escape-chars text "<>") :: Escape triangle brackets in the text
  1251. ">"
  1252. ==
  1253. ++ without-triangles
  1254. |= [text=@t]
  1255. (escape-chars text "()") :: Escape all parentheses '(' and ')'
  1256. --
  1257. ++ urlt
  1258. |= [u=urlt:ln:m]
  1259. ^- tape
  1260. ?~ title-text.u :: If there's no title text, then it's just an url
  1261. (url url.u)
  1262. ;:(weld (url url.u) " \"" (escape-chars (need title-text.u) "\"") "\"")
  1263. ++ label
  1264. |= [text=@t]
  1265. ^- tape
  1266. ;:(weld "[" (escape-chars text "[]") "]")
  1267. ++ target
  1268. |= [t=target:ln:m]
  1269. ^- tape
  1270. ?- -.t
  1271. %direct ;:(weld "(" (urlt urlt.t) ")") :: Wrap in parentheses
  1272. ::
  1273. %ref ?- type.t
  1274. %full (label label.t)
  1275. %collapsed "[]"
  1276. %shortcut ""
  1277. ==
  1278. ==
  1279. --
  1280. ::
  1281. ++ inline
  1282. |%
  1283. ++ contents
  1284. |= [=contents:inline:m]
  1285. ^- tape
  1286. %- zing %+ turn contents element
  1287. ++ element
  1288. |= [e=element:inline:m]
  1289. ?- -.e
  1290. %text (text e)
  1291. %link (link e)
  1292. %escape (escape e)
  1293. %entity (entity e)
  1294. %code-span (code e)
  1295. %strong (strong e)
  1296. %emphasis (emphasis e)
  1297. %strikethru (strikethru e)
  1298. %soft-line-break (softbrk e)
  1299. %line-break (hardbrk e)
  1300. %image (image e)
  1301. %autolink (autolink e)
  1302. ==
  1303. ++ text
  1304. |= [t=text:inline:m]
  1305. ^- tape
  1306. (trip text.t) :: So easy!
  1307. ::
  1308. ++ entity
  1309. |= [e=entity:inline:m]
  1310. ^- tape
  1311. ;:(weld "&" (trip code.e) ";")
  1312. ::
  1313. ++ link
  1314. |= [l=link:inline:m]
  1315. ^- tape
  1316. ;: weld
  1317. "["
  1318. (contents contents.l)
  1319. "]"
  1320. (target:ln target.l)
  1321. ==
  1322. ::
  1323. ++ image
  1324. |= [i=image:inline:m]
  1325. ^- tape
  1326. ;: weld
  1327. "!["
  1328. (escape-chars alt-text.i "]")
  1329. "]"
  1330. (target:ln target.i)
  1331. ==
  1332. ::
  1333. ++ autolink
  1334. |= [a=autolink:inline:m]
  1335. ^- tape
  1336. ;: weld
  1337. "<"
  1338. (trip text.a)
  1339. ">"
  1340. ==
  1341. ::
  1342. ++ escape
  1343. |= [e=escape:inline:m]
  1344. ^- tape
  1345. (snoc "\\" char.e) :: Could use `escape-chars` but why bother-- this is shorter
  1346. ::
  1347. ++ softbrk
  1348. |= [s=softbrk:inline:m]
  1349. ^- tape
  1350. "\0a"
  1351. ++ hardbrk
  1352. |= [h=hardbrk:inline:m]
  1353. ^- tape
  1354. "\\\0a"
  1355. ++ code
  1356. |= [c=code:inline:m]
  1357. ^- tape
  1358. ;:(weld (reap num-backticks.c '`') (trip text.c) (reap num-backticks.c '`'))
  1359. ::
  1360. ++ strong
  1361. |= [s=strong:inline:m]
  1362. ^- tape
  1363. ;: weld
  1364. (reap 2 emphasis-char.s)
  1365. (contents contents.s)
  1366. (reap 2 emphasis-char.s)
  1367. ==
  1368. ::
  1369. ++ emphasis
  1370. |= [e=emphasis:inline:m]
  1371. ^- tape
  1372. ;: weld
  1373. (trip emphasis-char.e)
  1374. (contents contents.e)
  1375. (trip emphasis-char.e)
  1376. ==
  1377. ::
  1378. ++ strikethru
  1379. |= [s=strikethru:inline:m]
  1380. ^- tape
  1381. ;: weld
  1382. (reap sig-count.s '~')
  1383. (contents contents.s)
  1384. (reap sig-count.s '~')
  1385. ==
  1386. --
  1387. ::
  1388. ++ leaf
  1389. |%
  1390. ++ node
  1391. |= [n=node:leaf:m]
  1392. ?- -.n
  1393. %blank-line (blank-line n)
  1394. %break (break n)
  1395. %heading (heading n)
  1396. %indent-codeblock (codeblk-indent n)
  1397. %fenced-codeblock (codeblk-fenced n)
  1398. %link-ref-definition (link-ref-def n)
  1399. %paragraph (paragraph n)
  1400. %table (table n)
  1401. :: ...etc
  1402. ==
  1403. ++ blank-line
  1404. |= [b=blank-line:leaf:m]
  1405. ^- tape
  1406. "\0a"
  1407. ::
  1408. ++ break
  1409. |= [b=break:leaf:m]
  1410. ^- tape
  1411. (weld (reap char-count.b char.b) "\0a")
  1412. ::
  1413. ++ heading
  1414. |= [h=heading:leaf:m]
  1415. ^- tape
  1416. ?- style.h
  1417. %atx
  1418. ;:(weld (reap level.h '#') " " (contents:inline contents.h) "\0a")
  1419. %setext
  1420. =/ line (contents:inline contents.h)
  1421. ;:(weld line "\0a" (reap (lent line) ?:(=(level.h 1) '-' '=')) "\0a")
  1422. ==
  1423. ::
  1424. ++ codeblk-indent
  1425. |= [c=codeblk-indent:leaf:m]
  1426. ^- tape
  1427. %+ rash text.c
  1428. %+ cook
  1429. |= [a=(list tape)]
  1430. ^- tape
  1431. %- zing %+ turn a |=(t=tape (weld " " t))
  1432. %- plus %+ cook snoc ;~(plug (star ;~(less (just '\0a') prn)) (just '\0a'))
  1433. ::
  1434. ++ codeblk-fenced
  1435. |= [c=codeblk-fenced:leaf:m]
  1436. ^- tape
  1437. ;: weld
  1438. (reap indent-level.c ' ')
  1439. (reap char-count.c char.c)
  1440. (trip info-string.c)
  1441. "\0a"
  1442. ^- tape %+ rash text.c
  1443. %+ cook zing %- star :: Many lines
  1444. %+ cook |= [a=tape newline=@t] :: Prepend each line with "> "
  1445. ^- tape
  1446. ;: weld
  1447. ?~(a "" (reap indent-level.c ' ')) :: If the line is blank, no indent
  1448. a
  1449. "\0a"
  1450. ==
  1451. ;~ plug :: Break into lines
  1452. (star ;~(less (just '\0a') prn))
  1453. (just '\0a')
  1454. ==
  1455. (reap indent-level.c ' ')
  1456. (reap char-count.c char.c)
  1457. "\0a"
  1458. ==
  1459. ::
  1460. ++ link-ref-def
  1461. |= [l=link-ref-def:leaf:m]
  1462. ^- tape
  1463. ;: weld
  1464. "["
  1465. (trip label.l)
  1466. "]: "
  1467. (urlt:ln urlt.l)
  1468. "\0a"
  1469. ==
  1470. ::
  1471. ++ table
  1472. => |%
  1473. ++ cell
  1474. |= [width=@ c=contents:inline:m]
  1475. ^- tape
  1476. =/ contents-txt (contents:inline c)
  1477. ;: weld
  1478. " "
  1479. contents-txt
  1480. (reap (sub width (add 1 (lent contents-txt))) ' ')
  1481. "|"
  1482. ==
  1483. ++ row
  1484. |= [widths=(list @) cells=(list contents:inline:m)]
  1485. ^- tape
  1486. ;: weld
  1487. "|"
  1488. |-
  1489. ^- tape
  1490. ?~ widths ~
  1491. %+ weld
  1492. (cell (head widths) (head cells))
  1493. $(widths (tail widths), cells (tail cells))
  1494. "\0a"
  1495. ==
  1496. ++ delimiter-row
  1497. |= [widths=(list @) align=(list ?(%l %c %r %n))]
  1498. ^- tape
  1499. ;: weld
  1500. "|"
  1501. |-
  1502. ^- tape
  1503. ?~ align ~
  1504. ;: weld
  1505. " "
  1506. ?- (head align)
  1507. %l (weld ":" (reap ;:(sub (head widths) 3) '-'))
  1508. %r (weld (reap ;:(sub (head widths) 3) '-') ":")
  1509. %c ;:(weld ":" (reap ;:(sub (head widths) 4) '-') ":")
  1510. %n (reap ;:(sub (head widths) 2) '-')
  1511. ==
  1512. " |"
  1513. $(align (tail align), widths (tail widths))
  1514. ==
  1515. "\0a"
  1516. ==
  1517. --
  1518. |= [t=table:leaf:m]
  1519. ^- tape
  1520. ;: weld
  1521. (row widths.t head.t)
  1522. (delimiter-row widths.t align.t)
  1523. =/ rows rows.t
  1524. |-
  1525. ^- tape
  1526. ?~ rows ~
  1527. %+ weld (row widths.t (head rows)) $(rows (tail rows))
  1528. ==
  1529. ::
  1530. ++ paragraph
  1531. |= [p=paragraph:leaf:m]
  1532. ^- tape
  1533. (contents:inline contents.p)
  1534. --
  1535. ::
  1536. ++ container
  1537. => |%
  1538. ++ line
  1539. %+ cook snoc
  1540. ;~ plug
  1541. (star ;~(less (just '\0a') prn))
  1542. (just '\0a')
  1543. ==
  1544. --
  1545. |%
  1546. ++ node
  1547. |= [n=node:container:m]
  1548. ?- -.n
  1549. %block-quote (block-quote n)
  1550. %ul (ul n)
  1551. %ol (ol n)
  1552. %tl (tl n)
  1553. ==
  1554. ::
  1555. ++ block-quote
  1556. |= [b=block-quote:container:m]
  1557. ^- tape
  1558. %+ scan (markdown markdown.b) :: First, render the contents
  1559. %+ cook zing %- plus :: Many lines
  1560. %+ cook |= [a=tape newline=@t] :: Prepend each line with "> "
  1561. ^- tape
  1562. ;: weld
  1563. ">"
  1564. ?~(a "" " ") :: If the line is blank, no trailing space
  1565. a
  1566. "\0a"
  1567. ==
  1568. ;~ plug :: Break into lines
  1569. (star ;~(less (just '\0a') prn))
  1570. (just '\0a')
  1571. ==
  1572. ::
  1573. ++ ul
  1574. |= [u=ul:container:m]
  1575. ^- tape
  1576. %- zing %+ turn contents.u :: Each bullet point...
  1577. |= [item=markdown:m]
  1578. ^- tape
  1579. %+ scan (markdown item) :: First, render bullet point contents
  1580. %+ cook zing
  1581. ;~ plug
  1582. %+ cook |= [a=tape] :: Prepend 1st line with indent + bullet char
  1583. ;: weld
  1584. (reap indent-level.u ' ')
  1585. (trip marker-char.u)
  1586. " "
  1587. a
  1588. ==
  1589. line :: first line
  1590. %- star
  1591. %+ cook |= [a=tape] :: Subsequent lines just get indent
  1592. ?: ?|(=("" a) =("\0a" a)) a
  1593. ;: weld
  1594. (reap indent-level.u ' ')
  1595. " " :: 2 spaces, to make it even with the 1st line
  1596. a
  1597. ==
  1598. line :: second and thereafter lines
  1599. ==
  1600. ++ tl
  1601. |= [t=tl:container:m]
  1602. ^- tape
  1603. %- zing %+ turn contents.t :: Each bullet point...
  1604. |= [is-checked=? item=markdown:m]
  1605. ^- tape
  1606. %+ scan (markdown item) :: First, render bullet point contents
  1607. %+ cook zing
  1608. ;~ plug
  1609. %+ cook |= [a=tape] :: Prepend 1st line with indent, bullet char, checkbox
  1610. ;: weld
  1611. (reap indent-level.t ' ')
  1612. (trip marker-char.t)
  1613. " ["
  1614. ?:(is-checked "x" " ")
  1615. "] "
  1616. a
  1617. ==
  1618. line :: first line
  1619. %- star
  1620. %+ cook |= [a=tape] :: Subsequent lines just get indent
  1621. ?: ?|(=("" a) =("\0a" a)) a
  1622. ;: weld
  1623. (reap indent-level.t ' ')
  1624. " " :: 2 spaces, to make it even with the 1st line
  1625. a
  1626. ==
  1627. line :: second and thereafter lines
  1628. ==
  1629. ::
  1630. ++ ol
  1631. |= [o=ol:container:m]
  1632. ^- tape
  1633. %- zing %+ turn contents.o :: Each item...
  1634. |= [item=markdown:m]
  1635. ^- tape
  1636. %+ scan (markdown item) :: First, render item contents
  1637. %+ cook zing
  1638. ;~ plug
  1639. %+ cook |= [a=tape] :: Prepend 1st line with indent + item number
  1640. ;: weld
  1641. (reap indent-level.o ' ')
  1642. (a-co:co start-num.o)
  1643. (trip marker-char.o)
  1644. " "
  1645. a
  1646. ==
  1647. line :: first line
  1648. %- star
  1649. %+ cook |= [a=tape] :: Subsequent lines just get indent
  1650. ?: ?|(=("" a) =("\0a" a)) a
  1651. ;: weld
  1652. (reap indent-level.o ' ')
  1653. (reap (lent (a-co:co start-num.o)) ' ')
  1654. " " :: 2 spaces, to make it even with the 1st line
  1655. a
  1656. ==
  1657. line :: second and thereafter lines
  1658. ==
  1659. --
  1660. ::
  1661. ++ markdown
  1662. |= [a=markdown:m]
  1663. ^- tape
  1664. %- zing %+ turn a |= [item=node:markdown:m]
  1665. ?- -.item
  1666. %leaf (node:leaf +.item)
  1667. %container (node:container +.item)
  1668. ==
  1669. --
  1670. ::
  1671. :: Enserialize as Sail (manx and marl)
  1672. ++ sail-en
  1673. =<
  1674. |= [document=markdown:m]
  1675. =/ link-ref-defs (all-link-ref-definitions document)
  1676. ^- manx
  1677. ;div
  1678. ;* (~(markdown sail-en link-ref-defs) document)
  1679. ==
  1680. ::
  1681. |_ [reference-links=(map @t urlt:ln:m)]
  1682. ++ inline
  1683. => |%
  1684. ++ get-direct-link :: DUPE: get-direct-link
  1685. |= [=target:ln:m]
  1686. ^- urlt:ln:m
  1687. ?- -.target
  1688. %direct urlt.target :: Direct link; use it
  1689. %ref :: Ref link; look it up
  1690. ~| "reflink not found: {<label.target>}"
  1691. (~(got by reference-links) label.target)
  1692. ==
  1693. --
  1694. |%
  1695. ++ contents
  1696. |= [=contents:inline:m]
  1697. ^- marl
  1698. %+ turn contents element
  1699. ++ element
  1700. |= [e=element:inline:m]
  1701. ^- manx
  1702. ?- -.e
  1703. %text (text e)
  1704. %link (link e)
  1705. %code-span (code e)
  1706. %escape (escape e)
  1707. %entity (entity e)
  1708. %strong (strong e)
  1709. %emphasis (emphasis e)
  1710. %strikethru (strikethru e)
  1711. %soft-line-break (softbrk e)
  1712. %line-break (hardbrk e)
  1713. %image (image e)
  1714. %autolink (autolink e)
  1715. ==
  1716. ++ text
  1717. |= [t=text:inline:m]
  1718. ^- manx
  1719. [[%$ [%$ (trip text.t)] ~] ~] :: Magic; look up the structure of a `manx` if you want
  1720. ++ escape
  1721. |= [e=escape:inline:m]
  1722. ^- manx
  1723. [[%$ [%$ (trip char.e)] ~] ~] :: Magic; look up the structure of a `manx` if you want
  1724. ++ entity
  1725. |= [e=entity:inline:m]
  1726. ^- manx
  1727. =/ fulltext (crip ;:(weld "&" (trip code.e) ";"))
  1728. [[%$ [%$ `tape`[fulltext ~]] ~] ~] :: We do a little sneaky
  1729. ++ softbrk
  1730. |= [s=softbrk:inline:m]
  1731. ^- manx
  1732. (text [%text ' '])
  1733. ++ hardbrk
  1734. |= [h=hardbrk:inline:m]
  1735. ^- manx
  1736. ;br;
  1737. ++ code
  1738. |= [c=code:inline:m]
  1739. ^- manx
  1740. ;code: {(trip text.c)}
  1741. ++ link
  1742. |= [l=link:inline:m]
  1743. ^- manx
  1744. =/ =urlt:ln:m (get-direct-link target.l)
  1745. ;a(href (trip text.url.urlt), title (trip (fall title-text.urlt '')))
  1746. ;* (contents contents.l)
  1747. ==
  1748. ++ image
  1749. |= [i=image:inline:m]
  1750. ^- manx
  1751. =/ target target.i
  1752. =/ =urlt:ln:m (get-direct-link target.i)
  1753. ;img(src (trip text.url.urlt), alt (trip alt-text.i));
  1754. ++ autolink
  1755. |= [a=autolink:inline:m]
  1756. ^- manx
  1757. ;a(href (trip text.a)): {(trip text.a)}
  1758. ++ emphasis
  1759. |= [e=emphasis:inline:m]
  1760. ^- manx
  1761. ;em
  1762. ;* (contents contents.e)
  1763. ==
  1764. ++ strong
  1765. |= [s=strong:inline:m]
  1766. ^- manx
  1767. ;strong
  1768. ;* (contents contents.s)
  1769. ==
  1770. ++ strikethru
  1771. |= [s=strikethru:inline:m]
  1772. ^- manx
  1773. ;strike
  1774. ;* (contents contents.s)
  1775. ==
  1776. --
  1777. ++ leaf
  1778. |%
  1779. ++ node
  1780. |= [n=node:leaf:m]
  1781. ^- manx
  1782. ?- -.n
  1783. %blank-line (blank-line n)
  1784. %break (break n)
  1785. %heading (heading n)
  1786. %indent-codeblock (codeblk-indent n)
  1787. %fenced-codeblock (codeblk-fenced n)
  1788. %table (table n)
  1789. %paragraph (paragraph n)
  1790. %link-ref-definition (text:inline [%text ' ']) :: Link ref definitions don't render as anything
  1791. :: ...etc
  1792. ==
  1793. ++ heading
  1794. |= [h=heading:leaf:m]
  1795. ^- manx
  1796. :-
  1797. :_ ~ ?+ level.h !! :: Tag and attributes; attrs are empty (~)
  1798. %1 %h1
  1799. %2 %h2
  1800. %3 %h3
  1801. %4 %h4
  1802. %5 %h5
  1803. %6 %h6
  1804. ==
  1805. (contents:inline contents.h)
  1806. ++ blank-line
  1807. |= [b=blank-line:leaf:m]
  1808. ^- manx
  1809. (text:inline [%text ' '])
  1810. ++ break
  1811. |= [b=break:leaf:m]
  1812. ^- manx
  1813. ;hr;
  1814. ++ codeblk-indent
  1815. |= [c=codeblk-indent:leaf:m]
  1816. ^- manx
  1817. ;pre
  1818. ;code: {(trip text.c)}
  1819. ==
  1820. ++ codeblk-fenced
  1821. |= [c=codeblk-fenced:leaf:m]
  1822. ^- manx
  1823. ;pre
  1824. ;+ ?: =(info-string.c '')
  1825. ;code: {(trip text.c)}
  1826. ;code(class (weld "language-" (trip info-string.c))): {(trip text.c)}
  1827. ==
  1828. ++ table
  1829. |= [t=table:leaf:m]
  1830. ^- manx
  1831. ;table
  1832. ;thead
  1833. ;tr
  1834. ;* =/ hdr head.t
  1835. =/ align align.t
  1836. |-
  1837. ?~ hdr ~
  1838. :- ;th(align ?-((head align) %c "center", %r "right", %l "left", %n ""))
  1839. ;* (contents:inline (head hdr))
  1840. ==
  1841. $(hdr (tail hdr), align (tail align))
  1842. ==
  1843. ==
  1844. ;tbody
  1845. ;* %+ turn rows.t
  1846. |= [r=(list contents:inline:m)]
  1847. ^- manx
  1848. ;tr
  1849. ;* =/ row r
  1850. =/ align align.t
  1851. |-
  1852. ?~ row ~
  1853. :- ;td(align ?-((head align) %c "center", %r "right", %l "left", %n ""))
  1854. ;* (contents:inline (head row))
  1855. ==
  1856. $(row (tail row), align (tail align))
  1857. ==
  1858. ==
  1859. ==
  1860. ++ paragraph
  1861. |= [p=paragraph:leaf:m]
  1862. ^- manx
  1863. ;p
  1864. ;* (contents:inline contents.p)
  1865. ==
  1866. --
  1867. ::
  1868. ++ container
  1869. |%
  1870. ++ node
  1871. |= [n=node:container:m]
  1872. ^- manx
  1873. ?- -.n
  1874. %block-quote (block-quote n)
  1875. %ul (ul n)
  1876. %ol (ol n)
  1877. %tl (tl n)
  1878. ==
  1879. ::
  1880. ++ block-quote
  1881. |= [b=block-quote:container:m]
  1882. ^- manx
  1883. ;blockquote
  1884. ;* (~(. markdown reference-links) markdown.b)
  1885. ==
  1886. ::
  1887. ++ ul
  1888. |= [u=ul:container:m]
  1889. ^- manx
  1890. ;ul
  1891. ;* %+ turn contents.u |= [a=markdown:m]
  1892. ^- manx
  1893. ;li
  1894. ;* (~(. markdown reference-links) a)
  1895. ==
  1896. ==
  1897. ::
  1898. ++ ol
  1899. |= [o=ol:container:m]
  1900. ^- manx
  1901. ;ol(start (a-co:co start-num.o))
  1902. ;* %+ turn contents.o |= [a=markdown:m]
  1903. ^- manx
  1904. ;li
  1905. ;* (~(. markdown reference-links) a)
  1906. ==
  1907. ==
  1908. ++ tl
  1909. |= [t=tl:container:m]
  1910. ^- manx
  1911. ;ul.task-list
  1912. ;* %+ turn contents.t |= [is-checked=? a=markdown:m]
  1913. ^- manx
  1914. ;li
  1915. ;+ ?: is-checked
  1916. ;input(type "checkbox", checked "true");
  1917. ;input(type "checkbox");
  1918. ;* (~(. markdown reference-links) a)
  1919. ==
  1920. ==
  1921. --
  1922. ::
  1923. ++ markdown
  1924. |= [a=markdown:m]
  1925. ^- marl
  1926. %+ turn a |= [item=node:markdown:m]
  1927. ?- -.item
  1928. %leaf (node:leaf +.item)
  1929. %container (node:container +.item)
  1930. ==
  1931. --
  1932. --