Skip to content

Instantly share code, notes, and snippets.

@need12648430
Last active Aug 29, 2015
Embed
What would you like to do?
tiny mustache-like templating engine in 2.6kb/108 loc
var template = "{?condition}Condition exists! {condition}{/condition}\n\n" +
"{^whereami}I am not here!{/whereami}\n\n" +
"{#list}{name} is {age} years old! {?old}They are *old*!{/old}{!old}They are not old!{/old}\n{/list}";
console.log(stache(template, {
condition: "Hello world!",
list: [
{name: "Charles", age: "25"},
{name: "Bob", age: "42", old: true},
{name: "Samantha", age: "32"},
]
}));
var stache = (function (template, data) {
function get(scope, key) {
for(var i = scope.length - 1; i >= 0; i --)
if (scope[i][key])
return scope[i][key];
return null;
}
function accept (tokens, types) {
var token;
if (types.indexOf((token = (tokens.length > 0 ? tokens.shift() : ["eof", null]))[0]) > -1)
return token;
else
throw {
name: "UnexpectedTokenException",
message: [t[2], t[0]]
};
}
function parse(template, data) {
var match, tokens = [], rules = [
{t: "iftag", r: /^(\{[#\?]([a-zA-Z0-9]+)\})/},
{t: "notiftag", r: /^(\{[!\^]([a-zA-Z0-9]+)\})/},
{t: "endtag", r: /^(\{\/([a-zA-Z0-9]+)\})/},
{t: "tag", r: /^(\{([a-zA-Z0-9]+)\})/},
{t: "text", r: /^([\s\S]*?)\{[#\?!\^\/]?[a-zA-Z0-9]+\}/},
{t: "text", r: /^([\s\S]*?)$/},
];
while (template.length > 0) { // lex
for (var i = 0; i < rules.length; i ++) {
if (!(match = template.match(rules[i].r)))
continue;
break;
}
tokens.push([rules[i].t, match, tokens.length]);
template = template.substring(match[1].length);
}
return statement(tokens, [data]); // parse
}
function statement (tokens, scope) {
var token, result = "";
while (tokens.length > 0) {
token = accept(tokens, ["iftag", "notiftag", "text", "tag"]);
if (["iftag", "notiftag"].indexOf(token[0]) > -1)
result += condition(tokens, token, scope);
else if (token[0] == "tag")
result += tag(token, scope);
else if (token[0] == "text")
result += token[1][1];
}
return result;
}
function condition (tokens, start, scope) {
var contents = [], end, value, statements, result = "";
for (;;) {
end = accept(tokens, ["iftag", "notiftag", "endtag", "text", "tag"]);
if (end[0] == "endtag" && start[1][2] == end[1][2])
break;
contents.push(end);
}
if (start[0] == "iftag" && !!(value = get(scope, start[1][2]))) {
if (Array.isArray(value)) {
buffer = "";
for (var i = 0; i < value.length; i ++) {
statements = contents.slice(0);
if (typeof value[i] === "object")
scope.push(value[i]);
result += statement(statements, scope);
scope.pop();
}
return result;
}
return statement(contents, scope);
} else if (start[0] == "notiftag" && !get(scope, start[1][2]))
return statement(contents, scope);
return "";
}
function tag (token, scope) {
if (!!get(scope, token[1][2]))
return get(scope, token[1][2]);
return "";
}
return parse;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment