liquor 0.9.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cabd820710ffda20b3cc967b38f162922615e1f3
4
- data.tar.gz: ce09ef8a23a5eed2e09d47533739428a9ef90ba8
3
+ metadata.gz: f27a409b1a1c3302cf30202dae7528a5754b46b8
4
+ data.tar.gz: c7fafccfb2ed2c21d8c7bc4d31b67212cca01b25
5
5
  SHA512:
6
- metadata.gz: e0faafde65c90d9ba87992878b2235225caf382d674b248c2f1e153fc2deeae61fec888257397e9a426fb2922bc6c69027973fe997103f79a0b1b5229f70e46d
7
- data.tar.gz: b2c97079c8f8ce35049749dcae24dccc4f640a18c12bcd479938ec703c732761bdc4a4ed18c142526d9e7ed6f3c73fab8cf74504a3630d77f93e2e914e9a211b
6
+ metadata.gz: 29b724eabcd218b53b0654dd9622eb28c406620bf533ca0caaa24c2b90202552b15544e6b1849f7aeeb23507423d76f2348d69ef78bf61be0853ac45010666bf
7
+ data.tar.gz: 24fddc2d7991ad6eeca0c25a3764039412b2e2974444b5acd3c171b590c831489afdce649834dec6e955faed85a36119d9741cd18e123ba952a9e6b3cd21b537
data/Guardfile CHANGED
@@ -5,7 +5,10 @@ guard 'rspec' do
5
5
  watch('spec/spec_helper.rb') { "spec" }
6
6
 
7
7
  watch(%r{^lib/liquor/grammar/.+\.(rl|racc)$}) { `rake` }
8
- watch('doc/language-spec.md') { `rake` }
8
+ watch('doc/language-spec.md') do
9
+ `rake doc/language-spec.html`
10
+ Notifier.notify('Documentation was regenerated')
11
+ end
9
12
 
10
13
  notification :libnotify
11
14
  end
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Liquor
2
2
 
