/ block.fnl
block.fnl
1 (local fennel (require :fennel)) 2 (fn _G.pp [x] (print (fennel.view x))) 3 4 (local block-grammar 5 [{:name "Section" 6 :sp-chars ["-----"] 7 :regex "%-%-%-%-%-"} 8 {:name "Image" 9 :sp-chars ["!["] 10 :regex "!%[.-%]%(.-%)\n"} 11 {:name "Tag" 12 :sp-chars [":"] 13 :end-also true 14 :regex ":.-:\n"} 15 {:name "Quote" 16 :any-space true 17 :sp-chars [">"] 18 :continue-until-no-match true 19 :regex "%s*>.-\n"} 20 {:name "Metadata" 21 :sp-chars ["%"] 22 :regex "%%.-%s.-\n"} 23 {:name "Code" 24 :sp-chars ["{{{"] 25 :regex "{{{.-\n" 26 :capture-mode true 27 :mode-regex "}}}"} 28 {:name "Header" 29 :sp-chars ["="] 30 :end-also true 31 :regex "=+[^=]-=+\n"} 32 {:name "List" 33 :sp-chars ["#" "-"] 34 :any-space true 35 :continue-until-no-match true 36 :regex "^[%s%-%#].-\n"} 37 {:name "Footnote" 38 :sp-chars ["::\n"] 39 :regex "%s*::%s-\n" 40 :capture-mode true 41 :mode-regex "%s*::%s-\n"} 42 {:name "Paragraph" 43 :regex ".-\n"}]) 44 45 (fn block-lexer [contents grammar ?debug] 46 (var d (or ?debug false)) 47 (var current 1) 48 (var blocks []) 49 (var current-block-type nil) 50 (var current-block "") 51 (var capture-mode false) 52 (while (< current (- (length contents) 1)) 53 (var matched false) 54 (let [start (string.find contents "\n" current) 55 current-str (string.sub contents current start)] 56 (each [_ g (ipairs grammar) &until matched] 57 (if d 58 (do 59 (print (.. "Current: " current)) 60 (print (.. "Start: " start)) 61 (print (.. "Current-str: " current-str)) 62 (print (.. "checking regex: " (if (not capture-mode) 63 (. g :regex) 64 (tostring (. g :mode-regex))))))) 65 (let [m (if (not capture-mode) 66 (string.match current-str (. g :regex)) ; We're not in previous capture mode 67 (if (= (. g :name) current-block-type) ; And the regex we're checking is the one we want 68 (string.match current-str (. g :mode-regex)) ; Use the mode specific regex 69 nil))] ; Or don't check 70 (if d 71 (print (.. "M: " (tostring m)))) 72 (if (not (= nil m)) ; This is a match or move on 73 (do 74 (set matched true) 75 (if 76 capture-mode ; We matched and it's capture-mode, so we end capture mode 77 (do 78 (set current-block (.. current-block current-str "\n")) 79 (table.insert blocks {:type current-block-type 80 :block current-block}) 81 (set current-block "") 82 (if d 83 (do 84 (print "Capture-mode completed") 85 (print (.. "Inserted into table: " current-block)) 86 (print (.. "New current-block: " current-block)))) 87 (set capture-mode nil) 88 (set current-block-type nil)) 89 (or ; We append to the current block 90 (= nil current-block-type) ; If it's the first block 91 (and (= (. g :name) current-block-type) ; Or it matches the previous block, 92 (not (= (. g :name) "Paragraph")))) ; and it's not a paragraph 93 (do ; Continueing adding things 94 (set current-block-type (. g :name)) 95 (set capture-mode (. g :capture-mode)) 96 (set current-block (.. current-block current-str "\n")) 97 (if d 98 (print (.. "Set current-block: " current-block)))) 99 (do ; Else we start a new block, and insert our accumulation 100 (table.insert blocks {:type current-block-type 101 :block current-block}) 102 (set current-block current-str) 103 (set capture-mode (. g :capture-mode)) 104 (if d 105 (do 106 (print (.. "Inserted into table: " current-block)) 107 (print (.. "New current-block: " current-block)) 108 (print (.. "New Capture-mode: " (tostring (. g :capture-mode)))))) 109 (set current-block-type (. g :name)))) 110 (set current (+ 1 start))) 111 (if (and ; We didn't match, BUT 112 capture-mode ; Or we're in capture mode 113 (= (. g :name) current-block-type)) ; And are testing the right regex (should be implicitly true if we reached here) 114 (do 115 (set current-block (.. current-block current-str "\n")) 116 (set matched true) ; We don't need to check the others 117 (set current (+ 1 start)) 118 (if d 119 (do 120 (print (.. "Didnt match capture-mode regex, appending anyway")) 121 (print (.. "New current-block: " current-block))))))))))) 122 (if current-block-type 123 (do 124 (if d 125 (print "End of file reached, unloading current")) 126 (table.insert blocks {:type current-block-type 127 :block current-block}))) 128 blocks) 129 130 ; Each \n point 131 ; Read contents from current to next \n 132 ; If it matches the regex 133 ; Either it's the same pattern as the last one (or the last one was nil), so we continue adding to the block-accumulator 134 ; 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 135 ; If not, continue with block descent 136 137 ; This string has weird behaviour in nvim conjure repl 138 ; (string.match "{{{python\n return arg + 1\n}}}\n" "{{{.-\n.-}}}\n") 139 140 ; (= [{:type "Paragraph" 141 ; :block "This is a paragraph." 142 ; {:type "List" 143 ; :block "- This\n- List\n- Should\n- Be One"} 144 145 {:grammar block-grammar 146 :lexer block-lexer}