Commit faca8199 authored by Marien Fressinaud's avatar Marien Fressinaud

add: Add support for `if` and `for` in Boopsy

parent b7bdd945
......@@ -299,6 +299,55 @@ This will generate the `site/my_page.html` file:
</html>
```
## Template syntax
As explained above, you can create templates under the `./templates` folder.
These templates are basic HTML files, with a pinch of special syntax.
To output the value of a variable, we already saw how to do:
```html
<p>
{{ ARTICLE_CONTENT }}
</p>
```
Any valid Python expression is accepted here. For instance, you might want to
display the article publication date in a human format:
```html
<div class="article-pubdate">
{{ datetime.strptime(ARTICLE_DATE, "%Y-%m-%d %H:%M").strftime("%a %d %B %Y")
</div>
```
Yes, it's a bit complex for now. Hopefully, you will not have to call this very
often. Improvement is planned.
Then, you might want to display content conditionnaly with a `if` statement:
```html
<head>
{% if PAGE_TITLE %}
<title>{{ PAGE_TITLE }} - {{ SITE_TITLE }}</title>
{% else %}
<title>{{ SITE_TITLE }}</title>
{% endif %}
</head>
```
`elif` statement is also supported the same way as `if`.
Finally, the `for` statement gives you the possibility to generate lists:
```html
<ul>
{% for author in ARTICLE_AUTHORS %}
<li>{{ author }}</li>
{% enfor %}
</ul>
```
## Tests
There are some tests (i.e. doctests) that can be run to check that everything
......
......@@ -70,10 +70,13 @@ class Template:
code = Code()
code.write("rendering = []")
ops_stack = []
# The template is splitted in tokens:
# - {{ a_python_expression }}
# - {% if a_python_expression %}
# - or all the other strings
tokens = re.split(r"(?s)({{.*?}})", self.template)
tokens = re.split(r"(?s)({{.*?}}|{%.*?%})", self.template)
for token in tokens:
if token.startswith("{{"):
expression = token[2:-2].strip()
......@@ -83,11 +86,87 @@ class Template:
# Expression token is just valid Python code so we add it to
# the rendering array. It will be evaluated later.
code.write(f"rendering.append({expression})")
elif token.startswith("{%"):
words = token[2:-2].strip().split()
statement = words[0]
args = words[1:]
if statement == "if":
if len(args) < 1:
raise BoopsySyntaxError(
"a condition is expected in if statement"
)
ops_stack.append("if")
condition = " ".join(args)
code.write(f"if {condition}:")
code.indent()
elif statement == "elif":
last_op = ops_stack[-1]
if last_op != "if":
raise BoopsySyntaxError(
"elif statement can only be used with a if statement"
)
if len(args) < 1:
raise BoopsySyntaxError(
"a condition is expected in elif statement"
)
condition = " ".join(args)
code.dedent()
code.write(f"elif {condition}:")
code.indent()
elif statement == "else":
last_op = ops_stack[-1]
if last_op != "if":
raise BoopsySyntaxError(
"else statement can only be used with a if statement"
)
if len(args) != 0:
raise BoopsySyntaxError("else statement expects no arguments")
code.dedent()
code.write(f"else:")
code.indent()
elif statement == "for":
if len(args) != 3:
raise BoopsySyntaxError("for statement expects 3 arguments")
if args[1] != "in":
raise BoopsySyntaxError(
"for statement expects second argument to be 'in'"
)
ops_stack.append("for")
code.write(f"for {args[0]} in {args[2]}:")
code.indent()
elif statement == "endif" or statement == "endfor":
if len(args) != 0:
raise BoopsySyntaxError(f"{statement} expects no argument")
if not ops_stack:
raise BoopsySyntaxError(
f"unexpected {statement} (too many ends)"
)
current_op = statement[3:]
expected_op = ops_stack.pop()
if expected_op != current_op:
raise BoopsySyntaxError(
f"unexpected {statement} (should be end{expected_op})"
)
code.dedent()
else:
raise BoopsySyntaxError(f"unknown statement (got '{statement}')")
elif token:
# default tokens must be added to the rendering array
literal = repr(token)
code.write(f"rendering.append({literal})")
if ops_stack:
raise BoopsySyntaxError(
f"missing end statement (expected 'end{ops_stack[-1]}')"
)
# `result` will store the `rendering` variable so we can get the finale
# string to return to the caller.
context = variables.copy()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment