diff options
| author | 2025-01-27 06:44:44 -0500 | |
|---|---|---|
| committer | 2025-01-27 06:44:44 -0500 | |
| commit | 179ad4a9157a082fb34d0cb74e7dd6fcd68088d9 (patch) | |
| tree | aab681af468ac386a814927ae1754c65dc339a1f | |
| parent | indented code block test (diff) | |
expand starts and continues by adding current node as argument
This simplifies the parser at the expense of moving the `add-new-node!`
declaration into the `starts` procedure. This allows for the procedure
to mutate the node when needed, which is needed for properly parsing
pandoc-style grid tables.
| -rw-r--r-- | README.md | 11 | ||||
| -rw-r--r-- | market/blocks.scm | 43 | ||||
| -rw-r--r-- | market/blocks.sld | 4 | ||||
| -rw-r--r-- | market/default.scm | 77 | ||||
| -rw-r--r-- | tests/run.scm | 36 |
5 files changed, 91 insertions, 80 deletions
@@ -33,7 +33,16 @@ child block of the block. After going as far possible, the line is then checked with a `starts` procedure (stored in each block) to see if any new block can be started. -Todo: add no-paragraph blocks? +TODO: According to the continuation line rules, block continuations +consisting entirely of spaces must be placed to make sure a blank line +is in the correct block, even though such text is not visible. The +seemingly correct thing to do is to make the block continue on a blank +line, but this cannot be done for every block (then blockquotes cannot +be separated by blank lines). + +The continuation could check for an empty line. Currently empty lines are +handled in a special way. They could be merged with the `add-new-paragraph` +handler, so that continuations could check for empty lines. ## Inline Syntax diff --git a/market/blocks.scm b/market/blocks.scm index a124cab..91319ce 100644 --- a/market/blocks.scm +++ b/market/blocks.scm @@ -59,33 +59,28 @@ (else)) (define (make-empty-node data continues allowed-inside) + ;; Create a new node with no children. (make-block-node data continues allowed-inside (list-queue) #f)) (define (allowed-inside node line) - ((get-allowed-inside node) line)) + ((get-allowed-inside node) node line)) (define (continues node line) - ((get-continues node) line)) + ((get-continues node) node line)) -(define (close-active-children node) +(define (close-active-children! node) ;; Close all active children in the current node. (set-active-child! node #f)) -(define (add-new-active-child! node child) +(define (add-new-child! node child active-child?) + ;; Add a new `child` to `node`. If `active-child?` is truthy, then + ;; `child` is the new active child of `node`. Otherwise, `child` is added + ;; and no child of `node` is active. (list-queue-add-back! (node-children node) child) - (set-active-child! node child)) - -(define (add-new-child node new-child-of-node continue-node line) - ;; Add `new-child-of-node` to `node`, and continue parsing with - ;; `continue-node` and `line`. - ;; - ;; For leaf blocks (like headings), `continue-node` is `#f`, which - ;; means to close the block. - (add-new-active-child! node new-child-of-node) - (if continue-node - (parse-line-to-node continue-node line) - (set-active-child! node #f))) + (set-active-child! node (if active-child? + child + #f))) (define (process-active-child node line active-child) ;; Act on the `active-child` with the given `line`. @@ -95,7 +90,8 @@ ;; If the new line is the start of a block allowed in `node` then close ;; `child` and add a new child. Otherwise, add to the paragraph. (cond - ((allowed-inside node line) first-arg => (cut add-new-child node <...>)) + ((allowed-inside node line) first-arg => parse-line-to-node!) + ((empty-line? line) (close-active-children! node)) (else (xstring-append! active-child line #\newline)))) (define (handle-child-block) ;; Handle insertion of a new line when the active child is a block. @@ -104,8 +100,9 @@ ;; allowed after `child` in this node. Finally, check if `line` is ;; allowed inside this node. If not, add the line as a paragraph. (cond - ((continues active-child line) => (cut parse-line-to-node active-child <>)) - ((allowed-inside node line) first-arg => (cut add-new-child node <...>)) + ((continues active-child line) => (cut parse-line-to-node! active-child <>)) + ((allowed-inside node line) first-arg => (cut parse-line-to-node! <...>)) + ((empty-line? line) (close-active-children! node)) (else (add-new-paragraph node line)))) (cond ((xstring? active-child) @@ -117,19 +114,17 @@ ;; Add new node as the active child to `node` with the data of `line`. (let ((new-child (line->xstring line))) (xstring-append! new-child #\newline) - (add-new-active-child! node new-child))) + (add-new-child! node new-child #t))) -(define (parse-line-to-node node line) +(define (parse-line-to-node! node line) ;; NODE. ;; ;; If there is an active node, either add the line to the node or start a ;; new node. If there is no active node, start a new node, either by ;; starting a new block or by starting a new paragraph. (cond - ((empty-line? line) (close-active-children node)) ((get-active-child node) => (cut process-active-child node line <>)) - ((allowed-inside node line) - first-arg => (cut add-new-child node <...>)) + ((allowed-inside node line) first-arg => parse-line-to-node!) (else (add-new-paragraph node line)))) diff --git a/market/blocks.sld b/market/blocks.sld index d4a78dc..9a10edd 100644 --- a/market/blocks.sld +++ b/market/blocks.sld @@ -24,6 +24,6 @@ (else)) (export make-block-node node-data node-children make-empty-node block-node? - add-new-active-child! - parse-line-to-node) + add-new-child! + parse-line-to-node!) (include "blocks.scm")) diff --git a/market/default.scm b/market/default.scm index 0f0ecae..85a1e53 100644 --- a/market/default.scm +++ b/market/default.scm @@ -14,8 +14,8 @@ | limitations under the License. |# -(define (always-false line) #f) -(define (line-always-true line) line) +(define (always-false node line) #f) +(define (line-always-true node line) line) ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Unordered lists and unordered list items @@ -29,7 +29,7 @@ (define (unordered-list-item? x) (and (block-node? x) (unordered-list-item-data? (node-data x)))) -(define (starts-unordered-list line) +(define (starts-unordered-list node line) (define (on-success line ch number-of-spaces) (define continues (let ((prefix (make-string (+ number-of-spaces 1) #\space))) @@ -37,7 +37,8 @@ (let ((item (make-empty-node (make-unordered-list-item-data ch) continues (default-allowed)))) - (values item item line))) + (add-new-child! node item #t) + (values item line))) (define (count-more-spaces line ch num) (cond ((eqv? (line-ref line) #\space) @@ -61,20 +62,21 @@ (define (code-block? x) (and (block-node? x) (eq? (node-data x) 'code-block))) -(define (starts-code-block line) - (define (continues-code-block line) +(define (starts-code-block node line) + (define (continues-code-block node line) (or (line-prefix line " ") (line-prefix line #\tab))) (define (on-success line) (let* ((paragraph (string->xstring "")) - (node (make-block-node 'code-block - continues-code-block - always-false - (list-queue paragraph) - paragraph))) - (values node node line))) + (new-node (make-block-node 'code-block + continues-code-block + always-false + (list-queue paragraph) + paragraph))) + (add-new-child! node new-node #t) + (values new-node line))) (cond - ((continues-code-block line) => on-success) + ((continues-code-block node line) => on-success) (else #f))) ;;; ;;;;;;;;;;;;;;;;; @@ -84,7 +86,7 @@ (define (block-quote? x) (and (block-node? x) (eq? (node-data x) 'block-quote))) -(define (continues-block-quote line) +(define (continues-block-quote node line) (cond ((line-prefix line ">") => (cut line-prefix-opt <> " ")) (else #f))) @@ -94,15 +96,17 @@ continues-block-quote (default-allowed))) -(define (starts-block-quote line) +(define (starts-block-quote node line) (define (on-success line number-of->) (let ((top-node (make-empty-block-quote))) (let loop ((cur-node top-node) (number-of-> number-of->)) (if (= number-of-> 1) - (values top-node cur-node line) + (begin + (add-new-child! node top-node #t) + (values cur-node line)) (let ((next-node (make-empty-block-quote))) - (add-new-active-child! cur-node next-node) + (add-new-child! cur-node next-node #t) (loop next-node (- number-of-> 1))))))) (define (expect-nesting-or-space line number-of->) (let ((ch (line-ref line))) @@ -132,16 +136,16 @@ (define (atx-heading-indent block) (atx-heading-data-indent (node-data block))) -(define (starts-atx-heading line) +(define (starts-atx-heading node line) ;; TODO: add detecting the end of the heading (define (on-success line indent) - (values (make-block-node (make-atx-heading-data indent) - always-false - always-false - (list-queue (line->xstring line)) - #f) - #f - line)) + (let ((new-node (make-block-node (make-atx-heading-data indent) + always-false + always-false + (list-queue (line->xstring line)) + #f))) + (add-new-child! node new-node #f) + (values node line))) (let detect ((line line) (indent 0)) (cond @@ -160,12 +164,12 @@ (define default-allowed (make-parameter - (lambda (line) + (lambda (node line) (cond - ((starts-code-block line) first-arg => values) - ((starts-block-quote line) first-arg => values) - ((starts-atx-heading line) first-arg => values) - ((starts-unordered-list line) first-arg => values) + ((starts-code-block node line) first-arg => values) + ((starts-block-quote node line) first-arg => values) + ((starts-atx-heading node line) first-arg => values) + ((starts-unordered-list node line) first-arg => values) (else #f))))) ;;; ;;;;;;;;;;;;;;;;; @@ -177,10 +181,13 @@ (define (document-node) (make-empty-node 'document - (lambda (x) x) + line-always-true (default-allowed))) -(define (parse->document str node) - (let ((node (or node (document-node)))) - (parse-line-to-node node (string->line str)) - node)) +(define parse->document + (case-lambda + ((str) (parse->document str (document-node))) + ((str node) + (parse-line-to-node! node (string->line str)) + node))) + diff --git a/tests/run.scm b/tests/run.scm index 550534d..4e8c8ae 100644 --- a/tests/run.scm +++ b/tests/run.scm @@ -29,14 +29,14 @@ (test-group "paragraphs" (let ((str - (chain (parse->document "hello, world" #f) + (chain (parse->document "hello, world") (node-children _) (list-queue-list _) (list-ref _ 0) (xstring->string _)))) (test-equal "single line" str "hello, world\n")) (let ((str - (chain (parse->document "hello," #f) + (chain (parse->document "hello,") (parse->document "world" _) (node-children _) (list-queue-list _) @@ -45,7 +45,7 @@ (test-equal "two lines" str "hello,\nworld\n")) (test-group "two paragraphs" (let ((children - (chain (parse->document "hello" #f) + (chain (parse->document "hello") (parse->document "" _) (parse->document "world" _) (node-children _) @@ -59,7 +59,7 @@ (test-group "block quotes" (test-group "single line" - (let ((block (chain (parse->document "> hello, world" #f) + (let ((block (chain (parse->document "> hello, world") (node-children _) (list-queue-list _) (list-ref _ 0)))) @@ -69,7 +69,7 @@ (list-ref _ 0) (xstring->string _)))) (test-equal "string" str "hello, world\n")))) - (let ((str (chain (parse->document "> hello," #f) + (let ((str (chain (parse->document "> hello,") (parse->document "> world" _) (node-children _) (list-queue-list _) @@ -80,7 +80,7 @@ (xstring->string _)))) (test-equal "two lines" str "hello,\nworld\n")) (test-group "multiple paragraphs" - (let ((lst (chain (parse->document "> hello" #f) + (let ((lst (chain (parse->document "> hello") (parse->document "> " _) (parse->document "> world" _) (node-children _) @@ -95,7 +95,7 @@ (xstring->string (list-ref lst 1)) "world\n"))) (test-group "interrupted" - (let ((lst (chain (parse->document "> hello, world" #f) + (let ((lst (chain (parse->document "> hello, world") (parse->document "outside of a block quote" _) (node-children _) (list-queue-list _)))) @@ -116,7 +116,7 @@ (xstring->string (list-ref lst 1))))) (test-group "two nested with spaces" (define block - (chain (parse->document "> > hello, world" #f) + (chain (parse->document "> > hello, world") (parse->document "> > continuing nested" _) (parse->document "> outside of the block quote" _) (node-children _) @@ -143,7 +143,7 @@ (xstring->string _)))) (test-group "two nested without spaces" (define block - (chain (parse->document ">> hello, world" #f) + (chain (parse->document ">> hello, world") (parse->document ">> continuing nested" _) (parse->document "> at first level" _) (parse->document "> continuing first level" _) @@ -170,7 +170,7 @@ (test-group "atx headings" (test-group "heading 1" - (let ((heading (chain (parse->document "# hello" #f) + (let ((heading (chain (parse->document "# hello") (node-children _) (list-queue-list _) (list-ref _ 0)))) @@ -181,7 +181,7 @@ (list-ref _ 0) (xstring->string _))))) (test-group "heading 6" - (let ((heading (chain (parse->document "###### hello" #f) + (let ((heading (chain (parse->document "###### hello") (node-children _) (list-queue-list _) (list-ref _ 0)))) @@ -192,7 +192,7 @@ (list-ref _ 0) (xstring->string _))))) (test-group "heading followed by paragraph" - (let ((children (chain (parse->document "## hello, " #f) + (let ((children (chain (parse->document "## hello, ") (parse->document "world" _) (parse->document "## another heading" _) (node-children _) @@ -218,7 +218,7 @@ (xstring->string _))) (test-equal "text" "another heading" text)))) (test-group "heading inside block quote" - (let ((children (chain (parse->document "> # nested heading" #f) + (let ((children (chain (parse->document "> # nested heading") (parse->document "> text inside quote" _) (parse->document "### outside of quote" _) (node-children _) @@ -253,7 +253,7 @@ (test-group "code blocks" (test-group "single line, 4 spaces" - (let ((block (chain (parse->document " (procedure? call/cc)" #f) + (let ((block (chain (parse->document " (procedure? call/cc)") (node-children _) (list-queue-list _) (list-ref _ 0)))) @@ -265,7 +265,7 @@ (list-ref _ 0) (xstring->string _))))) (test-group "single line, tab" - (let ((block (chain (parse->document "\t(procedure? call/cc)" #f) + (let ((block (chain (parse->document "\t(procedure? call/cc)") (node-children _) (list-queue-list _) (list-ref _ 0)))) @@ -277,7 +277,7 @@ (list-ref _ 0) (xstring->string _))))) (test-group "multiple lines, mixing" - (let ((str (chain (parse->document " (lambda (x)" #f) + (let ((str (chain (parse->document " (lambda (x)") (parse->document "\t x)" _) (node-children _) (list-queue-list _) @@ -290,7 +290,7 @@ "(lambda (x)\n x)\n" str))) (test-group "no quotes in code blocks" - (let ((first-child (chain (parse->document " > not a block" #f) + (let ((first-child (chain (parse->document " > not a block") (node-children _) (list-queue-list _) (list-ref _ 0) @@ -302,7 +302,7 @@ "> not a block\n" (xstring->string first-child)))) (test-group "code block in quotes" - (let ((node (chain (parse->document "> quoted code" #f) + (let ((node (chain (parse->document "> quoted code") (node-children _) (list-queue-list _) (list-ref _ 0)))) |
