Extending¶
You can extend Tpll’s functionality by adding custom tags and filters.
Creating a custom tag¶
Let’s create a template tag that outputs a friendly hello message:
import Tpll.Context (Context)
import Tpll.Tokenizer (Token)
import Tpll.Tags (TagAction(Render))
helloTag :: Context -> Token -> [Token] -> TagAction
helloTag ctx' token remainingTokens =
Render (remainingTokens, return "Hello! :)")
The parameters passed to the template tags are:
- The current context
- The token matched by this tag
- The next (or remaining) tokens
The return value of a template tag is a TagAction
, which can be either
Render
or RenderBlock
and we describe them below.
Normal tag¶
The helloTag
example used above is what we call here a normal tag and
uses the Render
action:
Render ([Token], IO String)
The arguments passed to this action are the list of tokens not consumed by the
tag and the IO String
to be printed out.
Block tag¶
The other type of template tag is the block tag, that returns a
RenderBlock
action. Its most common use is to create a temporary context
that will be used when consuming the block. A good example is the
{% for ... %}
tag:
<ul>
{% for arg1 in arg2 %}
<li>Value: {{ arg 1 }}
{% endfor %}
</ul>
It switches the context in every iteration of the loop. For this, we use the
RenderBlock
action, that’s used this way:
RenderBlock ([Context], Context, [Token], String)
Its arguments are:
- The list of contexts which the block will be iterated over.
- The context that will be used after the end of the block.
- The list of the remaining tokens.
- The string containing a regex pattern that identifies the token where the block must stop.
This is how the RenderBlock
action looks
This is an example of a RenderBlock
action returned by the {% for ... %}
tag:
RenderBlock (
-- context stack
[
ctx [("foo", cStr "bar"), ("item", cInt 1)],
ctx [("foo", cStr "bar"), ("item", cInt 2)],
ctx [("foo", cStr "bar"), ("item", cInt 3)],
],
-- old context
ctx [("foo", cStr "bar")],
-- remaining tokens:
-- {{ foo }}: {{ item }}; {% endfor %}
[
Variable { content = "foo", line = 2, raw = "{{ foo }}"},
Text { content = ": ", line = 2, raw = "" },
Variable { content = "item", line = 2, raw = "{{ item }}"},
Text { content = "; ", line = 2, raw = "" },
Tag { content = "endfor", line = 2, raw = "{% endfor %}"}
],
-- block delimiter
"endfor|empty"
)
And the output for this RenderBlock
action:
bar: 1; bar: 2; bar: 3;