Safe Haskell | None |
---|---|
Language | Haskell2010 |
Language.Ginger
Description
Jinja is a dynamically-typed template language designed for generating HTML and text output.
Ginger2 provides most of the Jinja template language, as much as that makes sense in a Haskell host application.
We do, however, avoid some of the most blatant Pythonisms, especially where we felt the features in question were more of an accidental result of binding template constructs to Python constructs.
We also add some optional features that are absent from the Python implementation, but that we felt useful and worth adding.
Template Syntax
Minimal Example
<!DOCTYPE html> <html> <head> <title>{{ title }}</title> </head> {# This is a comment. Comments are removed from the output. #} <body> <menu id="nav-main"> {% for item in navigation %} <li><a href="{{ item.url }}">{{ item.label }}</a></li> {% endfor %} </menu> <div class="layout-content-main"> <h1>{{ title }}</h1> {{ body }} </div> </body> </html>
Comments
Comments are delimited by {#
... #}
markers.
It is not currently possible to nest comments.
Interpolation
The {{
and }}
markers delimit interpolation: anything between these
markers is interpreted as an expression, and the result of evaluating that
expression is written into the template output at this point.
The following expression constructs are available:
Literals
String Literals
String literals can be written with single quotes:
'Hello, world!'
...or double quotes:
"Hello, world!"
Quotes inside string literals can be escaped using backslashes:
"Hello, \"world!\""
Numeric Literals
Integers can be written in decimal:
23
Floats can be written as decimal fractions:
23.5
...or in scientific notation:
0.25e10
Booleans
Two boolean literals exist: true
and false
. For backwards compatibility
with Jinja, True
and False
are accepted as alternative spellings.
None
The literal none
designates the NULL
object (approximately equivalent
to ()
or Nothing
in Haskell). For backwards compatibility with Jinja,
None
is accepted as an alternative spelling.
Variables
Variables (built-in, passed in by the host application, or defined within the template itself) are written as barewords. Their names must follow the rules for identifiers:
- They must start with an alphabetic ASCII character or an underscore.
- All subsequent characters must be alphanumeric ASCII characters or underscores.
Lists
List literals are written as comma-separated lists between square brackets, like in most programming languages:
["foo", "bar", "baz"]
Because Ginger is dynamically typed, lists are heterogenous, so the following is perfectly fine:
["foo", 23, none, true, 1.5]
Unlike Jinja, Ginger does not distinguish lists from tuples; there is only one list type, and all lists in Ginger are immutable.
Dictionaries
Dictionaries ("dicts") are key-value association containers. They are written as comma-separated lists of dot-separated key/value pairs between curly braces, like so:
{ "foo": 23, "bar": 42 }
Keys and values are both interpreted as expressions; this means that string
keys must be written as string literals, not barewords - barewords are
interpreted as variables, so for example { foo: "bar" }
will not make a
dictionary key "foo", but rather try to look up a variable named foo
in
the current scope, and use its value as the key.
Values can be anything; keys, however, must be scalars, that is, strings,
numbers, booleans, or none
.
Dot Member Access
Members of dictionaries and (some) native objects can be obtained using dot access syntax, like so:
someDictionary.foo
Note that the member name is written as a bareword; these are *not*
interpreted as variables, and quoting member names in dot syntax like string
literals is a syntax error.
Dot syntax can also be used to access built-in methods of various types of
values; for example, the string
type provides a method upper()
, which
returns the original string converted to uppercase, so you can write:
"Hello, world!".upper()
...which will yield the string "HELLO, WORLD!"
.
Dot syntax will prioritize *attributes* (built-in properties) over *items* (data items stored in a dictionary).
Indexing
Members of dictionaries, as well as elements of lists and characters in a string, can be accessed using an index between square brackets, as follows.
Get an item from a list:
someList[3]
Get a member/item from a dictionary:
someDict["foo"]
Get the n-th character from a string:
someString[3]
Note that strings and lists will only accept integer indices, while dictionaries and native objects may accept arbitrary scalars.
Note further that, unlike dot syntax, the thing between square brackets is evaluated as an expression, so if you want to access a string key in a dictionary, it must be written as a quoted string literal. The following is an example of accessing a dictionary member through a variable key:
someDict[foo]
This will *not* look up a key named "foo", but rather, try to find a
variable named "foo" in the current scope, get its value, and use that as a
key to look up in someDict
.
Indices into strings and lists are zero-based, that is, [0]
yields the
first element of a list, or the first character of a string.
Square bracket syntax will prioritize *items* (data items stored in a container) over *attributes* (built-in methods associated with a value).
Slicing
Square bracket syntax can also be used to get sub-ranges from a list or
string, using the colon (:
) to separate the start and end of the desired
range. Negative indices imply counting from the end of the range; absent
indices refer to the start or end of the original sequence respectively.
Examples:
"Hello, world!"[1:2]
Get a substring of length 2, starting at the second character: "el".
"Hello, world!"[:2]
Get a substring of length 2, starting at the beginning of the string: He.
"Hello, world!"[-2:]
Get a substring up to the end of the string, starting two characters before the end: "d!".
"Hello, world!"[2:-2]
Get a substring from the third character up to 2 characters before the end of the string: "llo, worl".
Unary Operators
Two unary operators are available, both written in prefix notation:
not
provides boolean negation-
provides numeric negation
Binary Operators
All binary operators are written in infix notation. In order of precedence, the following classes of operators are available:
Boolean
and
- boolean ANDor
- boolean OR
Boolean Negation
Not a binary operator, but listed here to indicate precedence; see above.
Comparative and Membership
==
- equals!=
- not-equals<
- less-than; works on numbers as well as strings>
- greater-than; dito<=
- less-than-or-equal; dito>=
- greater-than-or-equal; ditoin
- membership test
Test
Not a real operator, but listed here to indicate precendence. See below for details on tests.
Concatenation
~
- String concatenation. If non-string arguments are given, they are converted to string. However, if either argument is encoded text, then the other argument will also be encoded first.
Additive
These operations are numeric. If both arguments are integers, integer operations will be used, otherwise, both arguments will be converted to float.
+
- Numeric addition.-
- Numeric subtraction.
Multiplicative
These operations are numeric. If both arguments are integers, integer operations will be used, otherwise, both arguments will be converted to float, unless specified otherwise.
*
- Numeric multiplication./
- Numeric division.%
- Integer modulus. Both arguments will be converted to int.//
- Integer division. Both arguments will be converted to int.
Power
Numeric operation; if both arguments are integers, integer arithmetic will be used, otherwise, both arguments will be cast to float.
**
Exponentiation:a ** b
means "a to the power of b".
Member access, filter, call
Not operators, but listed here to indicate precedence. See respective sections for details.
Ternary Operator
The ternary operator consists of the keywords if
and else
, and works
much like in Python:
"foo" if condition else "bar"
...means "evaluate to 'foo' if condition
holds, otherwise, evaluate to
'bar'".
Procedure Calls
Procedure calls are written like in most imperative languages, appending a comma-separated list of arguments between parentheses to the procedure to be called.
To call a procedure foo
with two arguments, 1
and 2
:
foo(1, 2)
Procedures may have optional arguments (which can be left out, using a default value instead), and arguments can also be given by name instead of positionally, e.g.:
foo(bar=23)
This calls procedure foo
with the bar
argument set to 23
, and any other
arguments left at their defaults.
Some objects can be called as procedures while also offering other APIs; an
exampe is the loop
object that is available within recursive for
loops
(see below), which exposes a number of fields providing information about
the state of the iteration, but can also be called as a procedure to recurse
into a deeper iteration level.
Filters
Filter syntax is a convenient syntactic alternative to procedure call syntax. It looks like this:
foo|center(10)
This will call the center
filter, passing the value of foo
as the first
argument, and 10
as a second argument. Thus, it is equivalent to:
center(foo, 10)
If no additional arguments are passed, the parentheses can be omitted:
foo|capitalize
This syntax is particularly useful when chaining multiple filters, e.g.:
foo|default('n/a')|lower|trim
One filter, default
, and its alias d
, cannot be implemented as
procedures, because it must inspect its argument as an unevaluated
expression - evaluating it before passing it to the filter would cause a
"Not In Scope" error when the argument isn't defined, making the entire
filter moot. Hence, this filter is only available through filter syntax, not
as a procedure.
Tests
Tests are written using the is
keyword, much like a binary operator;
however, they are special-cased in the language, for two reasons:
* They can inspect their argument unevaluated (e.g., the defined
test will
do this to determine whether the argument is in scope).
* Some tests have the same name as a procedure or filter, but their
functionality is different in a text context. E.g., lower
, when used as
a filter of procedure will convert its argument to lowercase, but when
used as a test, it will instead check whether the argument is lowercase.
In Jinja, tests may only occur in contexts where a boolean condition is
expected (e.g., the ternary operator, or an {% if ... %}
statement);
Ginger allows tests in any expression context, and treats the result as a
boolean value. For example, the following would be perfectly fine in Ginger,
but (probably) not work in Jinja:
{% set answers = { false: "odd", true: "even" } %} Foo is {{ answers[foo is even] }}.
Flow Control Statements
All statements are delimited using statement brackets: {%
... %}
.
{% filter %}
Apply a filter (see above, filter expressions) to a block of template code.
{% filter 'capitalize' %} Hello, world! {% endfilter %}
...will output:
HELLO, WORLD!
The filter itself may be specified as an expression (e.g. {% filter
capitalize %}
), or as a string that will be resolved as a variable (e.g.
{%filter
. Both are equivalent.capitalize
%}
Additional arguments may be passed just like with filter expression syntax:
{% filter center(100) %} Hello, world! {% endfilter %}
{% for %}
Loops over a collection (list or dictionary).
In its simplest form, it looks like this:
{% for user in users %} {{ user }} {% endfor %}
This will iterate over the elements of a list in the variable users
,
binding the current element to the variable user
within the scope of the
iteration body.
Inside of a for-loop block, you can access some special variables:
loop.index
- The current iteration of the loop. (1 indexed)loop.index0
- The current iteration of the loop. (0 indexed)loop.revindex
- The number of iterations from the end of the loop (1 indexed)loop.revindex0
- The number of iterations from the end of the loop (0 indexed)loop.first
- True if first iteration.loop.last
- True if last iteration.loop.length
- The number of items in the sequence.loop.cycle
- A helper function to cycle between a list of sequences. See the explanation below.loop.depth
- Indicates how deep in a recursive loop the rendering currently is. Starts at level 1loop.depth0
- Indicates how deep in a recursive loop the rendering currently is. Starts at level 0loop.previtem
- The item from the previous iteration of the loop. Undefined during the first iteration.loop.nextitem
- The item from the following iteration of the loop. Undefined during the last iteration.loop.changed(val)
- True if previously called with a different value (or not called at all).
While there are no continue
or break
statements to alter the flow of
a loop from within, it is possible to filter the iteree, by adding an if
construct to the loop header:
{% for user in users if user.username is not none %} {{ user.username }} {% endfor %}
The same effect can be achieved by wrapping the loop body in an if
statement; however, filtering the loop iteree has the advantage that the
loop
variables will count correctly.
E.g., given a list of users like so:
[ { "username": "tdammers" }, { "username": none }, { "username": "jdoe" } ]
This template will work correctly:
{% for user in users if user.username is not none %} {{ loop.index }}. {{ user.username }} {% endfor %}
...outputting:
1. tdammers 2. jdoe
Whereas this template:
{% for user in users %} {% if user.username is not none %} {{ loop.index }}. {{ user.username }} {% endif %} {% endfor %}
...would output:
1. tdammers 3. jdoe
An optional else
branch can be added to a loop, which will be used when
the iteration body has not been used at all (because the list was empty, or
because all items were filtered out):
{% for user in users %} <p>{{ user.username }}</p> {% else %} <p class="no-results">No users found.</p> {% endfor %}
Loops can also be used recursively. For this, two things are required:
- The loop needs to be declared as being recursive:
{% for ... in ... recursive %}
- The
loop
variable must be called with an appropriate iteree in order to recurse into it.
Example:
{% for branch in tree recursive %} <section> <h3>{{ branch.name }}</h3> <p>{{ branch.description }}</p> {% if "children" in branch %} {{ loop(branch.children) }} {% endif %} {% endfor %}
Please note that assignments in loops will be cleared at the end of the iteration and cannot outlive the loop scope.
To work around this, consider using namespace objects, created using the
namespace
procedure. However, if all you need to do is check the previous
and/or next item in the iteration, you can simply use loop.prev
and
loop.next
.
{% if %}
Conditionals. These work much like if then else in a typical imperative language. Three forms exist:
Simple if
with no else
:
{% if isGreeting %} Hi! {% endif %}
if
/ else
:
{% if isArriving %} Hello! {% else %} Goodbye! {% endif %}
if
elif
else
:
{% if isMorning %} Good morning! {% elif isEvening %} Good night! {% else %} Good day! {% endif %}
{% set %}
Assignment. There are two forms of the set
statement.
First form: assign an expression.
{% set name = "tdammers" %}
Second form: assign a block of encoded output.
{% set name %} tdammers {% endset %}
Variables set using either construct will be available within the current
scope; includes, imports, template inheritance, the {% with %}
statement,
and {% for %}
loops can affect scope.
{% with %}
Creates a nested scope. Any variables set or overwritten within the nested scope will only be reflected inside the nested scope; any variables from the containing scope will be available until overridden, and will revert back to their previous values when leaving the inner scope.
{% set foo = "A" %} {{ foo }} {% with %} {{ foo }} {% set foo = "B" %} {{ foo }} {% endwith %} {{ foo }}
Will yield:
A A B A
{% macro %}
Defines a macro. Macros are reusable bits of Jinja code, akin to procedures.
In fact, macros and procedures are represented as the same thing internally
in Ginger, and you can call procedures as macros (using the {% call %}
statement), and macros as procedures or filters (using expression-level
procedure call or filter syntax).
A macro definition looks like this:
{% macro userInfo(user, extraInfo=none) %} <section class="userinfo"> <h3>{{ user.username }}</h3> <p>Role: {{ user.role }}</p> <p>Status: {% if user.active %}active{% else %}inactive{% endif %} {% if extraInfo is not none %} {{ extraInfo }} {% endif %} </section> {% endmacro %}
Macros can be declared to take any number of arguments, the values of which will be bound to variables in the scope of the macro's body. Defaults can be given for each argument, making it optional; it is considered good practice to list required arguments (without defaults) before optional arguments, so that there is no ambiguity when arguments are given positionally (i.e., without explicit argument names).
Inside a macro body, a special variable, caller
, is made available if the
macro was invoked through a {% call %}
statement. In that case, caller
will be a procedure that outputs the body of the {% call %}
statement that
was used to invoke the macro.
{% call %}
Calls a macro (see above) with an additional body, which is passed through
the magic caller()
procedure.
Given this macro definition:
{% macro sectionize(title) %} <section> <h1>{{ title }}</h1> {{ caller() }} </section> {% endmacro %}
...the following will call that macro with a caller()
body:
{% call sectionize("Some Section") %} Lorem ipsum dolor sit amet. {% endcall %}
This will render as:
<section> <h1>Some Section</h1> Lorem ipsum dolor sit amet. </section>
{% include %}
{% include "foo.html" %}
This will load the template "foo.html", and insert its output at this position, as if the template source were pasted in.
By default, this implies that the included template will have access to the
scope of the location where it is included; you can change this by adding
without context
to the include statement:
{% include "foo.html" without context %}
It is also valid to write with context
, but since that is the default,
this will do nothing.
Missing templates are an error; if you want to be lenient, you can add
ignore missing
to the include, which ignore such errors. If both are given,
ignore missing
must come before with
/ without context
.
{% include "foo.html" ignore missing without context %}
{% import %}
Works much like include
, however, there are some key differences:
import
will not inject any output from the imported template. The imported template will still be evaluated in full, and any macros and top-level variables it defines (using{% macro %}
and{% set %}
) will be exported, but any output it generates will be discarded.import
defaults towithout context
, i.e., it does not have access to the scope in which the import statement appears.- Because the main purpose of
import
is to pull top-level definitions into the importing scope,import
supports syntax flavors that import specific exports (macros / variables) selectively, as well as one that binds the exports to a single variable acting as a quasi-namespace.
To import a template's exports wholesale:
{% import "util.html" %}
To bind an entire template's export to a quasi-namespace dictionary:
{% import "util.html" as util %}
To import specific exports selectively:
{% from "util.html" import abbreviateUsername, decorateUser %}
To import specific exports, renaming them to aliases:
{% from "util.html" import abbreviateUsername as abbrev, decorateUser as deco %}
{% extends %}
Used for template inheritance:
{% extends "parent.html" %}
Indicates that the current template "extends" the template
"parent.html". To render the current template, Ginger will take any blocks
(see below under "{% block %}
") from the current template, and inject
them into the corresponding blocks of the parent template.
Child templates should not directly output anything themselves; they should
only override blocks ({% block %}
) and top-level variables ({% set %}).
{% block %}
Blocks are overridable subsections of a template. The {% block %}
statement is used both to define a block and to override it: the first
template in an inheritance chain to use a block name defines it, and
determines where it appears in the output; subsequent templates in the
chain can override its contents, but not where it appears in the output.
Maybe the most common use case for this is to have a "skeleton" template that defines the overall layout of a web page, and a number of child templates that override those parts that are specific to their respective use cases.
Example:
(skeleton.html
)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block "title" %}Unnamed Page{% endblock %}</title> <link rel="stylesheet" href="/static/style.css"> </head> <body> {% include "mainnav.html" %} <div class="maincontent"> {% block content %} Nothing to see here. {% endblock } </div> </body> </html>
(userlist.html
)
{% extends "skeleton.html" %} {% block "title" %}Users{% endblock %} {% block "content" %} <h1>Users</h1> <ul> {% for user in users %} <li>{{ user.username }}</li> {% endfor %} </ul> {% endblock %}
List Of Builtin Globals
These are available in Jinja, and work (mostly) the same in Ginger.
abs
abs(value : number) → number
Arguments
value
Absolute of a number.
attr
attr(value : dict, attrName : string) → any
Arguments
value
attrName
Get a named attribute from a dict
or dict-like object.
Unlike []
or dot member access, this will only look at attributes, not items.
batch
batch(value : any, linecount : int, fill_with=none : any) → list
Arguments
value
linecount
- Number of items per chunk. Unlimited if not specified.
fill_with
- Filler to pad shorter chunks with. If not specified, don't pad.
Split up a list into chunks of length linecount
.
capitalize
capitalize(value : string) → string
Arguments
value
Convert value
to title case.
center
center(value : string, width=80 : int, fillchar=" " : string) → string
Arguments
value
width
fillchar
Pad string on both sides to center it in the given space
count
count(value : [string | list | dict]) → int
Alias for length
dictsort
dictsort(value : dict, case_sensitive=false : bool, by="key" : string, reverse=false : bool) → list
Arguments
value
case_sensitive
by
- One of
key
,value
reverse
Sort a dict, returning a list of key-value pairs.
e
e(value : any) → encoded
Alias for escape
escape
escape(value : any) → encoded
Arguments
value
Escape the argument.
even
even(value : int) → bool
Arguments
value
Check if value
is an even number
filesizeformat
filesizeformat(value : number, binary=false : bool) → string
Arguments
value
binary
- If set, use binary units (kiB, MiB, ...) instead of decimal (kB, MB, ...)
Format value
as a human-readable file size.
first
first(value : [list | string | bytes]) → any
Arguments
value
Get the first element from a list, or the first character from a string.
float
float(value : any, default=0.0 : any) → float
Arguments
value
default
Convert value
to float.
If default
is given, values that cannot be converted to floats will be replaced with this default value.
groupby
groupby(value : any, attribute : [string | int], default=none : any, case_sensitive=true : bool) → dict
Arguments
value
attribute
- Attribute to group by.
default
- Default value to use if an object doesn't have the requested attribute.
case_sensitive
- Use case-sensitive comparisons for grouping objects.
Group a list of objects by an attribute.
The attribute can use dot notation for nested access, e.g. 'address.city'
.
int
int(value : any, default=0 : any, base=10 : any) → int
Arguments
value
default
base
Convert value
to int.
If default
is given, values that cannot be converted to integers will be replaced with this default value.
items
items(value : dict) → list
Arguments
value
Convert a dict to a list of its elements without the keys.
join
join(iterable : list, d="" : string, attr=none : any) → string
Arguments
iterable
d
- Default value to use to replace empty elements
attr
- If given, an attribute to pick from each element
Join an iterable into a string
json
json(value : any, indent=none : any) → string
Alias for tojson
last
last(value : [list | string | bytes]) → any
Arguments
value
Get the last element from a list, or the last character from a string.
length
length(value : [string | list | dict]) → int
Arguments
value
Get the length of a string, list, or dictionary.
list
list(value : any) → list
Arguments
value
Convert value
to a list, if possible
lower
lower(value : string) → string
Arguments
value
Convert value
to lowercase.
map
map() → list
Alias for builtins:map
max
max(value : list, case_sensitive=false : bool, attr=none : string) → any
Arguments
value
case_sensitive
- Treat upper and lowercase strings as distinct.
attr
- Get the object with the max value of this attribute.
Get the maximum value from a list
min
min(value : list, case_sensitive=false : bool, attr=none : string) → any
Arguments
value
case_sensitive
- Treat upper and lowercase strings as distinct.
attr
- Get the object with the min value of this attribute.
Get the minimum value from a list
namespace
namespace() → namespace
Create a namespace object.
Namespace objects are mutable dictionary-like objects; the main use case for these is to work around the fact that {% for %}
loops, macros, and other constructs establish local scopes, which means that any {% set %}
invocations inside those will not propagate to the containing scope.
Using a namespace object, this problem can be solved like in this example:
{% set ns = namespace() %} {% for x in items %} {{ x.bar }} {% set ns.foo = x.foo %} {% endfor %} {{ ns.foo }}
odd
odd(value : int) → bool
Arguments
value
Checks if value
is an odd number.
random
random(value : list) → any
Arguments
value
Pick a random element from a list
reject
reject(value : list, filter=none : [string | filter | test | procedure], attribute=none : [string | none]) → list
Arguments
value
filter
- A filter or test to apply to each element to determine whether to reject it or not.
attribute
- If specified, the name of an attribute to extract from each element for testing. This argument can only be passed by keyword, not positionally.
Reject by a test or filter, and/or an attribute.
replace
replace(value : string, old : string, new : string, count=none : any) → string
Arguments
value
old
- String to search for
new
- Replacement
count
- Maximum number of replacements.
String search-and-replace.
reverse
reverse(value : [list | string]) → [list | string]
Arguments
value
Reverse a list or string
round
round(value : float, precision=0 : int, method="common" : string) → [int | float]
Arguments
value
precision
method
- One of
common
,ceil
,floor
.
Round a floating-point value.
safe
safe(value : string) → encoded
Arguments
value
Mark value
as pre-encoded HTML.
select
select(value : list, filter=none : [string | filter | test | procedure], attribute=none : [string | none]) → list
Arguments
value
filter
- A filter or test to apply to each element to determine whether to select it or not.
attribute
- If specified, the name of an attribute to extract from each element for testing. This argument can only be passed by keyword, not positionally.
Select by a test or filter, and/or an attribute.
sort
sort(value : list, reverse=false : bool, case_sensitive=false : bool, attribute=none : any) → list
Arguments
value
reverse
case_sensitive
attribute
split
split(value : string, sep=none : string, maxsplit=none : int) → list
Arguments
value
sep
maxsplit
- Maximum number of splits. Unlimited if not specified.
Split a string by a separator.
string
string(value : any) → string
Arguments
value
Convert argument to string
sum
sum(value : list, attr=none : string, start=none : int) → any
Arguments
value
attr
- Use this attribute from each object in the list
start
- Start at this offset into the list
Get the sum of the values in a list
title
title(value : string) → string
Arguments
value
Convert value
to title case.
tojson
tojson(value : any, indent=none : any) → string
Arguments
value
indent
Convert value
to JSON
upper
upper(value : string) → string
Arguments
value
Convert value
to uppercase.
wordcount
wordcount(value : string) → int
Arguments
value
Counts words in value.
List Of Extension Globals
These are not available in Jinja
date
date(date : [string | list], format="%c" : string, tz=none : [string | int | list], locale=none : any) → string
Alias for dateformat
dateformat
dateformat(date : [string | list], format="%c" : string, tz=none : [string | int | list], locale=none : any) → string
Arguments
date
- May be given as a formatted date, or as a list of `[ year, month, day, hours, minutes, seconds, timezone ]`. Partial lists will be padded with appropriate defaults.
format
tz
- Time zone. May be given as a string specifying an offset (±HHMM), an integer offset in minutes, or a list containing 3 elements: offset in minues (int), summer-only (bool), and timezone name (string).
locale
- Select a locale. Not yet implemented, ignored.
Format a date/time value.
Format strings follow the specification found here: Date.Time.Format.formatTime
Accepted input formats:
- %Y-%m-%dT%H:%M:%S%Q%Z
(2025-11-28T23:54:32.1234UTC)
- %Y-%m-%d %H:%M:%S%Q
(2025-11-28 23:54:32.1234UTC)
- %Y-%m-%d %H:%M:%S%Q%z
(2025-11-28 23:54:32.1234+0100)
- %Y-%m-%d %H:%M:%S%Q%Z
(2025-11-28 23:54:32.1234UTC)
- %Y-%m-%d
(2025-11-28)
help
help(value : any) → dict
Arguments
value
Get documentation for the given value, if available.
Module 'regex'
regex.match
match(regex : any, haystack : any, opts="" : any) → list
Arguments
regex
haystack
opts
Match a regular expression against a string. Returns an array where the first element is the entire match, and subsequent elements are matches on subexpressions (capture groups).
regex.matches
matches(regex : any, haystack : any, opts="" : any) → list
Arguments
regex
haystack
opts
Match a regular expression against a string. Returns an array of matches, where each match is an array where the first element is the entire match, and subsequent elements are matches on subexpressions (capture groups).
regex.test
test(regex : any, haystack : any, opts="" : any) → bool
Arguments
regex
haystack
opts
Match a regular expression against a string. Returns true if at least one match exists, false otherwise.
strip
strip(value : string, chars=none : string) → string
Arguments
value
chars
- If specified: characters to strip.
Strip whitespace or selected characters from both ends of a string.
List Of Builtin Attributes
Bool
bool.bit_count
bit_count() → int
Bit count (popcount). Counts the number of set bits. Since a boolean only has one bit, this will always be either 0 or 1.
bool.denominator
bool.imag
bool.numerator
bool.real
bool.to_bytes
Int
int.bit_count
bit_count() → int
Bit count (popcount). Counts the number of set bits in an integer.
int.denominator
int.imag
int.numerator
int.real
Float
float.imag
float.real
String
string.capitalize
capitalize() → string
Convert value
to title case.
string.casefold
casefold() → string
Convert value
to canonical case for case-insensitive comparison
string.center
center(value : string, width=80 : int, fillchar=" " : string) → string
Arguments
value
width
fillchar
Pad string on both sides to center it in the given space
string.count
count(value : string, sub : string, start=0 : int, end=none : int) → int
Arguments
value
sub
- Substring to search for
start
end
Count the number of occurrences of a substring.
string.encode
encode(value : string, encoding="utf-8" : string, errors="strict" : any) → encoded
Arguments
value
encoding
- Encoding. One of
ascii
,utf8
(default),utf16le
,utf16be
,utf32le
,utf32be
errors
Encode string into the selected encoding.
string.endswith
endswith(value : string, suffix : string, start=0 : int, end=none : int) → bool
Arguments
value
suffix
start
end
Check whether a string ends with a given suffix.
string.isalnum
isalnum() → bool
Check whether a string is alpha-numeric (a letter or a digit).
string.isalpha
isalpha() → bool
Check whether a string is alphabetic (consists solely of letters).
string.isascii
isascii() → bool
Check whether a string consists solely of 7-bit ASCII characters.
string.isdecimal
isdecimal() → bool
Check whether a string is a decimal number
string.isdigit
isdigit() → bool
Check whether a string consists solely of digits.
string.islower
islower() → bool
Check whether a string is all-lowercase
string.isprintable
isprintable() → bool
Check whether a string contains only printable characters.
string.isspace
isspace() → bool
Check whether a string contains only whitespace.
string.isupper
isupper() → bool
Check whether a string is all-uppercase.
string.join
join(value : string, iterable=[] : list) → string
Arguments
value
iterable
str.join(iterable)
joins iterable
into a string, using str
as a separator.
string.length
string.lower
lower() → string
Convert value
to lowercase.
string.lstrip
lstrip(value : any, chars=none : any) → string
Arguments
value
chars
- If specified: characters to strip.
Strip whitespace or selected characters from the beginning of a string.
string.replace
replace(value : string, old : string, new : string, count=none : any) → string
Arguments
value
old
- String to search for
new
- Replacement
count
- Maximum number of replacements.
String search-and-replace.
string.rstrip
rstrip(value : any, chars=none : any) → string
Arguments
value
chars
- If specified: characters to strip.
Strip whitespace or selected characters from the end of a string.
string.split
split(value : string, sep=none : string, maxsplit=none : int) → list
Arguments
value
sep
maxsplit
- Maximum number of splits. Unlimited if not specified.
Split a string by a separator.
string.splitlines
splitlines() → string
Split a string into lines.
string.startswith
startswith(value : string, prefix : string, start=0 : int, end=none : int) → bool
Arguments
value
prefix
start
end
Check whether a string starts with a given prefix.
string.strip
strip(value : string, chars=none : string) → string
Arguments
value
chars
- If specified: characters to strip.
Strip whitespace or selected characters from both ends of a string.
string.title
title() → string
Convert value
to title case.
string.upper
upper() → string
Convert value
to uppercase.
List
Dict
dict.get
get(value : dict, key : scalar, default=none : any) → any
Arguments
value
key
default
Get an item from a dictionary.
dict.items
items() → list
Get a list of key/value pairs from dictionary value
as a list
dict.keys
keys() → list
Get a list of all keys in dict value
dict.values
values() → list
Extract the values from dictionary value
as a list
List Of Builtin Filters
These will only work in a filter context, not via procedure call syntax.
d
d(value : any, default : any) → any
Alias for default
default
default(value : any, default : any) → any
Arguments
value
default
Return default
if value
is false
, none
, or undefined, value
otherwise.
List Of Builtin Tests
These will only work in a test context (e.g., an is
-expression).
Some of these tests shadow globals of the same name but different functionality.
boolean
boolean(value : any) → bool
Arguments
value
Test whether value
is a boolean.
callable
callable(value : any) → bool
Arguments
value
Test whether value
is callable.
defined
defined(value : any) → bool
Arguments
value
Test whether a variable is defined.
eq
eq(value : any) → bool
Arguments
value
Test whether value
is a eq.
escaped
false
false(value : any) → bool
Arguments
value
Test whether value
is boolean false
filter
filter(value : any) → bool
Arguments
value
Test whether value
is a filter.
float
float(value : any) → bool
Arguments
value
Test whether value
is a float.
ge
ge(expr : any, arg : any) → any
Alias for [ >= ](#tests_ >= )
gt
gt(expr : any, arg : any) → any
Alias for [ > ](#tests_ > )
in
in(expr : any, arg : any) → any
Alias for [ in ](#tests_ in )
integer
integer() → bool
Test whether value
is an integer.
iterable
iterable(value : any) → bool
Arguments
value
Test whether value
is iterable.
Lists and list-like native objects are iterable.
le
le(expr : any, arg : any) → any
Alias for [ <= ](#tests_ <= )
lower
lower(value : any) → bool
Arguments
value
Test whether value
is an all-lowercase string
lt
lt(expr : any, arg : any) → any
Alias for [ < ](#tests_ < )
mapping
mapping(value : any) → bool
Arguments
value
Test whether value
is a mapping.
Mappings are dicts and dict-like native objects.
none
none(value : any) → bool
Arguments
value
Test whether value
is the none
value
number
number(value : any) → bool
Arguments
value
Test whether value
is a number (integer or float).
sameas
sequence
sequence(value : any) → bool
Arguments
value
Test whether value
is a sequence (i.e., a list).
string
string(value : any) → bool
Arguments
value
Test whether value
is a string.
test
test(value : any) → bool
Arguments
value
Test whether value
is a test.
true
true(value : any) → bool
Arguments
value
Test whether value
is boolean true
undefined
undefined(value : any) → bool
Alias for defined
upper
upper(value : any) → bool
Arguments
value
Test whether value
is an all-uppercase string.
Synopsis
- ginger :: (Monad m, SplitGen g) => TemplateLoader m -> POptions -> JinjaDialect -> g -> Encoder m -> Text -> Map Identifier (Value m) -> m (Either RuntimeError Encoded)
- data GingerT (m :: Type -> Type) a
- class Eval (m :: Type -> Type) a where
- data RuntimeError
- data Context (m :: Type -> Type) = Context {
- contextEncode :: Encoder m
- contextLoadTemplateFile :: TemplateLoader m
- contextVars :: !(Map Identifier (Value m))
- contextOutput :: !OutputPolicy
- defContext :: forall (m :: Type -> Type). Monad m => Context m
- data Env (m :: Type -> Type) = Env {
- envVars :: !(Map Identifier (Value m))
- envRootMay :: Maybe (Env m)
- emptyEnv :: forall (m :: Type -> Type). Env m
- defEnv :: forall (m :: Type -> Type). Monad m => Env m
- defVars :: forall (m :: Type -> Type). Monad m => Map Identifier (Value m)
- defVarsCompat :: forall (m :: Type -> Type). Monad m => Map Identifier (Value m)
- data Statement
- = PositionedS !SourcePosition !Statement
- | ImmediateS !Encoded
- | InterpolationS !Expr
- | CommentS !Text
- | ForS !(Maybe Identifier) !Identifier !Expr !(Maybe Expr) !Recursivity !Statement !(Maybe Statement)
- | IfS !Expr !Statement !(Maybe Statement)
- | MacroS !Identifier ![MacroArg] !Statement
- | CallS !Identifier ![Expr] ![(Identifier, Expr)] !Statement
- | FilterS !Identifier ![Expr] ![(Identifier, Expr)] !Statement
- | SetS !SetTarget !Expr
- | SetBlockS !SetTarget !Statement !(Maybe Expr)
- | IncludeS !Expr !IncludeMissingPolicy !IncludeContextPolicy
- | ImportS !Expr !(Maybe Identifier) !(Maybe [(Identifier, Maybe Identifier)]) !IncludeMissingPolicy !IncludeContextPolicy
- | BlockS !Identifier !Block
- | WithS ![(Identifier, Expr)] !Statement
- | GroupS ![Statement]
- data Expr
- = PositionedE !SourcePosition !Expr
- | NoneE
- | BoolE !Bool
- | StringLitE !Text
- | IntLitE !Integer
- | FloatLitE !Double
- | StatementE !Statement
- | ListE !(Vector Expr)
- | DictE ![(Expr, Expr)]
- | UnaryE !UnaryOperator !Expr
- | BinaryE !BinaryOperator !Expr !Expr
- | SliceE !Expr !(Maybe Expr) !(Maybe Expr)
- | DotE !Expr !Identifier
- | IsE !Expr !Expr ![Expr] ![(Identifier, Expr)]
- | CallE !Expr ![Expr] ![(Identifier, Expr)]
- | FilterE !Expr !Expr ![Expr] ![(Identifier, Expr)]
- | TernaryE !Expr !Expr !Expr
- | VarE !Identifier
- data Template = Template {
- templateParent :: !(Maybe Text)
- templateBody :: !Statement
- data Block = Block {
- blockBody :: !Statement
- blockScoped :: !Scoped
- blockRequired :: !Required
- data Value (m :: Type -> Type)
- data Scalar
- newtype Encoded = Encoded {}
- prettyRuntimeError :: RuntimeError -> String
- newtype Identifier = Identifier {}
- type Encoder (m :: Type -> Type) = Text -> m Encoded
- htmlEncoder :: Monad m => Encoder m
- data JinjaDialect
- data POptions = POptions {}
- defPOptions :: POptions
- data BlockTrimming
- data BlockStripping
- type TemplateLoader (m :: Type -> Type) = Text -> m (Maybe Text)
- fileLoader :: MonadIO m => FilePath -> TemplateLoader m
Interpreting Templates
Arguments
:: (Monad m, SplitGen g) | |
=> TemplateLoader m | Template loader to use for loading the initial template and
any included templates. For most use cases, |
-> POptions | Parser options, determining parser behavior. |
-> JinjaDialect | Jinja dialect; currently determines which built-in globals to load into the initial namespace. |
-> g | |
-> Encoder m | Encoder to use for automatic encoding. Use |
-> Text | Name of the initial template to load. For the |
-> Map Identifier (Value m) | Variables defined in the initial namespace. |
-> m (Either RuntimeError Encoded) |
One-stop function for parsing and interpreting a template.
data GingerT (m :: Type -> Type) a Source #
The Ginger interpreter monad. Provides error reporting / handling via
MonadError
, an execution context (Context
), and an evaluation state
(EvalState
).
Instances
MonadTrans GingerT Source # | |
Defined in Language.Ginger.Interpret.Type | |
Monad m => MonadError RuntimeError (GingerT m) Source # | |
Defined in Language.Ginger.Interpret.Type Methods throwError :: RuntimeError -> GingerT m a # catchError :: GingerT m a -> (RuntimeError -> GingerT m a) -> GingerT m a # | |
Monad m => Applicative (GingerT m) Source # | |
Defined in Language.Ginger.Interpret.Type | |
Functor m => Functor (GingerT m) Source # | |
Monad m => Monad (GingerT m) Source # | |
Monad m => MonadReader (Context m) (GingerT m) Source # | |
Monad m => MonadState (EvalState m) (GingerT m) Source # | |
class Eval (m :: Type -> Type) a where Source #
Eval
represents types that can be evaluated in some 'GingerT m' monadic
context.
data RuntimeError Source #
Constructors
ArgumentError | |
TagError | |
NonCallableObjectError Text | Object that was attempted to be used as a callable |
NotInScopeError Text | Identifier |
NotImplementedError Text | The thing that isn't implemented |
NumericError | |
TemplateFileNotFoundError Text | Template name |
TemplateParseError | |
FatalError Text | |
PositionedError !SourcePosition !RuntimeError |
Instances
data Context (m :: Type -> Type) Source #
Constructors
Context | |
Fields
|
data Env (m :: Type -> Type) Source #
Constructors
Env | |
Fields
|
defVarsCompat :: forall (m :: Type -> Type). Monad m => Map Identifier (Value m) Source #
AST
A statement in the template language.
Constructors
PositionedS !SourcePosition !Statement | Statement tagged with a source position |
ImmediateS !Encoded | Bare text written in the template, outside of any curly braces |
InterpolationS !Expr | An expression interpolation: |
CommentS !Text | Comment: |
ForS !(Maybe Identifier) !Identifier !Expr !(Maybe Expr) !Recursivity !Statement !(Maybe Statement) | @@ |
IfS !Expr !Statement !(Maybe Statement) | {% if condition %}yes branch{% else %}no branch{% endif %} |
MacroS !Identifier ![MacroArg] !Statement | {% macro name(args) %}body{% endmacro %} |
CallS !Identifier ![Expr] ![(Identifier, Expr)] !Statement | {% call macroName(args) %}body{% endcall %} |
FilterS !Identifier ![Expr] ![(Identifier, Expr)] !Statement | {% filter filterName(args, kwargs) %}body{% endfilter %} |
SetS !SetTarget !Expr | {% set name=expr %} |
SetBlockS !SetTarget !Statement !(Maybe Expr) | {% set name %}body{% endset %} |
IncludeS !Expr !IncludeMissingPolicy !IncludeContextPolicy | {% include includee ignore missing with context %} |
ImportS !Expr !(Maybe Identifier) !(Maybe [(Identifier, Maybe Identifier)]) !IncludeMissingPolicy !IncludeContextPolicy | {% import importee as localName item, other_item as other ignore missing with context %} |
BlockS !Identifier !Block | {% block name with scope required %}body{% endblock %} |
WithS ![(Identifier, Expr)] !Statement | {% with defs %}body{% endwith %} |
GroupS ![Statement] | Group of statements; not parsed, but needed for combining statements sequentially. |
Instances
Arbitrary Statement Source # | |
Show Statement Source # | |
Eq Statement Source # | |
Ord Statement Source # | |
RenderSyntax Statement Source # | |
Defined in Language.Ginger.Render Methods renderSyntax :: Statement -> Builder Source # | |
Monad m => Eval m Statement Source # | |
An expression. Expressions can occur in interpolations ({{ ... }}
), and
in various places inside statements.
Constructors
PositionedE !SourcePosition !Expr | |
NoneE | |
BoolE !Bool | |
StringLitE !Text | |
IntLitE !Integer | |
FloatLitE !Double | |
StatementE !Statement | |
ListE !(Vector Expr) | |
DictE ![(Expr, Expr)] | |
UnaryE !UnaryOperator !Expr | @UnaryE op rhs |
BinaryE !BinaryOperator !Expr !Expr | @BinaryE op lhs rhs |
SliceE !Expr !(Maybe Expr) !(Maybe Expr) | @SliceE slicee start length |
DotE !Expr !Identifier | @DotE lhs rhs |
IsE !Expr !Expr ![Expr] ![(Identifier, Expr)] | IsE scrutinee test args kwargs |
CallE !Expr ![Expr] ![(Identifier, Expr)] | CallE callee args kwargs |
FilterE !Expr !Expr ![Expr] ![(Identifier, Expr)] | FilterE arg0 filter args kwargs |
TernaryE !Expr !Expr !Expr | TernaryE cond yes no |
VarE !Identifier |
A template consists of an optional parent template (specified in the
source using the {% extends %}
construct), and a body statement.
Constructors
Template | |
Fields
|
A block represents a section of a template that can be overridden in derived templates ("template inheritance").
Constructors
Block | |
Fields
|
Representing Values
data Value (m :: Type -> Type) Source #
A value, as using by the interpreter.
Constructors
ScalarV !Scalar | |
ListV !(Vector (Value m)) | |
DictV !(Map Scalar (Value m)) | |
NativeV !(NativeObject m) | |
ProcedureV !(Procedure m) | |
TestV !(Test m) | |
FilterV !(Filter m) | |
MutableRefV !RefID |
Instances
Constructors
NoneScalar | |
BoolScalar !Bool | |
StringScalar !Text | |
EncodedScalar !Encoded | |
BytesScalar !ByteString | |
IntScalar !Integer | |
FloatScalar !Double |
Instances
Arbitrary Scalar Source # | |
FromJSON Scalar Source # | |
Defined in Language.Ginger.Value | |
FromJSONKey Scalar Source # | |
Defined in Language.Ginger.Value | |
ToJSON Scalar Source # | |
ToJSONKey Scalar Source # | |
Defined in Language.Ginger.Value | |
IsString Scalar Source # | |
Defined in Language.Ginger.Value Methods fromString :: String -> Scalar # | |
Show Scalar Source # | |
Eq Scalar Source # | |
Ord Scalar Source # | |
ToScalar Scalar Source # | |
Applicative m => FromValue Scalar m Source # | |
Defined in Language.Ginger.Value | |
ToValue Scalar a Source # | |
(Monad m, FromValue a m) => FromValue (Map Scalar a) m Source # | |
Defined in Language.Ginger.Value |
Represents an encoded string value, as opposed to a raw (unencoded) string,
which we represent as a plain Text
.
prettyRuntimeError :: RuntimeError -> String Source #
Pretty-print a RuntimeError
. The output is meant to be useful as a
user-facing error message.
newtype Identifier Source #
Identifiers are used to represent variable names and object fields.
Constructors
Identifier | |
Fields |
Instances
Configuration
htmlEncoder :: Monad m => Encoder m Source #
data JinjaDialect Source #
Constructors
DialectGinger2 | |
DialectJinja2 |
Instances
Parser and Parser Options
Constructors
POptions | |
Fields |
data BlockTrimming Source #
Constructors
NoTrimBlocks | |
TrimBlocks |
Instances
data BlockStripping Source #
Constructors
NoStripBlocks | |
StripBlocks |
Instances
Template Loaders
fileLoader :: MonadIO m => FilePath -> TemplateLoader m Source #