147 lines
5.8 KiB
Fennel
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}
|