incd-parser/block.fnl
2024-01-17 21:07:41 -08:00

147 lines
5.8 KiB
Fennel

(local fennel (require :fennel))
(fn _G.pp [x] (print (fennel.view x)))
(local block-grammar
[{:name "Section"
:sp-chars ["-----"]
:regex "%-%-%-%-%-"}
{:name "Image"
:sp-chars ["!["]
:regex "!%[.-%]%(.-%)\n"}
{:name "Tag"
:sp-chars [":"]
:end-also true
:regex ":.-:\n"}
{:name "Quote"
:any-space true
:sp-chars [">"]
:continue-until-no-match true
:regex "%s*>.-\n"}
{:name "Metadata"
:sp-chars ["%"]
:regex "%%.-%s.-\n"}
{:name "Code"
:sp-chars ["{{{"]
:regex "{{{.-\n"
:capture-mode true
:mode-regex "}}}"}
{:name "Header"
:sp-chars ["="]
:end-also true
:regex "=+[^=]-=+\n"}
{:name "List"
:sp-chars ["#" "-"]
:any-space true
:continue-until-no-match true
:regex "^[%s%-%#].-\n"}
{:name "Footnote"
:sp-chars ["::\n"]
:regex "%s*::%s-\n"
:capture-mode true
:mode-regex "%s*::%s-\n"}
{:name "Paragraph"
:regex ".-\n"}])
(fn block-lexer [contents grammar ?debug]
(var d (or ?debug false))
(var current 1)
(var blocks [])
(var current-block-type nil)
(var current-block "")
(var capture-mode false)
(while (< current (- (length contents) 1))
(var matched false)
(let [start (string.find contents "\n" current)
current-str (string.sub contents current start)]
(each [_ g (ipairs grammar) &until matched]
(if d
(do
(print (.. "Current: " current))
(print (.. "Start: " start))
(print (.. "Current-str: " current-str))
(print (.. "checking regex: " (if (not capture-mode)
(. g :regex)
(tostring (. g :mode-regex)))))))
(let [m (if (not capture-mode)
(string.match current-str (. g :regex)) ; We're not in previous capture mode
(if (= (. g :name) current-block-type) ; And the regex we're checking is the one we want
(string.match current-str (. g :mode-regex)) ; Use the mode specific regex
nil))] ; Or don't check
(if d
(print (.. "M: " (tostring m))))
(if (not (= nil m)) ; This is a match or move on
(do
(set matched true)
(if
capture-mode ; We matched and it's capture-mode, so we end capture mode
(do
(set current-block (.. current-block current-str "\n"))
(table.insert blocks {:type current-block-type
:block current-block})
(set current-block "")
(if d
(do
(print "Capture-mode completed")
(print (.. "Inserted into table: " current-block))
(print (.. "New current-block: " current-block))))
(set capture-mode nil)
(set current-block-type nil))
(or ; We append to the current block
(= nil current-block-type) ; If it's the first block
(and (= (. g :name) current-block-type) ; Or it matches the previous block,
(not (= (. g :name) "Paragraph")))) ; and it's not a paragraph
(do ; Continueing adding things
(set current-block-type (. g :name))
(set capture-mode (. g :capture-mode))
(set current-block (.. current-block current-str "\n"))
(if d
(print (.. "Set current-block: " current-block))))
(do ; Else we start a new block, and insert our accumulation
(table.insert blocks {:type current-block-type
:block current-block})
(set current-block current-str)
(set capture-mode (. g :capture-mode))
(if d
(do
(print (.. "Inserted into table: " current-block))
(print (.. "New current-block: " current-block))
(print (.. "New Capture-mode: " (tostring (. g :capture-mode))))))
(set current-block-type (. g :name))))
(set current (+ 1 start)))
(if (and ; We didn't match, BUT
capture-mode ; Or we're in capture mode
(= (. g :name) current-block-type)) ; And are testing the right regex (should be implicitly true if we reached here)
(do
(set current-block (.. current-block current-str "\n"))
(set matched true) ; We don't need to check the others
(set current (+ 1 start))
(if d
(do
(print (.. "Didnt match capture-mode regex, appending anyway"))
(print (.. "New current-block: " current-block)))))))))))
(if current-block-type
(do
(if d
(print "End of file reached, unloading current"))
(table.insert blocks {:type current-block-type
:block current-block})))
blocks)
; Each \n point
; Read contents from current to next \n
; If it matches the regex
; Either it's the same pattern as the last one (or the last one was nil), so we continue adding to the block-accumulator
; Or it's a different pattern, which means the current block is complete add it to the blocks list, and start a new accumulator filled with our current-str
; If not, continue with block descent
; This string has weird behaviour in nvim conjure repl
; (string.match "{{{python\n return arg + 1\n}}}\n" "{{{.-\n.-}}}\n")
; (= [{:type "Paragraph"
; :block "This is a paragraph."
; {:type "List"
; :block "- This\n- List\n- Should\n- Be One"}
{:grammar block-grammar
:lexer block-lexer}