-
Notifications
You must be signed in to change notification settings - Fork 126
state machine
Phil Hagelberg edited this page Aug 14, 2024
·
3 revisions
;; Because Fennel has tail-call optimization, you can use direct function calls
;; to construct a state machine for parsing. This code constructs a parser for
;; strings that use # and {} as tags and delimiters to structure text. For
;; example, this text:
;; bing bang #start-entry{1} This is a #test entry with a ##flagged
;; #tag{that has many arguments to it} #end-entry
;; ... parses into this data:
;; -> ["bing bang" "#start-entry{1}" " This is a" "#test" "entry with a"
;; "##flagged" "#tag{that has many arguments to it}" "#end-entry"]
;; For more about using tail-calls for state machines, see the book Programming
;; in Lua: https://www.lua.org/pil/6.3.html
;; because the state functions are mutually recursive, we have to put them in
;; a table, otherwise they won't all be in scope for each other.
(local parsers {})
(fn add-char [results char]
(let [last (. results (# results))]
(doto results
(tset (# results) (.. last char)))))
(fn trim-trailing-space [results]
(let [last (. results (# results))]
(tset results (# results) (last:gsub "%s+$" ""))
(when (= (. results (# results)) "")
(table.remove results))))
(fn next-item [results]
(doto results
(trim-trailing-space)
(table.insert "")))
(fn parsers.base [str i results]
(match (string.sub str i i)
"" results ; done!
"#" (parsers.pound str (+ i 1) (-> results (next-item) (add-char "#")))
"{" (error "Curly braces must come after pound")
char (parsers.base str (+ i 1) (add-char results char))))
(fn parsers.pound [str i results]
(match (string.sub str i i)
"" results ; done!
"{" (parsers.curly str (+ i 1) (add-char results "{"))
" " (parsers.begin str (+ i 1) (next-item results))
char (parsers.pound str (+ i 1) (add-char results char))))
(fn parsers.curly [str i results]
(match (string.sub str i i)
"}" (parsers.begin str (+ i 1) (-> results (add-char "}") (next-item)))
"{" (error "Curly braces may not nest.")
"" (error "Incomplete curly!")
char (parsers.curly str (+ i 1) (add-char results char))))
(fn parsers.begin [str i results]
(match (string.sub str i i)
" " (parsers.begin str (+ i 1) results) ; consume all leading space
char (parsers.base str i results))) ; then transition to base state
(local view (require :fennel.view))
(print (view (parsers.begin (table.concat arg " ") 1 [""])))