KXStudio Website https://kx.studio/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

spec.txt 14KB

9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. ==================================
  2. Twig Template Engine Specification
  3. ==================================
  4. This specification specifies a simple cross-language template engine for at least
  5. PHP, Python and Ruby.
  6. Purpose
  7. =======
  8. A language independent and simple template engine is useful for applications that
  9. use code which is written in more than one programming language. Good Examples
  10. are portal systems which use a blog written in Ruby, a forum software written in
  11. PHP and a planet system written in Python.
  12. Inspiration
  13. ===========
  14. Twig uses a syntax similar to the Genshi text templates which in turn were
  15. inspired by django which also inspired Jinja (all three of them python template
  16. engines) which inspired the Twig runtime environment.
  17. Undefined Behavior
  18. ==================
  19. To simplify porting the template language to different platforms in a couple of
  20. situations the behavior is undefined. Template authors may never take advantage
  21. of such a situation!
  22. Syntax
  23. ======
  24. I'm too lazy to write down the syntax as BNF diagram but the following snippet
  25. should explain the syntax elements::
  26. <!DOCTYPE HTML>
  27. {# This is a comment #}
  28. <title>{% block title %}Page Title Goes Here{% endblock %}</title>
  29. {% if show_navigation %}
  30. <nav>
  31. <ul>
  32. {% for item in navigation %}
  33. <li><a href="${item.href|e}">$item.caption</a></li>
  34. {% endfor %}
  35. </ul>
  36. </nav>
  37. {% endif %}
  38. <article>{% block body %}{% endblock %}</article>
  39. Comments and Whitespace
  40. -----------------------
  41. Everything between ``{#`` and ``#}`` is ignored by the lexer. Inside blocks and
  42. variable sections the Lexer has to remove whitespace too.
  43. Output Expressions
  44. ------------------
  45. To output expressions two syntaxes exist. Simple variable output or full
  46. expression output::
  47. $this.is.a.variable.output
  48. ${ expression | goes | here }
  49. The former is what we call a variable expression, the second a full expression.
  50. Variable expressions must not contain whitespace, whereas a full expression
  51. must print the output of the full wrapped expression.
  52. Expressions
  53. -----------
  54. Expressions allow basic string manipulation and arithmetic calculations. It is
  55. an infix syntax with the following operators in this precedence:
  56. =========== ==============================================================
  57. Operator Description
  58. =========== ==============================================================
  59. ``+`` Convert both arguments into a number and add them up.
  60. ``-`` Convert both arguments into a number and substract them.
  61. ``*`` Convert both arguments into a number and multiply them.
  62. ``/`` Convert both arguments into a number and divide them.
  63. ``%`` Convert both arguments into a number and calculate the rest
  64. of the integer division.
  65. ``~`` Convert both arguments into a string and concatenate them.
  66. ``or`` True if the left or the right expression is true.
  67. ``and`` True if the left and the right expression is true.
  68. ``not`` negate the expression
  69. =========== ==============================================================
  70. All number conversions have an undefined precision but the implementations
  71. should try to select the best possible type. For example, if the implementation
  72. sees an integer and a float that looks like an integer it may convert the
  73. latter into a long and add them.
  74. Use parentheses to group expressions.
  75. If an object cannot be compared the implementation might raise an error or fail
  76. silently. Template authors may never apply mathematical operators to untrusted
  77. data. This is especially true for the php implementation where the following
  78. outputs ``42``::
  79. ${ "foo41" + 1 }
  80. This is undefined behavior and will break on different implementations or
  81. return ``0`` as ``"foo41"`` is not a valid number.
  82. Types
  83. ~~~~~
  84. The following types exist:
  85. =========== =============== ==============================================
  86. Type Literal Description
  87. =========== =============== ==============================================
  88. ``integer`` `\d+` One of the two numeric types. Which of them
  89. is used and when is up to the implementation.
  90. ``float`` `\d+\.\d+` Floating point values.
  91. ``string`` see below A unicode string. The PHP implementation has
  92. to use bytestrings here and may use mb_string.
  93. ``bool`` `(true|false)` Represents boolean values.
  94. ``none`` `none` This type is returned on missing variables or
  95. attributes.
  96. =========== =============== ==============================================
  97. String regex::
  98. (?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')(?sm)
  99. Attribute Lookup
  100. ~~~~~~~~~~~~~~~~
  101. There are two ways to look up attributes on objects. The dot and the
  102. subscript syntax, both inspired by JavaScript. Basically the following
  103. expressions do the very same::
  104. foo.name.0
  105. foo['name'][0]
  106. This is useful to dynamically get attributes from objects::
  107. foo[bar]
  108. The underlaying implementation is free to specify on it's own what an attribute
  109. lookup means. The PHP reference implementation for example performs these
  110. actions on ``foo.bar``:
  111. - try ``$foo['bar']``
  112. - try ``$foo->bar()`` (if they're using ``foo.bar`` and not ``foo[bar]``)
  113. - try ``$foo->bar``
  114. - try ``$foo->getBar()`` (if they're using ``foo.bar`` and not ``foo[bar]``)
  115. The first match returns the object, attribute access to nonexisting attributes
  116. returns `none`.
  117. Filtering
  118. ~~~~~~~~~
  119. The template language does not specify function calls, but filters can be used
  120. to further modify variables using functions the template engine provides.
  121. The following snippet shows how filters are translated to function calls::
  122. ${ 42 | foo(1, 2) | bar | baz }
  123. -> baz(bar(foo(42, 1, 2)))
  124. The following filters must be provided by the implementation:
  125. =================== ======================================================
  126. Name Description
  127. =================== ======================================================
  128. `date` Format the timestamp using the PHP date formatting
  129. rules. This may sound like a nonstandard way of
  130. formatting dates but it's a way very popular among
  131. template designers and also used by django.
  132. `strftime` Format the timestamp using standard strftime rules.
  133. `numberformat` Apply number formatting on the string. This may or
  134. may not use local specific rules.
  135. `moneyformat` Like `numberformat` but for money.
  136. `filesizeformat` Takes a number of bytes and displays it as KB/MB/GB
  137. `format` Applies `sprintf` formatting on the string::
  138. ${ "%s %2f" | format(string, float) }
  139. `even` Is the number even?
  140. `odd` Is the number odd?
  141. `escape` Apply HTML escaping on a string. This also has to
  142. convert `"` to `&quot; but leave `'` unmodified.
  143. `e` Alias for `escape`.
  144. `urlencode` URL encode the string. If the second parameter is
  145. true this function should encode for path sections,
  146. otherwise for query strings.
  147. `quotes` Escape quotes (\', \", etc.)
  148. `title` Make the string lowercase and upper case the first
  149. characters of all words.
  150. `capitalize` Like `title` but capitalizes only the first char of
  151. the whole string.
  152. `upper` Convert the string to uppercase.
  153. `lower` Convert the string to lowercase.
  154. `strip` Trim leading and trailing whitespace.
  155. `lstrip` Trim leading whitespace.
  156. `rstrip` Trim trailing whitespace.
  157. `translate` Translate the string using either the "theme" domain
  158. or the "chyrp" domain if in Admin. (Chyrp-specific)
  159. `translate_plural` Translate the (singular) string, or the plural string
  160. if the number passed is not 1.
  161. `normalize` Convert all excessive whitespace (including linebreaks)
  162. into a single space.
  163. `truncate` Truncate a string, providing ellipsis, if it is longer
  164. than the passed length. Keeps words in tact by default,
  165. but with a second boolean parameter will be strict.
  166. `replace` Replaces the occurrence of the first argument with the
  167. second argument in the string.
  168. `linebreaks` Convert linebreaks to <br />'s.
  169. `camelize` Convert string to camelcase.
  170. `strip_tags` Strip HTML from the string.
  171. `pluralize` Return the pluralization of a string, or if a number
  172. is passed and it is 1, don't pluralize.
  173. `sanitize` Remove special characters from a string.
  174. `join` Concatenate the array items and join them with the
  175. string provided (or commas by default).
  176. `split` Split a string into an array at the given breakpoints.
  177. `first` First entry of an Array.
  178. `offset` Entry at Array[offset].
  179. `last` Last entry of an Array.
  180. `reverse` Reverse the Array items.
  181. `count` Count the number of items in an array or string
  182. characters.
  183. `length` Alias for `count`.
  184. `default` If the value is `none` the first argument is returned
  185. `keys` Keys of an Array.
  186. `items` Items of an Array.
  187. `inspect` Dumps the variable or value.
  188. `fallback` If the value is empty or `none`, return this value.
  189. `selected` If the first argument is the same as the value, output
  190. `class="selected"`, or `selected` if the second
  191. argument is `true`.
  192. `option_selected` Same as `selected`, but for `selected="selected"`.
  193. `checked` Same as `selected`, but for `checked="checked"`.
  194. =================== ======================================================
  195. Additionally, if a filter is missing (say, ${ foo | bar_filter }, in Chyrp it
  196. checks for an associated Trigger filter by that filter's name.
  197. For Loops
  198. ---------
  199. Iteration works via for loops. Loops work a bit like their Python counterparts,
  200. except that they don't support multilevel tuple unpacking and that they add a new
  201. layer to the context. Thus at the end of the iteration all the modifications on
  202. the context disappear. Additionally, inside loops you have access to a special
  203. `loop` object which provides runtime information:
  204. ====================== ===================================================
  205. Variable Description
  206. ====================== ===================================================
  207. ``loop.index`` The current iteration of the loop (1-indexed)
  208. ``loop.index0`` The current iteration of the loop (0-indexed)
  209. ``loop.revindex`` The number of iterations from the end of the
  210. loop (1-indexed)
  211. ``loop.revindex0`` The number of iterations from the end of the
  212. loop (0-indexed)
  213. ``loop.first`` True if this is the first time through the loop
  214. ``loop.last`` True if this is the last time through the loop
  215. ``loop.parent`` For nested loops, this is the loop "above" the
  216. current one
  217. ====================== ===================================================
  218. Additionally for loops can have an `else` section that is executed if no
  219. iteration took place.
  220. Example
  221. ~~~~~~~
  222. ::
  223. <ul>
  224. {% for user in users %}
  225. <li><a href="$user.href">${ user.username | escape }</a></li>
  226. {% else %}
  227. <li><em>No users found!</em></li>
  228. {% endfor %}
  229. </ul>
  230. Notes on Iteration
  231. ~~~~~~~~~~~~~~~~~~
  232. Because we have to cope with PHP too, which has problematic arrays that are
  233. neither hashmaps nor lists, we have no support for associative array iteration
  234. at all. How do you iterate over associative arrays then? Using a filter::
  235. {% for key, value in array | items %}
  236. ...
  237. {% endfor %}
  238. To iterate over the keys only::
  239. {% for key in array | keys %}
  240. ...
  241. {% endfor %}
  242. If Conditions
  243. -------------
  244. If conditions work like like Ruby, PHP and Python, just that we use PHP
  245. keywords. Also, use `elseif` and not `else if`::
  246. {% if expr1 %}
  247. ...
  248. {% elseif expr2 %}
  249. ...
  250. {% else %}
  251. ...
  252. {% endif %}
  253. Inheritance
  254. -----------
  255. Template inheritance allows you to build a base "skeleton" template that
  256. contains all the common elements of your site and defines **blocks** that
  257. child templates can override.
  258. Here a small template inheritance example::
  259. <!DOCTYPE HTML>
  260. <html lang="en">
  261. <link rel="stylesheet" href="style.css">
  262. <title>{% block title %}My site{% endblock %}</title>
  263. <div id="sidebar">
  264. {% block sidebar %}
  265. <ul>
  266. <li><a href="/">Home</a></li>
  267. <li><a href="/blog/">Blog</a></li>
  268. </ul>
  269. {% endblock %}
  270. </div>
  271. <div id="content">
  272. {% block content %}{% endblock %}
  273. </div>
  274. </html>
  275. If we call that template "base.html" a "index.html" template could override
  276. it and fill in the blocks::
  277. {% extends "base.html" %}
  278. {% block title %}Foo &mdash; {% super %}{% endblock %}
  279. {% block content %}
  280. This is the content
  281. {% endblock %}
  282. By using `{% super %}` you can render the parent's block. The template
  283. filenames must be constant strings (we don't support dynamic inheritance
  284. for simplicity) and are relative to the loader folder, not the current
  285. template.