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 +4 -4
- data/Guardfile +4 -1
- data/README.md +131 -2
- data/Rakefile +13 -0
- data/doc/language-spec.html +360 -25
- data/doc/language-spec.md +286 -19
- data/lib/liquor/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f27a409b1a1c3302cf30202dae7528a5754b46b8
|
4
|
+
data.tar.gz: c7fafccfb2ed2c21d8c7bc4d31b67212cca01b25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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')
|
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
|
-
|
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
|
-
|
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
|
data/doc/language-spec.html
CHANGED
@@ -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.
|
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
|
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
|
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
|
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">
|
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><</code>, <code><=</code>, <code>></code> and <code>>=</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><</code>, <code><=</code>, <code>></code> and <code>>=</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
|
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>
|
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>
|
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
|
-
<
|
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>
|
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><br></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>&</code>, <code><</code>, <code>></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>&</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">
|
1098
|
+
<h2 id="appendix-layouts">7 Layouts</h2>
|
764
1099
|
|
765
|
-
<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>
|
data/doc/language-spec.md
CHANGED
@@ -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
|
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
|
-
:
|
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_
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
##
|
963
|
+
## 7 Layouts {#appendix-layouts}
|
697
964
|
|
698
|
-
|
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.
|
data/lib/liquor/version.rb
CHANGED
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.
|
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-
|
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.
|
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
|