Search code examples
clojuremustache

How can one achieve introspection in mustache template engine?


(def template "{{name}}.{{surname}}
  {{#data}}
    * {{.}}
  {{/data}}")

(introspect template) => {:name "" :surname "" :data []}

Is there introspect implementation?


Solution

  • If the question is how to build the introspect function, instaparse is one way to go:

    (require '[instaparse.core :as insta])
    
    (def parse
      (insta/parser
        "<moustache> = (tagged-block / word / sp)*
         <tagged-block> = comment | section | var
         comment = <tag-open> <'!'> (word | sp)* <tag-close>
         section = section-block-open (current-item / var / comment / word / sp)* <section-block-close>
         <section-block-open> = <tag-open> <'#'> name <tag-close>
         section-block-close = tag-open '/' name tag-close
         var = <tag-open> name <tag-close>
         current-item  = <tag-open> <'.'> <tag-close>
         <word> = #'[^\\s{}]+'
         <sp> = #'[\\s]+' 
         name = #'[^\\s{}]+'
         tag-open = '{{'
         tag-close = '}}'"))
    
    (def template "{{name}}.{{surname}}
      {{#data}}
      * {{.}}
      {{/data}}")
    
    (parse template) ;=> ([:var [:name "name"]] "." [:var [:name "surname"]] "\n  " [:var [:name "#data"]] "\n  " "*" " " [:var [:name "."]] "\n  " [:var [:name "/data"]])
    
    (defn introspect [parsed]
      (->> (tree-seq sequential? seq parsed)
           (filter sequential?)
           (filter #(#{:var :section} (first %)))
           (map (juxt first (comp keyword last second)))
           (map #(case (first %)
                   :var [(second %) ""]
                   :section [(second %) []]))
           (into {})))
    
    (assert (= {:name "" :surname "" :data []}
               (introspect (parse template))))
    

    I included support for comments, other mustache constructs should be easy to add:

    (parse "Hello {{people}}!! {{! people could be the world!!}}")
    ;=> ("Hello" " " [:var [:name "people"]] "!!" " " [:comment " " "people" " " "could" " " "be" " " "the" " " "world!!"])
    

    It doesn't cover the entire moustache grammar, but it's a good start I think.