cripslock

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit fa8b9f9c8df7c9b44a8a7cdbace1dd087a2dc216
parent 99599adeb54817639d592c72d929d7db1765f4a3
Author: Daniel Barlow <dan@telent.net>
Date:   Thu, 17 Jul 2025 13:51:06 +0100

WIP change approach to list parsing

Diffstat:
Mtextile.fnl | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 78 insertions(+), 38 deletions(-)

diff --git a/textile.fnl b/textile.fnl @@ -1,5 +1,8 @@ (import-macros { : define-tests : expect : expect= } :assert) +(fn list-append [a1 a2] + (icollect [_ v (ipairs a2) &into a1] v)) + (fn read-blocks [stream] (fn [] (let [lines (icollect [l #(stream:read "L") @@ -151,65 +154,102 @@ ) -;; a list item starts with one or more * or # in the left-hand column and -;; continues until there is another list item of the same type (ul/ol) -;; nesting level, or until the end of the block -;; a list item may contain another list -;; there need not be a blank line before the list +;; a list item starts with any list token *+ or #+ and continues until +;; - end of block +;; - next occurence of the same token +;; - occurence of a parent token -(fn wrap-list-item [tok content] - (let [list-type (if (= (string.sub tok 1 1) "#") :ol :ul) - inmost? (= (# tok) 1)] - (if inmost? - [list-type [:li content]] - [list-type [:li - (wrap-list-item (string.sub tok 2) content)]]))) +;; a list item may contain another list -(fn list-append [a1 a2] - (icollect [_ v (ipairs a2) &into a1] v)) +;; however, the third case will never occur: +;; - a toplevel list has no parent list and therefore lasts until end +;; of the block. +;; - when splitting a child list we [pass it only a single list item +;; of the parent list, so so won't encounter a parent list token + +;; there need not be a blank line before the list -(fn collapse-lists [lists] - (case lists - [[tag1 & els1] [tag1 & els2] & more] - (do - (collapse-lists - (cons-tag - (cons-tag tag1 (list-append els1 els2)) - more))) - [first & rest] - (cons-tag first (collapse-lists rest)) - [] [] - )) +(fn chop-block [tok block start] + (case (string.find block tok start) + (s e) (cons-tag (string.sub block start (- s 1)) + (chop-block tok block (+ 1 e))) + nil [(string.sub block start)])) +(fn split-list-items [block] + (let [tok (string.match block "%f[^\0\n]([%*%#]+)")] + (if tok + (values + (chop-block (.. "%f[^\0\n]" (string.gsub tok "(.)" "%%%1" ) "%f[^%*%#] *") block 1) + (if (= (string.sub tok 1 1) "#") :ol :ul)) + (values [block] nil)))) (fn extract-lists [block] - (. - (collapse-lists - (icollect [(tok content) (string.gmatch block "([%#%*]+) *([^%#%*]+)")] - (wrap-list-item tok content))) - 1)) + (let [([preamble & lists] tag) (split-list-items block)] + (if tag + (list-append + (or (inline-markup preamble) []) + [(cons-tag tag (icollect [_ i (ipairs lists)] + (cons-tag :li (extract-lists i))))]) + (inline-markup preamble)))) (define-tests :extract-lists - (expect= (extract-lists "* item 1\n* item 2\n*item 3\n") - [:ul [:li "item 1\n"] [:li "item 2\n"] [:li "item 3\n"]]) + + (case (split-list-items "there is no list in this para\n") + (text ?tag) + (and + (expect= text ["there is no list in this para\n"]) + (expect= ?tag nil))) + + (expect= + (pick-values 1 (split-list-items "para\n* item 1\n*item 2\n")) + ["para\n" "item 1\n" "item 2\n"]) + + (expect= + (pick-values 1 (split-list-items "* item 1\n*item 2\n")) + ["" "item 1\n" "item 2\n"]) + + (expect= (extract-lists "* item 1\n* item 2\n* item 3\n") + [ [:ul [:li "item 1\n"] [:li "item 2\n"] [:li "item 3\n"]]]) + + (expect= (extract-lists "* item 1\n* item 2\n* item 3\n") + [ [:ul [:li "item 1\n"] [:li "item 2\n"] [:li "item 3\n"]]]) + + (expect= (extract-lists "* item 1\n* item 2\n* item 3\n") + [[:ul [:li "item 1\n"] [:li "item 2\n"] [:li "item 3\n"]]]) (expect= (extract-lists "* item 1\n** subitem\n*item 2\n") - [:ul [:li "item 1\n"] [:li [:ul [:li "subitem\n"]]] [:li "item 2\n"]]) + [[:ul + [:li "item 1\n" + [:ul [:li "subitem\n"]]] + [:li "item 2\n"]]]) (expect= (extract-lists "# item 1\n# item 2\n## subitem\n") - [:ol [:li "item 1\n"] [:li "item 2\n"] [:li [:ol [:li "subitem\n"]]]]) + [[:ol + [:li "item 1\n"] + [:li "item 2\n" + [:ol [:li "subitem\n"]]]]]) (expect= (extract-lists "* item 1\n** subitem\n*item 2\n") - [:ul [:li "item 1\n"] [:li [:ul [:li "subitem\n"]]] [:li "item 2\n"]]) - + [[:ul + [:li "item 1\n" [:ul [:li "subitem\n"]]] + [:li "item 2\n"]]]) - ) + (expect= (extract-lists "* item 1\n# numbered item\n* item 2\n") + [[:ul + [:li "item 1\n" + [:ol [:li "numbered item\n"]]] + [:li "item 2\n"]]]) + (expect= (extract-lists "* item 1\n* _emph_ item 2\n* item 3\n") + [ [:ul [:li "item 1\n"] + [:li [:em "emph"] " item 2\n"] + [:li "item 3\n"]]]) + ) {}