3
- TODO: Write a gem description
3
+ Liquor is a safe and extensible templating language that compiles to Ruby.
4
+ It can be thought of as a replacement of [Liquid](https://github.com/Shopify/liquid),
5
+ though it does not provide a migration path.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,7 +20,130 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- TODO: Write usage instructions here
23
+ The language is described in [the specification](http://evilmartians.github.io/liquor/language-spec.html).
24
+
25
+ ### Compiling templates
26
+
27
+ The Liquor templates are rendered in two stages. First, you need to compile them using `Liquor::Manager`. Assuming `templates` is a hash mapping template names to contents and predefined variables, the following code demonstrates the usage of `Liquor::Manager`:
28
+
29
+ ``` ruby
30
+ manager = Liquor::Manager.new
31
+
32
+ templates.each do |name, (body, predefined)|
33
+ if manager.partial? name
34
+ manager.register_partial name, body
35
+ else
36
+ manager.register_template name, body, predefined
37
+ end
38
+ end
39
+
40
+ unless manager.compile
41
+ manager.errors.each do |error|
42
+ source, * = templates[error.location[:file]]
43
+ puts error.decorate(source)
44
+ end
45
+ end
46
+ ```
47
+
48
+ The `error.decorate` call will extract the corresponding line from the source and highlight the character range that caused the error.
49
+
50
+ ### Rendering templates
51
+
52
+ The `context` is a hash that is shared between the layout and the inner template. The `layout_environment` and `inner_environment` contain the list of variables initially provided to corresponding template; it must match the value of `predefined` of the template at compilation time.
53
+
54
+ ``` ruby
55
+ context ||= {}
56
+
57
+ # Production-mode code
58
+ diagnostics = Liquor::Runtime.capture_diagnostics do
59
+ inner = manager.render(template_name, inner_environment, context)
60
+ manager.render(layout_name, layout_environment, context.merge(_inner_template: inner))
61
+ end
62
+
63
+ # Development-mode code
64
+ Liquor::Runtime.with_fatal_deprecations do
65
+ # idem
66
+ end
67
+ ```
68
+
69
+ The difference between development-mode and production-mode code is that in development mode, all runtime errors are raised as `Liquor::Diagnostic` exceptions, and in production mode, they are returned from `Liquor::Runtime.capture_diagnostics` instead.
70
+
71
+ The code to perform decoration of the errors is the same as for compile-time errors.
72
+
73
+ Additionally, both development-mode and production-mode code can raise `Liquor::HostError` at render time, reserved for uncaught Ruby exceptions inside Liquor code.
74
+
75
+ ### Extending Liquor
76
+
77
+ Liquor _functions_ are similar to Ruby functions. They should not have any side effects. To learn how to define your own Liquor functions, see [lib/stdlib/builtin_functions.rb](lib/stdlib/builtin_functions.rb).
78
+
79
+ Liquor _tags_ provide means for inserting arbitrary Ruby code in the compiled output. They can do almost anything, and can have side effects. To learn how to define your own Liquor tags, see [lib/stdlib/builtin_tags.rb](lib/stdlib/builtin_tags.rb).
80
+
81
+ Both functions and tags are organized in _libraries_. A Liquor library looks like this:
82
+
83
+ ``` ruby
84
+ module LiquorFoo
85
+ include Liquor::Library
86
+
87
+ function # ...
88
+
89
+ tag # ...
90
+ end
91
+ ```
92
+
93
+ The libraries should be provided to the `Liquor::Manager` constructor, e.g. `Liquor::Manager.new(import: [LiquorFoo])`.
94
+
95
+ ### Passing Ruby objects to Liquor code
96
+
97
+ Liquor represents primitive Liquor objects (null, booleans, integers, strings and tuples) using the corresponding primitive Ruby objects, so they can be passed as-is. Liquor does not extend core Ruby classes.
98
+
99
+ All other objects must include `Liquor::External` in order to be accessible from Liquor. They need to call `export` on module level to explicitly mark functions as accessible to Liquor. The functions always receive two arguments: the unnamed argument and the hash of keyword arguments, e.g.:
100
+
101
+ ``` ruby
102
+ class FooExternal
103
+ include Liquor::External
104
+
105
+ def meth(arg, kwargs)
106
+ # ...
107
+ end
108
+ export :meth
109
+ ```
110
+
111
+ Additionally, external methods can be deprecated using `deprecate :meth, date: '2014-11-11', message: 'meth is deprecated, use meth1 instead'`. The _date_ (i.e. the intended date of final removal) and _message_ will appear in the message of the emitted `Liquor::Diagnostic`. The diagnostic will only be emitted when the method is actually called.
112
+
113
+ ### Library integrations
114
+
115
+ Liquor contains code to integrate with some popular libraries
116
+
117
+ #### ActiveRecord
118
+
119
+ Liquor contains built-in support for passing ActiveRecord scopes and model instances as externals. To use it, `require 'liquor/dropable'`, then `include Liquor::Dropable` in the model, and then simply pass the scope or model instance to the template.
120
+
121
+ #### Rails
122
+
123
+ Liquor contains a Rails renderer for Liquor templates that supports layouts. To use it `require 'liquor/extensions/rails', then use the following code in your actions:
124
+
125
+ ``` ruby
126
+ render liquor: {
127
+ manager: manager,
128
+ template: template_name,
129
+ layout: layout_name,
130
+ environment: environment,
131
+ }
132
+ ```
133
+
134
+ See the section "Renering templates" for the meaning of the renderer arguments.
135
+
136
+ #### Kaminari
137
+
138
+ To use Kaminari integration, `require 'liquor/extensions/kaminari'`. This will monkey-patch `Kaminari::PaginatableArray` and allow to pass any object paginated by Kaminari to Liquor templates.
139
+
140
+ #### Tire
141
+
142
+ To use Tire integration, `require 'liquor/extensions/tire'`. This will monkey-patch `Tire::Results::Collection` and `Tire::Search::Search` and allow to pass any object generated by Tire to Liquor templates.
143
+
144
+ #### ThinkingSphinx
145
+
146
+ To use ThinkingSphinx integration, `require 'liquor/extensions/thinking_sphinx'`. This will monkey-patch `ThinkingSphinx::Search` and allow to pass any object generated by ThinkingSphinx to Liquor templates.
22
147
 
23
148
  ## Contributing
24
149
 
@@ -27,3 +152,7 @@ TODO: Write usage instructions here
27
152
  3. Commit your changes (`git commit -am 'Added some feature'`)
28
153
  4. Push to the branch (`git push origin my-new-feature`)
29
154
  5. Create new Pull Request
155
+
156
+ ## License
157
+
158
+ Liquor is distributed under the terms of [MIT license](MIT-LICENSE).
data/Rakefile CHANGED
@@ -26,3 +26,16 @@ RSpec::Core::RakeTask.new do |t|
26
26
  t.pattern = FileList['spec/**/*_spec.rb']
27
27
  t.rspec_opts = %w(-fs --color)
28
28
  end
29
+
30
+ desc "Populate github pages from doc/"
31
+ task :gh_pages => ['doc/language-spec.html'] do
32
+ `git clone \`git config --get remote.origin.url\` .gh-pages --reference .`
33
+ `git -C .gh-pages checkout --orphan gh-pages`
34
+ `git -C .gh-pages reset`
35
+ `git -C .gh-pages clean -dxf`
36
+ `cp -t .gh-pages/ doc/language-spec.html`
37
+ `git -C .gh-pages add .`
38
+ `git -C .gh-pages commit -m "Update Pages"`
39
+ `git -C .gh-pages push origin gh-pages -f`
40
+ `rm -rf .gh-pages`
41
+ end
@@ -1,12 +1,12 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
-
5
- <meta http-equiv="Content-type" content="text/html;UTF-8">
6
-
4
+
5
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
6
+
7
7
 
8
8
  <title>Liquor 2.0 Language Specification</title>
9
- <meta name="generator" content="kramdown 1.3.0" />
9
+ <meta name="generator" content="kramdown 1.4.2" />
10
10
  </head>
11
11
  <body>
12
12
  <style>
@@ -112,8 +112,6 @@ dd strong, code {
112
112
 
113
113
  <h1 id="liquor-20-language-specification">Liquor 2.0 Language Specification</h1>
114
114
 
115
- <p style="text-align: right"><em>This version of specification is a working draft.</em></p>
116
-
117
115
  <h2 id="table-of-contents">Table of Contents</h2>
118
116
 
119
117
  <ul id="markdown-toc">
@@ -160,12 +158,20 @@ dd strong, code {
160
158
  <li><a href="#scope-resolution">4.2 Scope Resolution</a></li>
161
159
  </ul>
162
160
  </li>
163
- <li><a href="#runtime-behavior">5 Runtime Behavior</a></li>
161
+ <li><a href="#runtime-behavior">5 Runtime Behavior</a> <ul>
162
+ <li><a href="#type-error">5.1 Type Error</a></li>
163
+ <li><a href="#external-error">5.2 External Error</a></li>
164
+ <li><a href="#the-dummy-external">5.3 The dummy external</a></li>
165
+ </ul>
166
+ </li>
164
167
  <li><a href="#builtins">6 Builtins</a> <ul>
165
168
  <li><a href="#builtin-tags">6.1 Required tags</a> <ul>
166
169
  <li><a href="#declare">6.1.1 declare</a></li>
167
170
  <li><a href="#assign">6.1.2 assign</a></li>
168
- <li><a href="#for">6.1.3 for</a></li>
171
+ <li><a href="#for">6.1.3 for</a> <ul>
172
+ <li><a href="#for-loop-external">6.1.3.1 for loop external</a></li>
173
+ </ul>
174
+ </li>
169
175
  <li><a href="#if">6.1.4 if</a></li>
170
176
  <li><a href="#unless">6.1.5 unless</a></li>
171
177
  <li><a href="#capture">6.1.6 capture</a></li>
@@ -174,10 +180,66 @@ dd strong, code {
174
180
  <li><a href="#include">6.1.9 include</a></li>
175
181
  </ul>
176
182
  </li>
177
- <li><a href="#functions">6.2 Functions</a></li>
183
+ <li><a href="#functions">6.2 Functions</a> <ul>
184
+ <li><a href="#universal-functions">6.2.1 Universal functions</a> <ul>
185
+ <li><a href="#isempty">6.2.1.1 is_empty</a></li>
186
+ <li><a href="#size">6.2.1.2 size</a></li>
187
+ </ul>
188
+ </li>
189
+ <li><a href="#conversion-functions">6.2.2 Conversion functions</a> <ul>
190
+ <li><a href="#strftime">6.2.2.1 strftime</a></li>
191
+ <li><a href="#tonumber">6.2.2.2 to_number</a></li>
192
+ </ul>
193
+ </li>
194
+ <li><a href="#integer-functions">6.2.3 Integer functions</a> <ul>
195
+ <li><a href="#iseven">6.2.3.1 is_even</a></li>
196
+ <li><a href="#isodd">6.2.3.2 is_odd</a></li>
197
+ </ul>
198
+ </li>
199
+ <li><a href="#string-functions">6.2.4 String functions</a> <ul>
200
+ <li><a href="#downcase">6.2.4.1 downcase</a></li>
201
+ <li><a href="#upcase">6.2.4.2 upcase</a></li>
202
+ <li><a href="#capitalize">6.2.4.3 capitalize</a></li>
203
+ <li><a href="#startswith">6.2.4.4 starts_with</a></li>
204
+ <li><a href="#stripnewlines">6.2.4.5 strip_newlines</a></li>
205
+ <li><a href="#join">6.2.4.6 join</a></li>
206
+ <li><a href="#split">6.2.4.7 split</a></li>
207
+ <li><a href="#replace">6.2.4.8 replace</a></li>
208
+ <li><a href="#replacefirst">6.2.4.9 replace_first</a></li>
209
+ <li><a href="#remove">6.2.4.10 remove</a></li>
210
+ <li><a href="#removefirst">6.2.4.11 remove_first</a></li>
211
+ <li><a href="#newlinetobr">6.2.4.12 newline_to_br</a></li>
212
+ <li><a href="#urlescape">6.2.4.13 url_escape</a></li>
213
+ <li><a href="#function-html-escape">6.2.4.14 html_escape</a></li>
214
+ <li><a href="#htmlescapeonce-h">6.2.4.15 html_escape_once, h</a></li>
215
+ <li><a href="#striphtml">6.2.4.16 strip_html</a></li>
216
+ <li><a href="#decodehtmlentities">6.2.4.17 decode_html_entities</a></li>
217
+ </ul>
218
+ </li>
219
+ <li><a href="#tuple-functions">6.2.5 Tuple functions</a> <ul>
220
+ <li><a href="#compact">6.2.5.1 compact</a></li>
221
+ <li><a href="#reverse">6.2.5.2 reverse</a></li>
222
+ <li><a href="#uniq">6.2.5.3 uniq</a></li>
223
+ <li><a href="#function-min">6.2.5.4 min</a></li>
224
+ <li><a href="#max">6.2.5.5 max</a></li>
225
+ <li><a href="#ingroupsof">6.2.5.6 in_groups_of</a></li>
226
+ <li><a href="#ingroups">6.2.5.7 in_groups</a></li>
227
+ <li><a href="#includes">6.2.5.8 includes</a></li>
228
+ <li><a href="#indexof">6.2.5.9 index_of</a></li>
229
+ </ul>
230
+ </li>
231
+ <li><a href="#truncation-functions">6.2.6 Truncation functions</a> <ul>
232
+ <li><a href="#function-truncate">6.2.6.1 truncate</a></li>
233
+ <li><a href="#truncatewords">6.2.6.2 truncate_words</a></li>
234
+ <li><a href="#htmltruncate">6.2.6.3 html_truncate</a></li>
235
+ <li><a href="#htmltruncatewords">6.2.6.4 html_truncate_words</a></li>
236
+ </ul>
237
+ </li>
238
+ </ul>
239
+ </li>
178
240
  </ul>
179
241
  </li>
180
- <li><a href="#appendix-layouts">Appendix A: Layouts</a></li>
242
+ <li><a href="#appendix-layouts">7 Layouts</a></li>
181
243
  </ul>
182
244
  </li>
183
245
  </ul>
@@ -278,9 +340,9 @@ The following operators are infix and unary: <code>-</code>, <code>!</code>.</p>
278
340
 
279
341
  <p>Arithmetic operators are <code>*</code> (multiplication), <code>/</code> (division), <code>%</code> (modulo), <code>+</code> (plus) and <code>-</code> (minus; binary and unary).</p>
280
342
 
281
- <p>All arithmetic operators except <code>+</code>, whether unary or binary, require every argument to be of type <strong>Integer</strong>. If this is not the case, a runtime error condition is signaled.</p>
343
+ <p>All arithmetic operators except <code>+</code>, whether unary or binary, require every argument to be of type <strong>Integer</strong>. If this is not the case, a runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
282
344
 
283
- <p>Operator <code>+</code> requires both arguments to be of the same type, and only accepts arguments of type <strong>Integer</strong>, <strong>String</strong> or <strong>Tuple</strong>. If any of the conditions is not satisfied, a runtime error condition is signaled. For arguments of type <strong>String</strong> or <strong>Tuple</strong>, the <code>+</code> operator evaluates to the concatenation of left and right arguments in that order.</p>
345
+ <p>Operator <code>+</code> requires both arguments to be of the same type, and only accepts arguments of type <strong>Integer</strong>, <strong>String</strong> or <strong>Tuple</strong>. If any of the conditions is not satisfied, a runtime error condition (<a href="#type-error">type error</a>) is signaled. For arguments of type <strong>String</strong> or <strong>Tuple</strong>, the <code>+</code> operator evaluates to the concatenation of left and right arguments in that order.</p>
284
346
 
285
347
  <p>If the result of an arithmetic operation, except operator <code>+</code> with non-<strong>Integer</strong> arguments, exceeds the range an implementation can represent, the behavior is implementation-defined.</p>
286
348
 
@@ -303,15 +365,15 @@ The following operators are infix and unary: <code>-</code>, <code>!</code>.</p>
303
365
 
304
366
  <p>Operators <code>==</code> and <code>!=</code> compare values by equality, not identity. Thus, the expression <code>[ 1, 2 ] == [ 1, 2 ]</code> evluates to <em>true</em>. These operators never signal an error condition or implicitly convert types.</p>
305
367
 
306
- <p>Operators <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code> and <code>&gt;=</code> require both arguments to be of type <strong>Integer</strong>. If this is not the case, a runtime error condition is signaled. Otherwise, a corresponding value of type <strong>Boolean</strong> is returned.</p>
368
+ <p>Operators <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code> and <code>&gt;=</code> require both arguments to be of type <strong>Integer</strong>. If this is not the case, a runtime error condition (<a href="#type-error">type error</a>) is signaled. Otherwise, a corresponding value of type <strong>Boolean</strong> is returned.</p>
307
369
 
308
370
  <h5 id="indexing-operator">2.4.2.4 Indexing Operator</h5>
309
371
 
310
372
  <p>Indexing operator is <code>[]</code>.</p>
311
373
 
312
- <p>Indexing operator requires its left-hand side argument to be of type <strong>Tuple</strong> or <strong>External</strong>, and right-hand side argument to be of type <strong>Integer</strong>. If this is not the case, a runtime error condition is signaled.</p>
374
+ <p>Indexing operator requires its left-hand side argument to be of type <strong>Tuple</strong> or <strong>External</strong>, and right-hand side argument to be of type <strong>Integer</strong>. If this is not the case, a runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
313
375
 
314
- <p>If the left-hand side argument is of type <strong>External</strong>, the behavior is implementation-defined. A runtime error condition can be signaled if the particular external value does not support indexing.</p>
376
+ <p>If the left-hand side argument is of type <strong>External</strong>, the behavior is implementation-defined. A runtime error condition (<a href="#external-error">external error</a>) is signaled if the particular external value does not support indexing.</p>
315
377
 
316
378
  <p>Indexing operator of form <code><em>t</em>[<em>n</em>]</code> evaluates to <em>n</em>-th value from tuple <em>t</em> with zero-based indexing. If <code>n</code> is negative, then <em>n</em>+1-th element from the end of tuple is returned. For example, <code><em>t</em>[-1]</code> will evaluate to the last element of the tuple <em>t</em>.</p>
317
379
 
@@ -335,13 +397,13 @@ The following operators are infix and unary: <code>-</code>, <code>!</code>.</p>
335
397
 
336
398
  <p>The <code>.</code> form is syntactic sugar for <code>.()</code> form without any arguments. That is, <code><em>e</em>.<em>f</em></code> is completely equivalent to <code><em>e</em>.<em>f</em>()</code>.</p>
337
399
 
338
- <p>Access operator requires its left-hand side argument to be of type <strong>External</strong>. If this is not the case, a runtime error condition is signaled.</p>
400
+ <p>Access operator requires its left-hand side argument to be of type <strong>External</strong>. If this is not the case, a runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
339
401
 
340
402
  <p>Access operator of form <code><em>e</em>.<em>f</em>(<em>arg</em> <em>kw:</em> <em>value</em>)</code> evaluates to the result of calling method <em>f</em> of external object <em>e</em> with the corresponding arguments. Argument syntax is the same as for <a href="#function-calls">function calls</a>.</p>
341
403
 
342
404
  <p>This evaluation is done in an implementation-defined way. Access operator can evaluate to any type.</p>
343
405
 
344
- <p>If the requested method does not exist in the external object or cannot successfully evaluate, a runtime error condition is signaled. Errors in the called method must not interrupt execution of the calling Liquor program.</p>
406
+ <p>If the requested method does not exist in the external object or cannot successfully evaluate, a runtime error condition (<a href="#external-error">external error</a>) is signaled. Errors in the called method must not interrupt execution of the calling Liquor program.</p>
345
407
 
346
408
  <h4 id="variable-access">2.4.5 Variable Access</h4>
347
409
 
@@ -378,7 +440,7 @@ In essence, <code><em>e</em> | <em>f</em> a: 1 | <em>g</em></code> is equivalent
378
440
 
379
441
  <h3 id="interpolations">2.6 Interpolations</h3>
380
442
 
381
- <p>An interpolation is a syntactic construct of form <code>{{ expr }}</code> which can be embedded in a block. The expression <code>expr</code> should evaluate to a value of type <strong>String</strong> or <strong>Null</strong>; an <a href="#type-conversion">implicit conversion</a> might take place. If this is not the case, a runtime error condition is signaled.</p>
443
+ <p>An interpolation is a syntactic construct of form <code>{{ expr }}</code> which can be embedded in a block. The expression <code>expr</code> should evaluate to a value of type <strong>String</strong> or <strong>Null</strong>; an <a href="#type-conversion">implicit conversion</a> might take place. If this is not the case, a runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
382
444
 
383
445
  <p>If <em>expr</em> evaluates to a <strong>String</strong>, the interpolation returns it. Otherwise, the interpolation returns an empty string.</p>
384
446
 
@@ -624,7 +686,30 @@ In essence, <code><em>e</em> | <em>f</em> a: 1 | <em>g</em></code> is equivalent
624
686
 
625
687
  <h2 id="runtime-behavior">5 Runtime Behavior</h2>
626
688
 
627
- <p>TODO</p>
689
+ <p>Evaluation of Liquor programs follows lexical order for blocks, and is undefined for expressions. As all expressions are pure, this does not result in ambiguity.</p>
690
+
691
+ <p>A Liquor program always evaluates to a string. Liquor recognizes the value of and attempts to produce sensible output even for partially invalid programs; to keep the codebase manageable, the runtime environment must report all runtime errors to the programmer.</p>
692
+
693
+ <h3 id="type-error">5.1 Type Error</h3>
694
+
695
+ <p>A type error arises when a value of certain type(s) is expected in a context, but a value of a different type is provided. In this case, the runtime records the error and substitutes the value for a zero value, respectively for every type:</p>
696
+
697
+ <ul>
698
+ <li><strong>Null</strong>: <em>null</em>.</li>
699
+ <li><strong>Boolean</strong>: <em>false</em>.</li>
700
+ <li><strong>Integer</strong>: <em>0</em>.</li>
701
+ <li><strong>String</strong>: <em>"”</em>.</li>
702
+ <li><strong>Tuple</strong>: <em>[]</em>.</li>
703
+ <li><strong>External</strong>: the <a href="#dummy-external">dummy external</a>.</li>
704
+ </ul>
705
+
706
+ <h3 id="external-error">5.2 External Error</h3>
707
+
708
+ <p>An external error arises when an unknown external method is called, or there is a problem evaluating the external method. In this case, the runtime records the error and returns <em>null</em> instead.</p>
709
+
710
+ <h3 id="the-dummy-external">5.3 The dummy external</h3>
711
+
712
+ <p>The dummy external is an external object which performs no operation when any method is called on it and returns <em>null</em>.</p>
628
713
 
629
714
  <h2 id="builtins">6 Builtins</h2>
630
715
 
@@ -664,11 +749,25 @@ In essence, <code><em>e</em> | <em>f</em> a: 1 | <em>g</em></code> is equivalent
664
749
  <em>code</em>
665
750
  {% end for %}</code></pre>
666
751
 
667
- <p>In the <em>for..in</em> form, this tag invokes <em>code</em> with <em>var</em> bound to each element of <em>list</em> sequentally. If <em>list</em> is not a <em>Tuple</em>, a runtime error condition is signaled.</p>
752
+ <p>In the <em>for..in</em> form, this tag invokes <em>code</em> with <em>var</em> bound to each element of <em>list</em> sequentally. If <em>list</em> is not a <em>Tuple</em>, a runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
753
+
754
+ <p>In the <em>for..from..to</em> form, this tag invokes <em>code</em> with <em>var</em> bound to each integer between <em>lower-limit</em> and <em>upper-limit</em>, inclusive. If <em>lower-limit</em> or <em>upper-limit</em> is not an <em>Integer</em>, a runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
755
+
756
+ <p>Both forms of the tag also bind a special <em>var</em>_loop variable to the [for loop external]{#for-external}.</p>
668
757
 
669
- <p>In the <em>for..from..to</em> form, this tag invokes <em>code</em> with <em>var</em> bound to each integer between <em>lower-limit</em> and <em>upper-limit</em>, inclusive. If <em>lower-limit</em> or <em>upper-limit</em> is not an <em>Integer</em>, a [runtime error condition] is signaled.</p>
758
+ <p>The <em>for</em> tag evaluates to the concatenation of the values its <em>code</em> has evaluated to.</p>
670
759
 
671
- <p>The <em>for</em> tag returns the concatenation of the values its <em>code</em> has evaluated to.</p>
760
+ <h5 id="for-loop-external">6.1.3.1 for loop external</h5>
761
+
762
+ <p>The for loop external is an <strong>External</strong> that allows to query the current state of the loop. It provides the following parameterless methods:</p>
763
+
764
+ <ul>
765
+ <li>length, returning the total amount of iterations.</li>
766
+ <li>index, returning the number of current iteration.</li>
767
+ <li>rindex, equivalent to <code>length - index - 1</code>.</li>
768
+ <li>is_first, equivalent to <code>index == 0</code>.</li>
769
+ <li>is_last, equivalent to <code>index == length - 1</code>.</li>
770
+ </ul>
672
771
 
673
772
  <h4 id="if">6.1.4 if</h4>
674
773
 
@@ -758,11 +857,247 @@ In essence, <code><em>e</em> | <em>f</em> a: 1 | <em>g</em></code> is equivalent
758
857
 
759
858
  <h3 id="functions">6.2 Functions</h3>
760
859
 
761
- <p>TODO</p>
860
+ <p>Liquor offers a number of builtin <a href="#function-calls">functions</a>. Their formal parameters are described using a shorthand notation:</p>
861
+
862
+ <p><code>fn_name(<em>unnamed-arg-type</em> kwarg1: <em>kwarg1-type</em> [kwarg2: <em>kwarg2-type, kwarg2-alt-type</em>])</code></p>
863
+
864
+ <p>In this case, function <em>fn_name</em> has an unnamed parameter accepting value of type <em>unnamed-arg-type</em>, a mandatory keyword parameter <em>kwarg1</em> accepting values of type <em>kwarg1-type</em>, and an optional keyword parameter <em>kwarg2</em> accepting values of either type <em>kwarg2-type</em> or <em>kwarg2-alt-type</em>.</p>
865
+
866
+ <h4 id="universal-functions">6.2.1 Universal functions</h4>
867
+
868
+ <h5 id="isempty">6.2.1.1 is_empty</h5>
869
+
870
+ <p><code>is_empty(<em>Any</em>)</code></p>
871
+
872
+ <p>Returns <em>true</em> iff the unnamed argument is one of <em>null</em>, <em>"”</em>, <em>[]</em>.</p>
873
+
874
+ <h5 id="size">6.2.1.2 size</h5>
875
+
876
+ <p><code>size(<em>String, Tuple</em>)</code></p>
877
+
878
+ <p>Returns the length of the unnamed argument as an integer.</p>
879
+
880
+ <h4 id="conversion-functions">6.2.2 Conversion functions</h4>
881
+
882
+ <h5 id="strftime">6.2.2.1 strftime</h5>
883
+
884
+ <p><code>size(<em>String</em> format: <em>String</em>)</code></p>
885
+
886
+ <p>Parses the unnamed argument as time in <a href="http://www.w3.org/TR/NOTE-datetime">ISO8601</a> format, and reformats it using an implementation-defined <a href="http://strftime.org/">strftime</a> alike function.</p>
887
+
888
+ <h5 id="tonumber">6.2.2.2 to_number</h5>
889
+
890
+ <p><code>to_number(<em>String, Integer</em>)</code></p>
891
+
892
+ <p>If the unnamed argument is an <strong>Integer</strong>, returns it. If it is a string, parses it as a decimal number, possibly with leading minus sign.</p>
893
+
894
+ <h4 id="integer-functions">6.2.3 Integer functions</h4>
895
+
896
+ <h5 id="iseven">6.2.3.1 is_even</h5>
897
+
898
+ <p><code>is_even(<em>Integer</em>)</code></p>
899
+
900
+ <p>Returns <em>true</em> if the unnamed argument is even, <em>false</em> otherwise.</p>
901
+
902
+ <h5 id="isodd">6.2.3.2 is_odd</h5>
903
+
904
+ <p><code>is_odd(<em>Integer</em>)</code></p>
905
+
906
+ <p>Returns <em>true</em> if the unnamed argument is odd, <em>false</em> otherwise.</p>
907
+
908
+ <h4 id="string-functions">6.2.4 String functions</h4>
909
+
910
+ <h5 id="downcase">6.2.4.1 downcase</h5>
911
+
912
+ <p><code>downcase(<em>String</em>)</code></p>
913
+
914
+ <p>Returns the unnamed argument, converted to lowercase, using the Unicode case folding.</p>
915
+
916
+ <h5 id="upcase">6.2.4.2 upcase</h5>
917
+
918
+ <p><code>upcase(<em>String</em>)</code></p>
919
+
920
+ <p>Returns the unnamed argument, converted to uppercase, using the Unicode case folding.</p>
921
+
922
+ <h5 id="capitalize">6.2.4.3 capitalize</h5>
923
+
924
+ <p><code>capitalize(<em>String</em>)</code></p>
925
+
926
+ <p>Returns the unnamed argument with its first character converted to uppercase, using the Unicode case folding.</p>
927
+
928
+ <h5 id="startswith">6.2.4.4 starts_with</h5>
929
+
930
+ <p><code>starts_with(<em>String</em> pattern: <em>String</em>)</code></p>
931
+
932
+ <p>Returns <em>true</em> if the unnamed argument starts with <em>pattern</em>, <em>false</em> otherwise. No normalization is performed.</p>
933
+
934
+ <h5 id="stripnewlines">6.2.4.5 strip_newlines</h5>
935
+
936
+ <p><code>strip_newlines(<em>String</em>)</code></p>
937
+
938
+ <p>Returns the unnamed argument without any <strong>U+000A</strong> characters.</p>
939
+
940
+ <h5 id="join">6.2.4.6 join</h5>
941
+
942
+ <p><code>join(<em>Tuple</em> with: <em>String</em>)</code></p>
943
+
944
+ <p>Returns the concatenation of elements of the unnamed argument (which all must be <strong>String</strong>s) interpsersed with the value of <em>with</em>.</p>
945
+
946
+ <h5 id="split">6.2.4.7 split</h5>
947
+
948
+ <p><code>split(<em>String</em> by: <em>String</em>)</code></p>
949
+
950
+ <p>Returns a tuple of fragments of the unnamed argument, extracted between occurences of <em>by</em>. If the unnamed argument is <em>"”</em>, returns <em>[]</em>.</p>
951
+
952
+ <h5 id="replace">6.2.4.8 replace</h5>
953
+
954
+ <p><code>replace(<em>String</em> pattern: <em>String</em> replacement: <em>String</em>)</code></p>
955
+
956
+ <p>Returns the unnamed argument with all occurences of <em>pattern</em> replaced with <em>replacement</em>.</p>
957
+
958
+ <h5 id="replacefirst">6.2.4.9 replace_first</h5>
959
+
960
+ <p><code>replace_first(<em>String</em> pattern: <em>String</em> replacement: <em>String</em>)</code></p>
961
+
962
+ <p>Returns the unnamed argument with the first occurence of <em>pattern</em> replaced with <em>replacement</em>.</p>
963
+
964
+ <h5 id="remove">6.2.4.10 remove</h5>
965
+
966
+ <p><code>remove(<em>String</em> pattern: <em>String</em>)</code></p>
967
+
968
+ <p>Returns the unnamed argument with all occurences of <em>pattern</em> removed.</p>
969
+
970
+ <h5 id="removefirst">6.2.4.11 remove_first</h5>
971
+
972
+ <p><code>remove_first(<em>String</em> pattern: <em>String</em>)</code></p>
973
+
974
+ <p>Returns the unnamed argument with the first occurences of <em>pattern</em> removed.</p>
975
+
976
+ <h5 id="newlinetobr">6.2.4.12 newline_to_br</h5>
977
+
978
+ <p><code>newline_to_br(<em>String</em>)</code></p>
979
+
980
+ <p>Returns the unnamed argument with <code>&lt;br&gt;</code> inserted before every <strong>U+000A</strong> character.</p>
981
+
982
+ <h5 id="urlescape">6.2.4.13 url_escape</h5>
983
+
984
+ <p><code>url_escape(<em>String</em>)</code></p>
985
+
986
+ <p>Returns the unnamed argument, processed using the <a href="http://www.w3.org/TR/html5/forms.html#url-encoded-form-data">application/x-www-form-urlencoded encoding algorithm</a>.</p>
987
+
988
+ <h5 id="function-html-escape">6.2.4.14 html_escape</h5>
989
+
990
+ <p><code>html_escape(<em>String</em>)</code></p>
991
+
992
+ <p>Returns the unnamed argument with <code>&amp;</code>, <code>&lt;</code>, <code>&gt;</code>, <code>'</code>, <code>"</code> and <code>/</code> escaped to the correpsonding HTML entities.</p>
993
+
994
+ <h5 id="htmlescapeonce-h">6.2.4.15 html_escape_once, h</h5>
995
+
996
+ <p><code>html_escape(<em>String</em>)</code></p>
997
+
998
+ <p><code>h(<em>String</em>)</code></p>
999
+
1000
+ <p>Like <a href="#function-html-escape">html_escape</a>, but does not affect <code>&amp;</code> that is a part of an HTML entity.</p>
1001
+
1002
+ <h5 id="striphtml">6.2.4.16 strip_html</h5>
1003
+
1004
+ <p><code>strip_html(<em>String</em>)</code></p>
1005
+
1006
+ <p>Returns the unnamed argument with all HTML tags and comments removed.</p>
1007
+
1008
+ <h5 id="decodehtmlentities">6.2.4.17 decode_html_entities</h5>
1009
+
1010
+ <p><code>decode_html_entities(<em>String</em>)</code></p>
1011
+
1012
+ <p>Returns the unnamed argument with all HTML entities replaced by the corresponding Unicode character.</p>
1013
+
1014
+ <h4 id="tuple-functions">6.2.5 Tuple functions</h4>
1015
+
1016
+ <h5 id="compact">6.2.5.1 compact</h5>
1017
+
1018
+ <p><code>compact(<em>Tuple</em>)</code></p>
1019
+
1020
+ <p>Returns the unnamed argument without <em>null</em> elements.</p>
1021
+
1022
+ <h5 id="reverse">6.2.5.2 reverse</h5>
1023
+
1024
+ <p><code>reverse(<em>Tuple</em>)</code></p>
1025
+
1026
+ <p>Returns the unnamed argument, reversed.</p>
1027
+
1028
+ <h5 id="uniq">6.2.5.3 uniq</h5>
1029
+
1030
+ <p><code>reverse(<em>Tuple</em>)</code></p>
1031
+
1032
+ <p>Returns the unnamed argument with only first instance of non-unique elements left.</p>
1033
+
1034
+ <h5 id="function-min">6.2.5.4 min</h5>
1035
+
1036
+ <p><code>min(<em>Tuple</em> [by: <em>String</em>])</code></p>
1037
+
1038
+ <p>Returns the minimal element of the unnamed argument. The ordering between values of different types is implementation-defined. Including an <strong>External</strong> in the unnamed argument may lead to a runtime error (<a href="#type-error">type error</a>).</p>
1039
+
1040
+ <p>If <em>by</em> is passed, the unnamed argument must consist only of <strong>External</strong> values. In this case, the ordering is performed by calling the method specified by <em>by</em>.</p>
1041
+
1042
+ <h5 id="max">6.2.5.5 max</h5>
1043
+
1044
+ <p><code>max(<em>Tuple</em> [by: <em>String</em>])</code></p>
1045
+
1046
+ <p>See <a href="#function-min">the min function</a>.</p>
1047
+
1048
+ <h5 id="ingroupsof">6.2.5.6 in_groups_of</h5>
1049
+
1050
+ <p><code>in_groups_of(<em>Tuple</em> size: <em>Integer</em> [fill_with: <em>String, Boolean</em>])</code></p>
1051
+
1052
+ <p>Returns the unnamed argument, split into tuples of <em>size</em> elements. If <em>fill_with</em> is passed, appends the value of <em>fill_with</em> to the last tuple, so that it is <em>size</em> elements big.</p>
1053
+
1054
+ <h5 id="ingroups">6.2.5.7 in_groups</h5>
1055
+
1056
+ <p><code>in_groups(<em>Tuple</em> count: <em>Integer</em> [fill_with: <em>String, Boolean</em>])</code></p>
1057
+
1058
+ <p>Returns the unnamed argument, split into <em>count</em> equally-sized (except the last one) tuples. If <em>fill_with</em> is passed, appends the value of <em>fill_with</em> to the last tuple, so that it is as big as the others.</p>
1059
+
1060
+ <h5 id="includes">6.2.5.8 includes</h5>
1061
+
1062
+ <p><code>includes(<em>Tuple, External</em> element: <em>Any</em>)</code></p>
1063
+
1064
+ <p>Returns <em>true</em> if the unnamed argument contains <em>element</em>, <em>false</em> otherwise. Not all externals support this operation; if an unsupported external is passed, runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
1065
+
1066
+ <h5 id="indexof">6.2.5.9 index_of</h5>
1067
+
1068
+ <p><code>index_of(<em>Tuple, External</em> element: <em>Any</em>)</code></p>
1069
+
1070
+ <p>Returns the index of <em>element</em> in the unnamed argument or <em>null</em> if it does not contain <em>element</em>. Not all externals support this operation; if an unsupported external is passed, runtime error condition (<a href="#type-error">type error</a>) is signaled.</p>
1071
+
1072
+ <h4 id="truncation-functions">6.2.6 Truncation functions</h4>
1073
+
1074
+ <h5 id="function-truncate">6.2.6.1 truncate</h5>
1075
+
1076
+ <p><code>truncate(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code></p>
1077
+
1078
+ <p>Returns the unnamed argument, truncated to <em>length</em> (50 by default) characters and with <em>omission</em> (<code>...</code> by default) appended.</p>
1079
+
1080
+ <h5 id="truncatewords">6.2.6.2 truncate_words</h5>
1081
+
1082
+ <p><code>truncate_words(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code></p>
1083
+
1084
+ <p>Returns the unnamed argument, truncated to <em>length</em> (15 by default) words and with <em>omission</em> (<code>...</code> by default) appended.</p>
1085
+
1086
+ <h5 id="htmltruncate">6.2.6.3 html_truncate</h5>
1087
+
1088
+ <p><code>html_truncate(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code></p>
1089
+
1090
+ <p>Returns the unnamed argument, truncated to <em>length</em> (50 by default) characters inside HTML text node and with <em>omission</em> (<code>...</code> by default) appended to the last HTML text node.</p>
1091
+
1092
+ <h5 id="htmltruncatewords">6.2.6.4 html_truncate_words</h5>
1093
+
1094
+ <p><code>html_truncate_words(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code></p>
1095
+
1096
+ <p>Returns the unnamed argument, truncated to <em>length</em> (15 by default) words inside HTML text node and with <em>omission</em> (<code>...</code> by default) appended to the last HTML text node.</p>
762
1097
 
763
- <h2 id="appendix-layouts">Appendix A: Layouts</h2>
1098
+ <h2 id="appendix-layouts">7 Layouts</h2>
764
1099
 
765
- <p>TODO</p>
1100
+ <p>In order to support the <a href="#contentfor"><em>content_for</em></a> and <a href="#yield"><em>yield</em></a> tags, the runtime maintains a dictionary associating the handles provided to <em>content_for</em> with the content. This dictionary is shared between all layout(s) and the innermost template; the templates are evaluated from the innermost to outermost.</p>
766
1101
 
767
1102
  </body>
768
1103
  </html>
@@ -102,8 +102,6 @@ dd strong, code {
102
102
  Liquor 2.0 Language Specification
103
103
  =================================
104
104
 
105
- <p style="text-align: right" markdown="1">*This version of specification is a working draft.*</p>
106
-
107
105
  Table of Contents
108
106
  -----------------
109
107
 
@@ -204,9 +202,9 @@ The operators `[]`, `.`, `()`, `.()` are not infix and are provided in this tabl
204
202
 
205
203
  Arithmetic operators are `*` (multiplication), `/` (division), `%` (modulo), `+` (plus) and `-` (minus; binary and unary).
206
204
 
207
- All arithmetic operators except `+`, whether unary or binary, require every argument to be of type **Integer**. If this is not the case, a runtime error condition is signaled.
205
+ All arithmetic operators except `+`, whether unary or binary, require every argument to be of type **Integer**. If this is not the case, a runtime error condition ([type error](#type-error)) is signaled.
208
206
 
209
- Operator `+` requires both arguments to be of the same type, and only accepts arguments of type **Integer**, **String** or **Tuple**. If any of the conditions is not satisfied, a runtime error condition is signaled. For arguments of type **String** or **Tuple**, the `+` operator evaluates to the concatenation of left and right arguments in that order.
207
+ Operator `+` requires both arguments to be of the same type, and only accepts arguments of type **Integer**, **String** or **Tuple**. If any of the conditions is not satisfied, a runtime error condition ([type error](#type-error)) is signaled. For arguments of type **String** or **Tuple**, the `+` operator evaluates to the concatenation of left and right arguments in that order.
210
208
 
211
209
  If the result of an arithmetic operation, except operator `+` with non-**Integer** arguments, exceeds the range an implementation can represent, the behavior is implementation-defined.
212
210
 
@@ -227,15 +225,15 @@ Comparison operators are `==` (equals), `!=` (not equals), `<` (less), `<=` (les
227
225
 
228
226
  Operators `==` and `!=` compare values by equality, not identity. Thus, the expression `[ 1, 2 ] == [ 1, 2 ]` evluates to _true_. These operators never signal an error condition or implicitly convert types.
229
227
 
230
- Operators `<`, `<=`, `>` and `>=` require both arguments to be of type **Integer**. If this is not the case, a runtime error condition is signaled. Otherwise, a corresponding value of type **Boolean** is returned.
228
+ Operators `<`, `<=`, `>` and `>=` require both arguments to be of type **Integer**. If this is not the case, a runtime error condition ([type error](#type-error)) is signaled. Otherwise, a corresponding value of type **Boolean** is returned.
231
229
 
232
230
  ##### 2.4.2.4 Indexing Operator
233
231
 
234
232
  Indexing operator is `[]`.
235
233
 
236
- Indexing operator requires its left-hand side argument to be of type **Tuple** or **External**, and right-hand side argument to be of type **Integer**. If this is not the case, a runtime error condition is signaled.
234
+ Indexing operator requires its left-hand side argument to be of type **Tuple** or **External**, and right-hand side argument to be of type **Integer**. If this is not the case, a runtime error condition ([type error](#type-error)) is signaled.
237
235
 
238
- If the left-hand side argument is of type **External**, the behavior is implementation-defined. A runtime error condition can be signaled if the particular external value does not support indexing.
236
+ If the left-hand side argument is of type **External**, the behavior is implementation-defined. A runtime error condition ([external error](#external-error)) is signaled if the particular external value does not support indexing.
239
237
 
240
238
  Indexing operator of form <code><em>t</em>[<em>n</em>]</code> evaluates to _n_-th value from tuple _t_ with zero-based indexing. If `n` is negative, then _n_+1-th element from the end of tuple is returned. For example, <code><em>t</em>[-1]</code> will evaluate to the last element of the tuple _t_.
241
239
 
@@ -259,13 +257,13 @@ Access operators are `.` and `.()`.
259
257
 
260
258
  The `.` form is syntactic sugar for `.()` form without any arguments. That is, <code><em>e</em>.<em>f</em></code> is completely equivalent to <code><em>e</em>.<em>f</em>()</code>.
261
259
 
262
- Access operator requires its left-hand side argument to be of type **External**. If this is not the case, a runtime error condition is signaled.
260
+ Access operator requires its left-hand side argument to be of type **External**. If this is not the case, a runtime error condition ([type error](#type-error)) is signaled.
263
261
 
264
262
  Access operator of form <code><em>e</em>.<em>f</em>(<em>arg</em> <em>kw:</em> <em>value</em>)</code> evaluates to the result of calling method _f_ of external object _e_ with the corresponding arguments. Argument syntax is the same as for [function calls](#function-calls).
265
263
 
266
264
  This evaluation is done in an implementation-defined way. Access operator can evaluate to any type.
267
265
 
268
- If the requested method does not exist in the external object or cannot successfully evaluate, a runtime error condition is signaled. Errors in the called method must not interrupt execution of the calling Liquor program.
266
+ If the requested method does not exist in the external object or cannot successfully evaluate, a runtime error condition ([external error](#external-error)) is signaled. Errors in the called method must not interrupt execution of the calling Liquor program.
269
267
 
270
268
  #### 2.4.5 Variable Access
271
269
 
@@ -301,7 +299,7 @@ A block can have other elements embedded into it. When such a block is executed,
301
299
 
302
300
  ### 2.6 Interpolations
303
301
 
304
- An interpolation is a syntactic construct of form `{{ expr }}` which can be embedded in a block. The expression `expr` should evaluate to a value of type **String** or **Null**; an [implicit conversion](#type-conversion) might take place. If this is not the case, a runtime error condition is signaled.
302
+ An interpolation is a syntactic construct of form `{{ expr }}` which can be embedded in a block. The expression `expr` should evaluate to a value of type **String** or **Null**; an [implicit conversion](#type-conversion) might take place. If this is not the case, a runtime error condition ([type error](#type-error)) is signaled.
305
303
 
306
304
  If _expr_ evaluates to a **String**, the interpolation returns it. Otherwise, the interpolation returns an empty string.
307
305
 
@@ -391,7 +389,7 @@ StringLiteral
391
389
  : **\'** ( **\\\\** \| **\\\'** \| _Any_ except **\'** )* **\'**
392
390
 
393
391
  TupleLiteral
394
- : **[** _TupleLiteralContent_ **]**
392
+ : **\[** _TupleLiteralContent_ **]**
395
393
 
396
394
  TupleLiteralContent
397
395
  : _Expression_ **,** _TupleLiteralContent_
@@ -411,7 +409,7 @@ Expression
411
409
  : _StringLiteral_
412
410
  : _TupleLiteral_
413
411
  : _Identifier_ _FunctionArguments_
414
- : _PrimaryExpression_ **[** _Expression_ **]**
412
+ : _PrimaryExpression_ **\[** _Expression_ **]**
415
413
  : _Expression_ **.** _Identifier_ _FunctionArguments_?
416
414
  : **-** _Expression_
417
415
  : **!** _Expression_
@@ -556,7 +554,28 @@ An implementation should have a way to inject variables into the outermost scope
556
554
  5 Runtime Behavior
557
555
  ------------------
558
556
 
559
- TODO
557
+ Evaluation of Liquor programs follows lexical order for blocks, and is undefined for expressions. As all expressions are pure, this does not result in ambiguity.
558
+
559
+ A Liquor program always evaluates to a string. Liquor recognizes the value of and attempts to produce sensible output even for partially invalid programs; to keep the codebase manageable, the runtime environment must report all runtime errors to the programmer.
560
+
561
+ ### 5.1 Type Error {#type-error}
562
+
563
+ A type error arises when a value of certain type(s) is expected in a context, but a value of a different type is provided. In this case, the runtime records the error and substitutes the value for a zero value, respectively for every type:
564
+
565
+ * **Null**: _null_.
566
+ * **Boolean**: _false_.
567
+ * **Integer**: _0_.
568
+ * **String**: _""_.
569
+ * **Tuple**: _[]_.
570
+ * **External**: the [dummy external](#dummy-external).
571
+
572
+ ### 5.2 External Error {#external-error}
573
+
574
+ An external error arises when an unknown external method is called, or there is a problem evaluating the external method. In this case, the runtime records the error and returns _null_ instead.
575
+
576
+ ### 5.3 The dummy external
577
+
578
+ The dummy external is an external object which performs no operation when any method is called on it and returns _null_.
560
579
 
561
580
  6 Builtins
562
581
  ----------
@@ -597,11 +616,23 @@ Tag _for_ has two valid syntactic forms:
597
616
  <em>code</em>
598
617
  {% end for %}</code></pre>
599
618
 
600
- In the _for..in_ form, this tag invokes _code_ with _var_ bound to each element of _list_ sequentally. If _list_ is not a *Tuple*, a runtime error condition is signaled.
619
+ In the _for..in_ form, this tag invokes _code_ with _var_ bound to each element of _list_ sequentally. If _list_ is not a *Tuple*, a runtime error condition ([type error](#type-error)) is signaled.
601
620
 
602
- In the _for..from..to_ form, this tag invokes _code_ with _var_ bound to each integer between _lower-limit_ and _upper-limit_, inclusive. If _lower-limit_ or _upper-limit_ is not an *Integer*, a [runtime error condition] is signaled.
621
+ In the _for..from..to_ form, this tag invokes _code_ with _var_ bound to each integer between _lower-limit_ and _upper-limit_, inclusive. If _lower-limit_ or _upper-limit_ is not an *Integer*, a runtime error condition ([type error](#type-error)) is signaled.
603
622
 
604
- The _for_ tag returns the concatenation of the values its _code_ has evaluated to.
623
+ Both forms of the tag also bind a special <em>var</em>_loop variable to the [for loop external]{#for-external}.
624
+
625
+ The _for_ tag evaluates to the concatenation of the values its _code_ has evaluated to.
626
+
627
+ ##### 6.1.3.1 for loop external
628
+
629
+ The for loop external is an **External** that allows to query the current state of the loop. It provides the following parameterless methods:
630
+
631
+ * length, returning the total amount of iterations.
632
+ * index, returning the number of current iteration.
633
+ * rindex, equivalent to `length - index - 1`.
634
+ * is_first, equivalent to `index == 0`.
635
+ * is_last, equivalent to `index == length - 1`.
605
636
 
606
637
  #### 6.1.4 if
607
638
 
@@ -691,8 +722,244 @@ See also notes on [Layout implementation](#appendix-layouts).
691
722
 
692
723
  ### 6.2 Functions
693
724
 
694
- TODO
725
+ Liquor offers a number of builtin [functions](#function-calls). Their formal parameters are described using a shorthand notation:
726
+
727
+ <code>fn_name(<em>unnamed-arg-type</em> kwarg1: <em>kwarg1-type</em> [kwarg2: <em>kwarg2-type, kwarg2-alt-type</em>])</code>
728
+
729
+ In this case, function _fn_name_ has an unnamed parameter accepting value of type _unnamed-arg-type_, a mandatory keyword parameter _kwarg1_ accepting values of type _kwarg1-type_, and an optional keyword parameter _kwarg2_ accepting values of either type _kwarg2-type_ or _kwarg2-alt-type_.
730
+
731
+ #### 6.2.1 Universal functions
732
+
733
+ ##### 6.2.1.1 is_empty
734
+
735
+ <code>is_empty(<em>Any</em>)</code>
736
+
737
+ Returns _true_ iff the unnamed argument is one of _null_, _""_, _[]_.
738
+
739
+ ##### 6.2.1.2 size
740
+
741
+ <code>size(<em>String, Tuple</em>)</code>
742
+
743
+ Returns the length of the unnamed argument as an integer.
744
+
745
+ #### 6.2.2 Conversion functions
746
+
747
+ ##### 6.2.2.1 strftime
748
+
749
+ <code>size(<em>String</em> format: <em>String</em>)</code>
750
+
751
+ Parses the unnamed argument as time in [ISO8601](http://www.w3.org/TR/NOTE-datetime) format, and reformats it using an implementation-defined [strftime](http://strftime.org/) alike function.
752
+
753
+ ##### 6.2.2.2 to_number
754
+
755
+ <code>to_number(<em>String, Integer</em>)</code>
756
+
757
+ If the unnamed argument is an **Integer**, returns it. If it is a string, parses it as a decimal number, possibly with leading minus sign.
758
+
759
+ #### 6.2.3 Integer functions
760
+
761
+ ##### 6.2.3.1 is_even
762
+
763
+ <code>is_even(<em>Integer</em>)</code>
764
+
765
+ Returns _true_ if the unnamed argument is even, _false_ otherwise.
766
+
767
+ ##### 6.2.3.2 is_odd
768
+
769
+ <code>is_odd(<em>Integer</em>)</code>
770
+
771
+ Returns _true_ if the unnamed argument is odd, _false_ otherwise.
772
+
773
+ #### 6.2.4 String functions
774
+
775
+ ##### 6.2.4.1 downcase
776
+
777
+ <code>downcase(<em>String</em>)</code>
778
+
779
+ Returns the unnamed argument, converted to lowercase, using the Unicode case folding.
780
+
781
+ ##### 6.2.4.2 upcase
782
+
783
+ <code>upcase(<em>String</em>)</code>
784
+
785
+ Returns the unnamed argument, converted to uppercase, using the Unicode case folding.
786
+
787
+ ##### 6.2.4.3 capitalize
788
+
789
+ <code>capitalize(<em>String</em>)</code>
790
+
791
+ Returns the unnamed argument with its first character converted to uppercase, using the Unicode case folding.
792
+
793
+ ##### 6.2.4.4 starts_with
794
+
795
+ <code>starts_with(<em>String</em> pattern: <em>String</em>)</code>
796
+
797
+ Returns _true_ if the unnamed argument starts with _pattern_, _false_ otherwise. No normalization is performed.
798
+
799
+ ##### 6.2.4.5 strip_newlines
800
+
801
+ <code>strip_newlines(<em>String</em>)</code>
802
+
803
+ Returns the unnamed argument without any **U+000A** characters.
804
+
805
+ ##### 6.2.4.6 join
806
+
807
+ <code>join(<em>Tuple</em> with: <em>String</em>)</code>
808
+
809
+ Returns the concatenation of elements of the unnamed argument (which all must be **String**s) interpsersed with the value of _with_.
810
+
811
+ ##### 6.2.4.7 split
812
+
813
+ <code>split(<em>String</em> by: <em>String</em>)</code>
814
+
815
+ Returns a tuple of fragments of the unnamed argument, extracted between occurences of _by_. If the unnamed argument is _""_, returns _[]_.
816
+
817
+ ##### 6.2.4.8 replace
818
+
819
+ <code>replace(<em>String</em> pattern: <em>String</em> replacement: <em>String</em>)</code>
820
+
821
+ Returns the unnamed argument with all occurences of _pattern_ replaced with _replacement_.
822
+
823
+ ##### 6.2.4.9 replace_first
824
+
825
+ <code>replace_first(<em>String</em> pattern: <em>String</em> replacement: <em>String</em>)</code>
826
+
827
+ Returns the unnamed argument with the first occurence of _pattern_ replaced with _replacement_.
828
+
829
+ ##### 6.2.4.10 remove
830
+
831
+ <code>remove(<em>String</em> pattern: <em>String</em>)</code>
832
+
833
+ Returns the unnamed argument with all occurences of _pattern_ removed.
834
+
835
+ ##### 6.2.4.11 remove_first
836
+
837
+ <code>remove_first(<em>String</em> pattern: <em>String</em>)</code>
838
+
839
+ Returns the unnamed argument with the first occurences of _pattern_ removed.
840
+
841
+ ##### 6.2.4.12 newline_to_br
842
+
843
+ <code>newline_to_br(<em>String</em>)</code>
844
+
845
+ Returns the unnamed argument with `<br>` inserted before every **U+000A** character.
846
+
847
+ ##### 6.2.4.13 url_escape
848
+
849
+ <code>url_escape(<em>String</em>)</code>
850
+
851
+ Returns the unnamed argument, processed using the [application/x-www-form-urlencoded encoding algorithm](http://www.w3.org/TR/html5/forms.html#url-encoded-form-data).
852
+
853
+ ##### 6.2.4.14 html_escape {#function-html-escape}
854
+
855
+ <code>html_escape(<em>String</em>)</code>
856
+
857
+ Returns the unnamed argument with `&`, `<`, `>`, `'`, `"` and `/` escaped to the correpsonding HTML entities.
858
+
859
+ ##### 6.2.4.15 html_escape_once, h
860
+
861
+ <code>html_escape(<em>String</em>)</code>
862
+
863
+ <code>h(<em>String</em>)</code>
864
+
865
+ Like [html_escape](#function-html-escape), but does not affect `&` that is a part of an HTML entity.
866
+
867
+ ##### 6.2.4.16 strip_html
868
+
869
+ <code>strip_html(<em>String</em>)</code>
870
+
871
+ Returns the unnamed argument with all HTML tags and comments removed.
872
+
873
+ ##### 6.2.4.17 decode_html_entities
874
+
875
+ <code>decode_html_entities(<em>String</em>)</code>
876
+
877
+ Returns the unnamed argument with all HTML entities replaced by the corresponding Unicode character.
878
+
879
+ #### 6.2.5 Tuple functions
880
+
881
+ ##### 6.2.5.1 compact
882
+
883
+ <code>compact(<em>Tuple</em>)</code>
884
+
885
+ Returns the unnamed argument without _null_ elements.
886
+
887
+ ##### 6.2.5.2 reverse
888
+
889
+ <code>reverse(<em>Tuple</em>)</code>
890
+
891
+ Returns the unnamed argument, reversed.
892
+
893
+ ##### 6.2.5.3 uniq
894
+
895
+ <code>reverse(<em>Tuple</em>)</code>
896
+
897
+ Returns the unnamed argument with only first instance of non-unique elements left.
898
+
899
+ ##### 6.2.5.4 min {#function-min}
900
+
901
+ <code>min(<em>Tuple</em> [by: <em>String</em>])
902
+
903
+ Returns the minimal element of the unnamed argument. The ordering between values of different types is implementation-defined. Including an **External** in the unnamed argument may lead to a runtime error ([type error](#type-error)).
904
+
905
+ If _by_ is passed, the unnamed argument must consist only of **External** values. In this case, the ordering is performed by calling the method specified by _by_.
906
+
907
+ ##### 6.2.5.5 max
908
+
909
+ <code>max(<em>Tuple</em> [by: <em>String</em>])
910
+
911
+ See [the min function](#function-min).
912
+
913
+ ##### 6.2.5.6 in_groups_of
914
+
915
+ <code>in_groups_of(<em>Tuple</em> size: <em>Integer</em> [fill_with: <em>String, Boolean</em>])</code>
916
+
917
+ Returns the unnamed argument, split into tuples of _size_ elements. If _fill_with_ is passed, appends the value of _fill_with_ to the last tuple, so that it is _size_ elements big.
918
+
919
+ ##### 6.2.5.7 in_groups
920
+
921
+ <code>in_groups(<em>Tuple</em> count: <em>Integer</em> [fill_with: <em>String, Boolean</em>])</code>
922
+
923
+ Returns the unnamed argument, split into _count_ equally-sized (except the last one) tuples. If _fill_with_ is passed, appends the value of _fill_with_ to the last tuple, so that it is as big as the others.
924
+
925
+ ##### 6.2.5.8 includes
926
+
927
+ <code>includes(<em>Tuple, External</em> element: <em>Any</em>)</code>
928
+
929
+ Returns _true_ if the unnamed argument contains _element_, _false_ otherwise. Not all externals support this operation; if an unsupported external is passed, runtime error condition ([type error](#type-error)) is signaled.
930
+
931
+ ##### 6.2.5.9 index_of
932
+
933
+ <code>index_of(<em>Tuple, External</em> element: <em>Any</em>)</code>
934
+
935
+ Returns the index of _element_ in the unnamed argument or _null_ if it does not contain _element_. Not all externals support this operation; if an unsupported external is passed, runtime error condition ([type error](#type-error)) is signaled.
936
+
937
+ #### 6.2.6 Truncation functions
938
+
939
+ ##### 6.2.6.1 truncate {#function-truncate}
940
+
941
+ <code>truncate(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code>
942
+
943
+ Returns the unnamed argument, truncated to _length_ (50 by default) characters and with _omission_ (`...` by default) appended.
944
+
945
+ ##### 6.2.6.2 truncate_words
946
+
947
+ <code>truncate_words(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code>
948
+
949
+ Returns the unnamed argument, truncated to _length_ (15 by default) words and with _omission_ (`...` by default) appended.
950
+
951
+ ##### 6.2.6.3 html_truncate
952
+
953
+ <code>html_truncate(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code>
954
+
955
+ Returns the unnamed argument, truncated to _length_ (50 by default) characters inside HTML text node and with _omission_ (`...` by default) appended to the last HTML text node.
956
+
957
+ ##### 6.2.6.4 html_truncate_words
958
+
959
+ <code>html_truncate_words(<em>String</em> [length: <em>Integer</em> omission: <em>String</em>])</code>
960
+
961
+ Returns the unnamed argument, truncated to _length_ (15 by default) words inside HTML text node and with _omission_ (`...` by default) appended to the last HTML text node.
695
962
 
696
- ## Appendix A: Layouts {#appendix-layouts}
963
+ ## 7 Layouts {#appendix-layouts}
697
964
 
698
- TODO
965
+ In order to support the [_content_for_](#contentfor) and [_yield_](#yield) tags, the runtime maintains a dictionary associating the handles provided to _content_for_ with the content. This dictionary is shared between all layout(s) and the innermost template; the templates are evaluated from the innermost to outermost.
@@ -1,3 +1,3 @@
1
1
  module Liquor
2
- VERSION = "0.9.6"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Zotov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-05-13 00:00:00.000000000 Z
13
+ date: 2014-11-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -238,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
238
  version: '0'
239
239
  requirements: []
240
240
  rubyforge_project:
241
- rubygems_version: 2.2.2
241
+ rubygems_version: 2.4.1
242
242
  signing_key:
243
243
  specification_version: 4
244
244
  summary: Liquor is a template system based on well-defined, strongly dynamically typed