Bitbucket is a code hosting site with unlimited public and private repositories. We're also free for small teams!


picostache.l - Logic-less {{mustache}} templates with PicoLisp

Sometimes the smallest mustache makes the biggest statement

picostache is an implementation of the mustache template system in PicoLisp.

Mustache is a logic-less template syntax. It can be used for HTML, config files, source code - anything. It works by expanding tags in a template using values provided in a hash or object.

We call it "logic-less" because there are no if statements, else clauses, or for loops. Instead

there are only tags. Some tags are replaced with a value, some nothing, and others a series of values.

For a language-agnostic overview of mustache's template syntax, see the mustache(5) manpage.


Below is quick example how to use picostache.:

(load "picostache.l")
(push 'Model (cons "name" "joe"))
(prinl (renderHtml Model "{{name}}"))

 > joe


A mustache template is a string that contains any number of mustache tags. Tags are indicated by the double mustaches that surround them. {{person}} is a tag, as is {{#person}}. In both examples we refer to person as the tag's key.

There are several types of tags available in picostache.


The most basic tag type is a simple variable. A {{name}} tag renders the value of the name key in the current context. If there is no such key, nothing is rendered.

All variables are NOT HTML-escaped by default.


(push 'Model (cons "name" "Joe"))
(push 'Model (cons "street" "123 Main St"))


* {{name}}
* {{street}}


 * Joe
 * 123 Main St


(prinl (renderHtml Model "* {{name}}^J* {{street}}"))


(prinl (renderHtml Model "* {{name}}
 * {{street}}"))


Sections render blocks of text one or more times, depending on the value of the key in the current context.

A section begins with a pound and ends with a slash. That is, {{#person}} begins a person section, while {{/person}} ends it. The text between the two tags is referred to as that section's "block".

The behavior of the section is determined by the value of the key.

False Values or Empty Lists

If the person key does not exist, or exists and has a value of NIL, undefined is an empty list, the block will not be rendered.


(push 'Model (cons "name"))


Shown. {{#name}}never shown{{/name}}



Non-Empty Lists

If the person key exists and is not NIL, undefined, and is not an empty list the

block will be rendered one or more times.

When the value is a list, the block is rendered once for each item in the list. The context of the

block is set to the current item in the list for each iteration. In this way we can loop over



(push 'Model (cons "todos" '(((todo . "sleep")) ((todo . "eat")))))


(setq View "{{#todos}}<b>{{todo}}</b>^J{{/#todos}}")



When looping over an array of strings, a . can be used to refer to the current item in the list.


(push 'Model '(musketeers . ("Athos" "Aramis" "Porthos" "D'Artagnan")))


(setq View "{{#musketeers}}* {{.}}^J{{/musketeers}}")


* Athos
* Aramis
* Porthos
* D'Artagnan


If the value of a section variable is a function, it will be called in the context of the current

item in the list on each iteration.


(push 'Model '(beatles . (((first . "John") (last . "Lennon")) ((first . "Paul") (last . "McCartney")))))

(push 'Model (cons "name" '((X) (pack (cdr (assoc 'first X)) " " (cdr (assoc 'last X))))))


(setq View "{{#beatles}} * {{name}}^J{{/beatles}}")


* John Lennon
* Paul McCartney

If the value of a section key is a function, it is called with the section's rendered block of text, as its first argument.


(push 'Model (cons "name" "Joe"))
(push 'Model (cons "bold" '((X) (pack "<b>" X "</b>"))))


(setq Template "{{#bold}}Hi {{name}}.{{/bold}}")


<b>Hi Joe.</b>

Inverted Sections

An inverted section opens with {{~section}} instead of {{#section}}. The block of an inverted section is rendered only if the value of that section's tag is NIL, undefined, or an empty list. NOTE This is different than standard mustache due to the way picolisp handles carets.


 #optional, can omit this altogether
 (push 'Model '(todos . NIL))


 (setq Template "{{#todos}}{{.}}{{/todos}}{{~todos}}no todos!{{/todos}}")


no todos!

Recent activity

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.