Intro
A Template Processor is a software component that given a
input and a
context renders a output based on that context. The input would contain instructions as well as data that is used by the processor; These instructions are formally known as the Template Language.
Some popular template engines (and languages) can be found
here.
Template engines are very common in web development frameworks or as standard libraries, they're also used in every major IDEs or text editors as means to do code generation for projects or snippets of code.
Examples
Let's start by showing some examples and extract the template language we're using from them:
Input: Hello {{name}}!
Context: name: xterm
Output: Hello xterm!
-
Input: Hello {{name}}! How's it going {{name}}?
Context: name: xterm
Output: Hello xterm! How's it going xterm?
-
Input: Please give me {{num_apples}} apples and {{num_oranges}} oranges
Context: num_apples: 5, num_oranges: 3
Output: Please give me 5 apples and 3 oranges
-
In the previous examples you will notice that part of our template language is the ability to substitute {{variable}} with it's corresponding value in the context (which is a feature provided in virtually most languages in either string formatters or string interpolation). Let's add some control flow logic to our template language:
Input:
{% if adult %}
You're an adult!
{% endif %}
Context: adult: true
If adult is true
Output: You're an adult!
If adult is false
Output: (nothing)
-
Input:
{% for todo in todolist %}
{{todo}}<br>
{% endfor %}
Context: todolist: ['Buy food', 'Eat food', 'Sleep']
Output:
Buy food<br>Eat food<br>Sleep
-
Exercise
Your task in this exercise is to build a Mini Template Processor using the programming language of your choice. It must be able to render the following instructions:
{{variable}} # for substitutions
{% if cond %}content{% endif %} # for conditionals
{% for foo in bar %}content{% endfor %} # looping construct
In order to maintain the some consistency across the different solutions in different languages, please consider adhering to the following programming interface:
render(input, context) -> output
Here's sample usage:
assert render('{{name}}', {'name': 'xterm'}) == 'xterm'
assert render('{% if value %}sup{% endif %}', {'value': false}) == ''
assert render('{% for i in nums %}{{i}}{% endfor %}', {'nums': range(10)}) == '0123456789'
P.S.1: The resulting template should obviously maintain the whitespace provided in the input.
P.S.2: Extra context does not err the execution but improperly formatted template does.
Please don't let
performance hinder the possibility of exotic solutions, this is meant as a fun exercise.
Bonus Points
Provide a value filter mechanism that you could apply on variables, examples:
{{ name|len }}
would output 5 for a context of name: xterm
{% for num in nums|even %}
{{ num }}
{% endfor %}
would output 2468 for a context of nums: [1,2,3,4,5,6,7,8,9]
you could optionally provide a parameter to the filter through the following syntax:
{{ variable|filter:param1,param2 }}
the programming interface for the filters is as follows:
valuefilter(value, *params)