2019-02-01 03:38:59 +00:00
|
|
|
# A flexible templater for janet. Compiles
|
|
|
|
# templates to janet functions that produce buffers.
|
|
|
|
|
|
|
|
(defn template
|
|
|
|
"Compile a template string into a function"
|
|
|
|
[source]
|
|
|
|
|
|
|
|
# State for compilation machine
|
|
|
|
(def p (parser/new))
|
|
|
|
(def forms @[])
|
|
|
|
|
|
|
|
(defn parse-chunk
|
|
|
|
"Parse a string and push produced values to forms."
|
|
|
|
[chunk]
|
|
|
|
(parser/consume p chunk)
|
|
|
|
(while (parser/has-more p)
|
|
|
|
(array/push forms (parser/produce p)))
|
|
|
|
(if (= :error (parser/status p))
|
|
|
|
(error (parser/error p))))
|
|
|
|
|
|
|
|
(defn code-chunk
|
|
|
|
"Parse all the forms in str and return them
|
|
|
|
in a tuple prefixed with 'do."
|
|
|
|
[str]
|
|
|
|
(parse-chunk str)
|
|
|
|
true)
|
|
|
|
|
|
|
|
(defn string-chunk
|
|
|
|
"Insert string chunk into parser"
|
|
|
|
[str]
|
|
|
|
(parser/insert p str)
|
|
|
|
(parse-chunk "")
|
|
|
|
true)
|
|
|
|
|
|
|
|
# Run peg
|
|
|
|
(def grammar
|
|
|
|
~{:code-chunk (* "{%" (drop (cmt '(any (if-not "%}" 1)) ,code-chunk)) "%}")
|
|
|
|
:main-chunk (drop (cmt '(any (if-not "{%" 1)) ,string-chunk))
|
|
|
|
:main (any (+ :code-chunk :main-chunk (error "")))})
|
|
|
|
(def parts (peg/match grammar source))
|
|
|
|
|
|
|
|
# Check errors in template and parser
|
|
|
|
(unless parts (error "invalid template syntax"))
|
|
|
|
(parse-chunk "\n")
|
|
|
|
(case (parser/status p)
|
|
|
|
:pending (error (string "unfinished parser state " (parser/state p)))
|
|
|
|
:error (error (parser/error p)))
|
|
|
|
|
|
|
|
# Make ast from forms
|
2019-03-12 15:35:27 +00:00
|
|
|
(def ast ~(fn [&opt params] (default params @{}) (,buffer ,;forms)))
|
2019-02-01 03:38:59 +00:00
|
|
|
|
2019-04-16 19:41:45 +00:00
|
|
|
(def ctor (compile ast (fiber/getenv (fiber/current)) source))
|
2019-02-01 03:38:59 +00:00
|
|
|
(if-not (function? ctor)
|
|
|
|
(error (string "could not compile template")))
|
|
|
|
(ctor))
|