liquor 0.1.1 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -9
- data/Gemfile +7 -0
- data/Guardfile +11 -0
- data/MIT-LICENSE +6 -2
- data/README.md +4 -122
- data/Rakefile +20 -23
- data/doc/language-spec.html +768 -0
- data/doc/language-spec.md +698 -0
- data/lib/liquor.rb +39 -68
- data/lib/liquor/ast_tools.rb +28 -0
- data/lib/liquor/compiler.rb +110 -0
- data/lib/liquor/context.rb +76 -254
- data/lib/liquor/diagnostics.rb +151 -0
- data/lib/liquor/drop/drop.rb +168 -0
- data/lib/liquor/drop/drop_delegation.rb +24 -0
- data/lib/liquor/drop/drop_scope.rb +118 -0
- data/lib/liquor/drop/dropable.rb +17 -0
- data/lib/liquor/emitter.rb +313 -0
- data/lib/liquor/extensions/kaminari.rb +14 -0
- data/lib/liquor/extensions/pagination.rb +235 -0
- data/lib/liquor/extensions/rails.rb +97 -0
- data/lib/liquor/extensions/thinking_sphinx.rb +14 -0
- data/lib/liquor/extensions/tire.rb +30 -0
- data/lib/liquor/external.rb +79 -0
- data/lib/liquor/function.rb +94 -0
- data/lib/liquor/grammar/lexer.rb +1223 -0
- data/lib/liquor/grammar/lexer.rl +297 -0
- data/lib/liquor/grammar/parser.racc +288 -0
- data/lib/liquor/grammar/parser.rb +885 -0
- data/lib/liquor/library.rb +41 -0
- data/lib/liquor/manager.rb +146 -0
- data/lib/liquor/runtime.rb +167 -0
- data/lib/liquor/stdlib/builtin_functions.rb +315 -0
- data/lib/liquor/stdlib/builtin_tags.rb +228 -0
- data/lib/liquor/stdlib/html_truncater.rb +162 -0
- data/lib/liquor/stdlib/partial_tags.rb +76 -0
- data/lib/liquor/tag.rb +83 -14
- data/lib/liquor/version.rb +1 -1
- data/liquor.gemspec +29 -6
- data/spec/builtins_spec.rb +264 -0
- data/spec/compiler_spec.rb +136 -0
- data/spec/context_spec.rb +49 -0
- data/spec/drop_delegation_spec.rb +21 -0
- data/spec/drop_spec.rb +222 -0
- data/spec/errors_spec.rb +40 -0
- data/spec/external_spec.rb +207 -0
- data/spec/function_spec.rb +80 -0
- data/spec/lexer_spec.rb +173 -0
- data/spec/library_spec.rb +18 -0
- data/spec/manager_spec.rb +84 -0
- data/spec/parser_spec.rb +381 -0
- data/spec/partials_spec.rb +74 -0
- data/spec/runtime_spec.rb +97 -0
- data/spec/spec_helper.rb +94 -0
- data/spec/tag_spec.rb +7 -0
- metadata +216 -173
- data/AUTHORS +0 -2
- data/CHANGELOG +0 -48
- data/Gemfile.lock +0 -91
- data/History.txt +0 -44
- data/LICENSE +0 -23
- data/example/server/example_servlet.rb +0 -37
- data/example/server/liquid_servlet.rb +0 -28
- data/example/server/liquor_servlet.rb +0 -28
- data/example/server/server.rb +0 -12
- data/example/server/templates/index.liquid +0 -6
- data/example/server/templates/index.liquor +0 -6
- data/example/server/templates/products.liquid +0 -45
- data/example/server/templates/products.liquor +0 -45
- data/init.rb +0 -8
- data/lib/extras/liquid_view.rb +0 -51
- data/lib/extras/liquor_view.rb +0 -51
- data/lib/liquor/block.rb +0 -101
- data/lib/liquor/condition.rb +0 -120
- data/lib/liquor/document.rb +0 -17
- data/lib/liquor/drop.rb +0 -256
- data/lib/liquor/errors.rb +0 -11
- data/lib/liquor/extensions.rb +0 -72
- data/lib/liquor/file_system.rb +0 -62
- data/lib/liquor/htmltags.rb +0 -74
- data/lib/liquor/module_ex.rb +0 -60
- data/lib/liquor/standardfilters.rb +0 -315
- data/lib/liquor/strainer.rb +0 -58
- data/lib/liquor/tags/assign.rb +0 -33
- data/lib/liquor/tags/capture.rb +0 -35
- data/lib/liquor/tags/case.rb +0 -83
- data/lib/liquor/tags/comment.rb +0 -9
- data/lib/liquor/tags/content_for.rb +0 -54
- data/lib/liquor/tags/cycle.rb +0 -59
- data/lib/liquor/tags/for.rb +0 -136
- data/lib/liquor/tags/if.rb +0 -80
- data/lib/liquor/tags/ifchanged.rb +0 -20
- data/lib/liquor/tags/include.rb +0 -56
- data/lib/liquor/tags/unless.rb +0 -33
- data/lib/liquor/tags/yield.rb +0 -49
- data/lib/liquor/template.rb +0 -181
- data/lib/liquor/variable.rb +0 -52
- data/performance/shopify.rb +0 -92
- data/performance/shopify/comment_form.rb +0 -33
- data/performance/shopify/database.rb +0 -45
- data/performance/shopify/json_filter.rb +0 -7
- data/performance/shopify/liquid.rb +0 -18
- data/performance/shopify/liquor.rb +0 -18
- data/performance/shopify/money_filter.rb +0 -18
- data/performance/shopify/paginate.rb +0 -93
- data/performance/shopify/shop_filter.rb +0 -98
- data/performance/shopify/tag_filter.rb +0 -25
- data/performance/shopify/vision.database.yml +0 -945
- data/performance/shopify/weight_filter.rb +0 -11
- data/performance/tests/dropify/article.liquid +0 -74
- data/performance/tests/dropify/blog.liquid +0 -33
- data/performance/tests/dropify/cart.liquid +0 -66
- data/performance/tests/dropify/collection.liquid +0 -22
- data/performance/tests/dropify/index.liquid +0 -47
- data/performance/tests/dropify/page.liquid +0 -8
- data/performance/tests/dropify/product.liquid +0 -68
- data/performance/tests/dropify/theme.liquid +0 -105
- data/performance/tests/ripen/article.liquid +0 -74
- data/performance/tests/ripen/blog.liquid +0 -13
- data/performance/tests/ripen/cart.liquid +0 -54
- data/performance/tests/ripen/collection.liquid +0 -29
- data/performance/tests/ripen/index.liquid +0 -32
- data/performance/tests/ripen/page.liquid +0 -4
- data/performance/tests/ripen/product.liquid +0 -75
- data/performance/tests/ripen/theme.liquid +0 -85
- data/performance/tests/tribble/404.liquid +0 -56
- data/performance/tests/tribble/article.liquid +0 -98
- data/performance/tests/tribble/blog.liquid +0 -41
- data/performance/tests/tribble/cart.liquid +0 -134
- data/performance/tests/tribble/collection.liquid +0 -70
- data/performance/tests/tribble/index.liquid +0 -94
- data/performance/tests/tribble/page.liquid +0 -56
- data/performance/tests/tribble/product.liquid +0 -116
- data/performance/tests/tribble/search.liquid +0 -51
- data/performance/tests/tribble/theme.liquid +0 -90
- data/performance/tests/vogue/article.liquid +0 -66
- data/performance/tests/vogue/blog.liquid +0 -32
- data/performance/tests/vogue/cart.liquid +0 -58
- data/performance/tests/vogue/collection.liquid +0 -19
- data/performance/tests/vogue/index.liquid +0 -22
- data/performance/tests/vogue/page.liquid +0 -3
- data/performance/tests/vogue/product.liquid +0 -62
- data/performance/tests/vogue/theme.liquid +0 -122
- data/test/assign_test.rb +0 -11
- data/test/block_test.rb +0 -58
- data/test/capture_test.rb +0 -41
- data/test/condition_test.rb +0 -115
- data/test/content_for_test.rb +0 -15
- data/test/context_test.rb +0 -479
- data/test/drop_test.rb +0 -162
- data/test/error_handling_test.rb +0 -89
- data/test/extra/breakpoint.rb +0 -547
- data/test/extra/caller.rb +0 -80
- data/test/file_system_test.rb +0 -30
- data/test/filter_test.rb +0 -147
- data/test/helper.rb +0 -24
- data/test/html_tag_test.rb +0 -31
- data/test/if_else_test.rb +0 -139
- data/test/include_tag_test.rb +0 -129
- data/test/module_ex_test.rb +0 -89
- data/test/output_test.rb +0 -121
- data/test/parsing_quirks_test.rb +0 -54
- data/test/regexp_test.rb +0 -45
- data/test/security_test.rb +0 -41
- data/test/standard_filter_test.rb +0 -170
- data/test/standard_tag_test.rb +0 -405
- data/test/statements_test.rb +0 -137
- data/test/strainer_test.rb +0 -27
- data/test/template_test.rb +0 -82
- data/test/test_helper.rb +0 -28
- data/test/unless_else_test.rb +0 -27
- data/test/variable_test.rb +0 -173
- data/test/yield_test.rb +0 -24
@@ -0,0 +1,698 @@
|
|
1
|
+
<style>
|
2
|
+
body {
|
3
|
+
margin: 0 auto;
|
4
|
+
color: #444444;
|
5
|
+
line-height: 1;
|
6
|
+
max-width: 960px;
|
7
|
+
padding: 30px;
|
8
|
+
}
|
9
|
+
h1, h2, h3, h4 {
|
10
|
+
color: #111111;
|
11
|
+
font-weight: 400;
|
12
|
+
}
|
13
|
+
h1, h2, h3, h4, h5 {
|
14
|
+
margin-bottom: 24px;
|
15
|
+
padding: 0;
|
16
|
+
}
|
17
|
+
h1 {
|
18
|
+
font-size: 48px;
|
19
|
+
}
|
20
|
+
h2 {
|
21
|
+
font-size: 36px;
|
22
|
+
}
|
23
|
+
h3 {
|
24
|
+
font-size: 24px;
|
25
|
+
}
|
26
|
+
h4 {
|
27
|
+
font-size: 21px;
|
28
|
+
}
|
29
|
+
h5 {
|
30
|
+
font-size: 18px;
|
31
|
+
}
|
32
|
+
a {
|
33
|
+
color: #0099ff;
|
34
|
+
margin: 0;
|
35
|
+
padding: 0;
|
36
|
+
vertical-align: baseline;
|
37
|
+
}
|
38
|
+
a:visited {
|
39
|
+
color: #0047ff;
|
40
|
+
}
|
41
|
+
a:hover {
|
42
|
+
text-decoration: none;
|
43
|
+
color: #ff6600;
|
44
|
+
}
|
45
|
+
li {
|
46
|
+
line-height: 24px;
|
47
|
+
}
|
48
|
+
p, ul, ol {
|
49
|
+
font-size: 16px;
|
50
|
+
line-height: 24px;
|
51
|
+
max-width: 740px;
|
52
|
+
margin-bottom: 14px;
|
53
|
+
}
|
54
|
+
pre {
|
55
|
+
padding: 0px 24px;
|
56
|
+
max-width: 800px;
|
57
|
+
white-space: pre-wrap;
|
58
|
+
}
|
59
|
+
code {
|
60
|
+
line-height: 1.5;
|
61
|
+
font-size: 13px;
|
62
|
+
background: #E9E8E7;
|
63
|
+
}
|
64
|
+
pre > code {
|
65
|
+
border: none;
|
66
|
+
background: none;
|
67
|
+
}
|
68
|
+
#markdown-toc, #markdown-toc ul {
|
69
|
+
list-style-type: none;
|
70
|
+
margin: 0 0 0 1em;
|
71
|
+
padding: 0;
|
72
|
+
}
|
73
|
+
#markdown-toc {
|
74
|
+
margin: 20px 0 0 0;
|
75
|
+
}
|
76
|
+
code, dd {
|
77
|
+
font-family: Consolas, Monaco, Andale Mono, monospace;
|
78
|
+
}
|
79
|
+
body, em {
|
80
|
+
font-family: Georgia, Palatino, serif;
|
81
|
+
}
|
82
|
+
dl {
|
83
|
+
font-size: 16px;
|
84
|
+
line-height: 24px;
|
85
|
+
}
|
86
|
+
dt {
|
87
|
+
font-style: italic;
|
88
|
+
margin-top: 10px;
|
89
|
+
}
|
90
|
+
dt:after {
|
91
|
+
font-style: normal;
|
92
|
+
padding-left: 0.5em;
|
93
|
+
content: '::';
|
94
|
+
}
|
95
|
+
dd strong, code {
|
96
|
+
padding: 1px;
|
97
|
+
border: 1px solid #E0DDDA;
|
98
|
+
border-radius: 0.2em;
|
99
|
+
}
|
100
|
+
</style>
|
101
|
+
|
102
|
+
Liquor 2.0 Language Specification
|
103
|
+
=================================
|
104
|
+
|
105
|
+
<p style="text-align: right" markdown="1">*This version of specification is a working draft.*</p>
|
106
|
+
|
107
|
+
Table of Contents
|
108
|
+
-----------------
|
109
|
+
|
110
|
+
* This list will get replaced with a table of contents.
|
111
|
+
{:toc}
|
112
|
+
|
113
|
+
1 Preface
|
114
|
+
---------
|
115
|
+
|
116
|
+
Liquor 2.0 language is developed with several goals in mind.
|
117
|
+
|
118
|
+
* First, it should be secure. There must not be a way to bypass sandbox restrictions.
|
119
|
+
* Second, it should not necessarily be compatible with Liquor 1.0, but should not have vastly different syntax. Liquor 1.0 syntax is easy to understand by frontend developers, and it should remain so.
|
120
|
+
* Third, it should be as much statically verifiable as it is rationally possible. The amount of errors which can be detected only at runtime should be minimal. This will also lead to efficient implementations.
|
121
|
+
* Fourth, it should be elegant and minimalistic.
|
122
|
+
|
123
|
+
This specification is primarily targeted at language implementors.
|
124
|
+
|
125
|
+
2 Overview
|
126
|
+
----------
|
127
|
+
|
128
|
+
### 2.1 Introduction
|
129
|
+
|
130
|
+
Liquor 2.0 language is a templating language for text-based content, e.g. HTML pages. As Liquor is a templating language, it is useless without extension with domain-specific features from a host environment; it is similar to [Lua](http://www.lua.org/) in this aspect.
|
131
|
+
|
132
|
+
Liquor is meant to be statically compiled to another language for efficiency, typically to the one the host environment is executed in. It also provides sandbox restrictions, which allow Liquor code to invoke certain methods on the host objects, but only ones explicitly marked as scriptable.
|
133
|
+
|
134
|
+
Liquor is a statically scoped, weakly and dynamically typed imperative language with lazily evaluated expressions. As it is essentially a domain-specific language for string concatenation, it has an unusual syntax where code is embedded in a text stream, and a final result of executing a Liquor program is always a string. All language constructs are similarly centered around string manipulation.
|
135
|
+
|
136
|
+
Liquor has four basic elements: _blocks_, _tags_, _interpolations_ and _expressions_. All four of these elements can be _executed_ and return a value. _Blocks_, _tags_ and _interpolations_ always return a string value.
|
137
|
+
|
138
|
+
Liquor does not have non-local control flow constructs by itself, such as exceptions and function definitions. This was done intentionally in order to simplify the language.
|
139
|
+
|
140
|
+
Liquor has distinct compile-time and runtime error checking. There are no fatal runtime errors, i.e. a Liquor program always evaluates to some value.
|
141
|
+
|
142
|
+
### 2.2 Types and Values
|
143
|
+
|
144
|
+
Liquor has the following basic types: **Null**, **Boolean**, **Integer**, **String**, **Tuple** and **External**. A value of every type except **External** can be created from within a Liquor program. Values of type **External** can only be returned by the host environment.
|
145
|
+
|
146
|
+
All Liquor values are immutable; once created, a value cannot change.
|
147
|
+
|
148
|
+
There is exactly one value of type **Null**, and it is called _null_.
|
149
|
+
|
150
|
+
There are exactly two values of type **Boolean**, and they are called _true_ and _false_.
|
151
|
+
|
152
|
+
The only values considered "falseful" in a conditional context are _null_ and _false_. Every other value, including **Integer** 0 (zero), is considered "truthful".
|
153
|
+
|
154
|
+
Type **Integer** denotes an integer value of unspecified size. Implementation may impose additional restrictions on the representable range of **Integer** type.
|
155
|
+
|
156
|
+
Type **String** denotes a sequence of [Unicode](http://unicode.org/) codepoints. Note that codepoints are not the same as characters or graphemes; there may exist an implementation-specific way of handling composite characters. See also the relevant [Unicode FAQ entry](http://www.unicode.org/faq/char_combmark.html).
|
157
|
+
|
158
|
+
Type **Tuple** denotes a heteromorphic sequence of values.
|
159
|
+
|
160
|
+
Type **External** denotes an object belonging to the host environment.
|
161
|
+
|
162
|
+
### 2.3 Type Conversion
|
163
|
+
|
164
|
+
Liquor supports exactly one implicit type conversion. In any context where a **String** value is expected, an **Integer** value can be provided. The **Integer** value will then be converted to a corresponding decimal ASCII representation without any leading zeroes.
|
165
|
+
|
166
|
+
### 2.4 Expressions
|
167
|
+
|
168
|
+
Liquor features _expressions_, which can be used to perform computations with values. This section does not define a normative grammar; the full grammar is provided in section [Grammar](#grammar).
|
169
|
+
|
170
|
+
Order of evaluation of Liquor expressions is not defined. As every value is immutable, the value of the entire expression should not depend upon the order of evaluation. Implementation-provided functions must not access or mutate global state; implementation-provided tags may access or mutate global state, but this is highly discouraged.
|
171
|
+
|
172
|
+
#### 2.4.1 Literals
|
173
|
+
|
174
|
+
All Liquor types except **External** can be specified as literals in expressions.
|
175
|
+
|
176
|
+
Identifiers _null_, _true_ and _false_ evaluate to the corresponding values.
|
177
|
+
|
178
|
+
Numeric literals evaluate to a corresponding **Integer** value, and always use base 10. Numeric literals can be specified with any amount of leading zeroes. There are no negative numeric literals.
|
179
|
+
|
180
|
+
String literals evaluate to a corresponding **String** value. String literals can be equivalently specified with single or double quotes. Strings support escaping with backslash, and there are exactly two escape sequences: one inserts a literal backslash, and the other one inserts a literal quote. More specifically, single quoted string supports escape sequences `\\` and `\'`, and double quoted string supports escape sequences `\\` and `\"`. A single backslash followed by any character not specified above is translated to a literal backslash.
|
181
|
+
|
182
|
+
Tuple literals evaluate to a corresponding **Tuple** value. Tuple literals are surrounded by square brackets and delimited with commas; that is, `[ 1, 2, 3 ]` is a tuple literal containing three integer values, one, two and three, in that exact order.
|
183
|
+
|
184
|
+
#### 2.4.2 Operators
|
185
|
+
|
186
|
+
Liquor supports unary and binary infix operators in expressions. All operators are left-associative.
|
187
|
+
|
188
|
+
Liquor operators are listed in order of precedence, from highest to lowest, by the following table:
|
189
|
+
|
190
|
+
1. `[]`, `.`, `()`, `.()`
|
191
|
+
2. unary `-`, `!`
|
192
|
+
3. `*`, `/`, `%`
|
193
|
+
4. `+`, binary `-`
|
194
|
+
5. `==`, `!=`, `<`, `<=`, `>`, `>=`
|
195
|
+
6. `&&`
|
196
|
+
7. `||`
|
197
|
+
|
198
|
+
The following operators are infix and binary: `*`, `/`, `%`, `+`, `-`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`.
|
199
|
+
The following operators are infix and unary: `-`, `!`.
|
200
|
+
|
201
|
+
The operators `[]`, `.`, `()`, `.()` are not infix and are provided in this table only to define precedence rules.
|
202
|
+
|
203
|
+
##### 2.4.2.1 Arithmetic Operators
|
204
|
+
|
205
|
+
Arithmetic operators are `*` (multiplication), `/` (division), `%` (modulo), `+` (plus) and `-` (minus; binary and unary).
|
206
|
+
|
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.
|
208
|
+
|
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.
|
210
|
+
|
211
|
+
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
|
+
|
213
|
+
##### 2.4.2.2 Boolean Operators
|
214
|
+
|
215
|
+
Boolean operators are `!` (not; unary), `&&` (and) and `||` (or).
|
216
|
+
|
217
|
+
All boolean operators, whether unary or binary, convert each argument to type **Boolean** prior to evaluation. The rules of conversion are:
|
218
|
+
|
219
|
+
1. If the value equals _null_ or _false_, it is assumed to be _false_.
|
220
|
+
2. Else, the value is assumed to be _true_.
|
221
|
+
|
222
|
+
All boolean operators return a value of type **Boolean**. Binary boolean operators do not provide any guarantees on order or sequence of evaluation. However, a correct implementation which does not feature functions with side effects will not suffer from this behavior.
|
223
|
+
|
224
|
+
##### 2.4.2.3 Comparison Operators
|
225
|
+
|
226
|
+
Comparison operators are `==` (equals), `!=` (not equals), `<` (less), `<=` (less or equal), `>` (greater) and `>=` (greater or equal).
|
227
|
+
|
228
|
+
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
|
+
|
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.
|
231
|
+
|
232
|
+
##### 2.4.2.4 Indexing Operator
|
233
|
+
|
234
|
+
Indexing operator is `[]`.
|
235
|
+
|
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.
|
237
|
+
|
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.
|
239
|
+
|
240
|
+
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
|
+
|
242
|
+
If the requested element does not exist in the tuple, the indexing operator evaluates to _null_.
|
243
|
+
|
244
|
+
#### 2.4.3 Function Calls
|
245
|
+
|
246
|
+
Identifiers can be bound to functions prior to compilation. Identifiers _null_, _true_ and _false_ cannot be bound to a function.
|
247
|
+
|
248
|
+
Functions are defined in an implementation-specific way. Functions can have zero to one unnamed formal parameters and any amount of named formal parameters. If an unnamed formal parameter is accepted, it is mandatory. Named formal parameters can be either mandatory or optional. Absence of a mandatory formal parameter will result in a compile-time error ([argument error](#argument-error)). Named formal parameter order is irrelevant.
|
249
|
+
|
250
|
+
Function calls have mandatory parentheses, and arguments are whitespace-delimited.
|
251
|
+
|
252
|
+
If a function call includes two named parameters with the same name, a compile-time error ([syntax error](#syntax-error)) is raised.
|
253
|
+
|
254
|
+
If a hypothetical function _substr_ has one unnamed formal parameter and two optional named formal parameters _from_ and _length_, then all of the following expressions are syntactically valid and will not result in a compile-time error: `substr("foobar")`, `substr("foobar" from: 1)`, `substr("foobar" from: 1 length:(5 - 2))`. The following expression, however, is syntactically valid but will result in a compile-time error: `substr(from: 1)`.
|
255
|
+
|
256
|
+
#### 2.4.4 Access Operators
|
257
|
+
|
258
|
+
Access operators are `.` and `.()`.
|
259
|
+
|
260
|
+
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
|
+
|
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.
|
263
|
+
|
264
|
+
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
|
+
|
266
|
+
This evaluation is done in an implementation-defined way. Access operator can evaluate to any type.
|
267
|
+
|
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.
|
269
|
+
|
270
|
+
#### 2.4.5 Variable Access
|
271
|
+
|
272
|
+
Every identifier except _null_, _true_ and _false_ which is not bound to a function name is available to be bound as a variable name. Such identifier would evaluate to a value of the variable.
|
273
|
+
|
274
|
+
Variable definition and scoping will be further discussed in section [Tags](#tags).
|
275
|
+
|
276
|
+
Referencing an undefined variable will result in a compile-time error ([name error](#name-error)).
|
277
|
+
|
278
|
+
#### 2.4.6 Filter Expressions
|
279
|
+
|
280
|
+
Filter expressions are a syntactic sugar for chaining method calls.
|
281
|
+
|
282
|
+
Filter expressions consist of a linear chain of function calls where _n_-th function's return value is passed to _n+1_-th function's unnamed parameter. Named parameters may be specified without parentheses within a corresponding chain element.
|
283
|
+
|
284
|
+
All functions used in a filter expression should accept an unnamed parameter. If this is not the case, a compile-time error ([argument error](#argument-error)) is raised. Semantics of mandatory and optional named parameters are the same as for [regular function calls](#function-calls).
|
285
|
+
|
286
|
+
<p markdown="0">
|
287
|
+
In essence, <code><em>e</em> | <em>f</em> a: 1 | <em>g</em></code> is equivalent to <code><em>g</em>(<em>f</em>(<em>e</em>() a: 1))</code>.
|
288
|
+
</p>
|
289
|
+
|
290
|
+
### 2.5 Blocks
|
291
|
+
|
292
|
+
A block is a chunk of plaintext with _tags_ and _interpolations_ embedded into it. Every Liquor program has at least one block: the toplevel one.
|
293
|
+
|
294
|
+
A block consisting only of plaintext would return its literal value upon execution. Thus, the famous Hello World program would be as follows:
|
295
|
+
|
296
|
+
Hello World!
|
297
|
+
|
298
|
+
This program would evaluate to a string `Hello World!`.
|
299
|
+
|
300
|
+
A block can have other elements embedded into it. When such a block is executed, these elements are executed in lexical order and are replaced with the value returned by the element.
|
301
|
+
|
302
|
+
### 2.6 Interpolations
|
303
|
+
|
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.
|
305
|
+
|
306
|
+
If _expr_ evaluates to a **String**, the interpolation returns it. Otherwise, the interpolation returns an empty string.
|
307
|
+
|
308
|
+
An example of using an interpolation would be:
|
309
|
+
|
310
|
+
The sum of two and three is: {{ 2 + 3 }}
|
311
|
+
|
312
|
+
This program would evaluate to a string `The sum of two and three is: 5`.
|
313
|
+
|
314
|
+
### 2.7 Tags
|
315
|
+
|
316
|
+
A tag is a syntactic construct of form `{% tag expr kw: arg do: %} ... {% end tag %}`. A tag has a syntax similar to a function call, but it can receive blocks of code as argument values and lazily evaluate passed expressions and blocks of code.
|
317
|
+
|
318
|
+
Tags have full control upon parameter evaluation. Tags can require arguments to be of a certain lexical form, e.g. a `for` tag could require its unnamed formal parameter to be a lexical identifier.
|
319
|
+
|
320
|
+
To pass a block of code to a tag, the closing tag delimiter should immediately follow a parameter name. Everything from the closing tag delimiter to the matching opening tag delimiter should be parsed as a block and passed as a value of the corresponding parameter. After the matching opening tag delimiter, the parameter list is continued.
|
321
|
+
|
322
|
+
If a tag <code><em>t</em></code> does not include any embedded blocks, it ends after a first matching closing tag delimiter. Otherwise, the tag ends after a first matching construct of the form <code>{% end <em>t</em> %}</code>.
|
323
|
+
|
324
|
+
Unlike functions, tags can receive multiple named parameters with the same name. Named parameters of tags are a syntactic tool and should be thoroughly verified by the implementation. Specifying incorrect names or order of named parameters may result in a compile-time error ([syntax error](#syntax-error)).
|
325
|
+
|
326
|
+
All of the following are examples of syntactically valid tags:
|
327
|
+
|
328
|
+
{% yield %}
|
329
|
+
|
330
|
+
{% if var > 10 do: %}
|
331
|
+
Var is greater than 10.
|
332
|
+
{% end if %}
|
333
|
+
|
334
|
+
{% for i in: [ 1, 2, 3 ] do: %}
|
335
|
+
Value: {{ i }}
|
336
|
+
{% end for %}
|
337
|
+
|
338
|
+
{% if length(params.test) == 1 then: %}
|
339
|
+
Test has length 1.
|
340
|
+
{% elsif: length(params.test) == 2 then: %}
|
341
|
+
Test has length 2.
|
342
|
+
{% else: %}
|
343
|
+
Test has unidentified length.
|
344
|
+
{% end if %}
|
345
|
+
|
346
|
+
{% capture "buffer" do: %}
|
347
|
+
This text will be printed twice.
|
348
|
+
{% end capture %}
|
349
|
+
{% yield from: "buffer" %}
|
350
|
+
{% yield from: "buffer" %}
|
351
|
+
|
352
|
+
|
353
|
+
3 Grammar
|
354
|
+
---------
|
355
|
+
|
356
|
+
The following Extended Backus-Naur Form grammar is normative. The native character set of Liquor is Unicode, and every character literal specified is an explicit codepoint.
|
357
|
+
|
358
|
+
Statement <code><em>a</em> to <em>b</em></code> is equivalent to codepoint set which includes every codepoint from _a_ to _b_ inclusive. Statement <code><em>a</em> except <em>b</em></code> means that both _a_ and _b_ are tokens which consist of exactly one codepoint, and every character satisfying _a_ and not satisfying in _b_ is accepted. Statement <code>lookahead <em>a</em></code> means that the current token should only be produced if the codepoint immediately following it satisfies _a_.
|
359
|
+
|
360
|
+
Strictly speaking, this grammar lies within _GLR_ domain, but if, as it is usually the case, an implementation has separate lexer and parser, a _LALR(1)_ parser could be used. This will be further explained in section [Blocks](#blocks-1).
|
361
|
+
|
362
|
+
### 3.1 Basic Syntax
|
363
|
+
|
364
|
+
Whitespace
|
365
|
+
: **U+0007** \| **U+000A** \| **U+0020**
|
366
|
+
|
367
|
+
Alpha
|
368
|
+
: **a** to **z** \| **A** to **Z**
|
369
|
+
|
370
|
+
Digit
|
371
|
+
: **0** to **9**
|
372
|
+
|
373
|
+
Any
|
374
|
+
: any Unicode character
|
375
|
+
|
376
|
+
Symbol
|
377
|
+
: _Alpha_ \| **_**
|
378
|
+
|
379
|
+
Identifier
|
380
|
+
: _Symbol_ ( _Symbol_ \| _Digit_ )* lookahead ( _Any_ except **:** )
|
381
|
+
|
382
|
+
Keyword
|
383
|
+
: _Symbol_ ( _Symbol_ \| _Digit_ )* **:**
|
384
|
+
: **=**
|
385
|
+
|
386
|
+
IntegerLiteral
|
387
|
+
: _Digit_+ lookahead ( _Any_ except _Symbol_ )
|
388
|
+
|
389
|
+
StringLiteral
|
390
|
+
: **\"** ( **\\\\** \| **\\\"** \| _Any_ except **\"** )* **\"**
|
391
|
+
: **\'** ( **\\\\** \| **\\\'** \| _Any_ except **\'** )* **\'**
|
392
|
+
|
393
|
+
TupleLiteral
|
394
|
+
: **[** _TupleLiteralContent_ **]**
|
395
|
+
|
396
|
+
TupleLiteralContent
|
397
|
+
: _Expression_ **,** _TupleLiteralContent_
|
398
|
+
: _Expression_
|
399
|
+
: empty
|
400
|
+
|
401
|
+
### 3.2 Expressions
|
402
|
+
|
403
|
+
Operator precedence table is provided in section [Operators](#operators).
|
404
|
+
|
405
|
+
PrimaryExpression
|
406
|
+
: _Identifier_
|
407
|
+
: **(** _Expression_ **)**
|
408
|
+
|
409
|
+
Expression
|
410
|
+
: _IntegerLiteral_
|
411
|
+
: _StringLiteral_
|
412
|
+
: _TupleLiteral_
|
413
|
+
: _Identifier_ _FunctionArguments_
|
414
|
+
: _PrimaryExpression_ **[** _Expression_ **]**
|
415
|
+
: _Expression_ **.** _Identifier_ _FunctionArguments_?
|
416
|
+
: **-** _Expression_
|
417
|
+
: **!** _Expression_
|
418
|
+
: _Expression_ **\*** _Expression_
|
419
|
+
: _Expression_ **/** _Expression_
|
420
|
+
: _Expression_ **%** _Expression_
|
421
|
+
: _Expression_ **+** _Expression_
|
422
|
+
: _Expression_ **-** _Expression_
|
423
|
+
: _Expression_ **==** _Expression_
|
424
|
+
: _Expression_ **!=** _Expression_
|
425
|
+
: _Expression_ **<** _Expression_
|
426
|
+
: _Expression_ **<=** _Expression_
|
427
|
+
: _Expression_ **>** _Expression_
|
428
|
+
: _Expression_ **>=** _Expression_
|
429
|
+
: _Expression_ **&&** _Expression_
|
430
|
+
: _Expression_ **\|\|** _Expression_
|
431
|
+
|
432
|
+
KeywordArguments
|
433
|
+
: ( _Keyword_ _Expression_ )*
|
434
|
+
|
435
|
+
FunctionArguments
|
436
|
+
: **(** _Expression_? _KeywordArguments_ **)**
|
437
|
+
|
438
|
+
FilterChain
|
439
|
+
: _Expression_ **\|** _FilterChainContinuation_
|
440
|
+
|
441
|
+
FilterChainContinuation
|
442
|
+
: _FilterFunctionCall_ **\|** _FilterChainContinuation_
|
443
|
+
: _FilterFunctionCall_
|
444
|
+
|
445
|
+
FilterFunctionCall
|
446
|
+
: _Identifier_ _FunctionKeywordArguments_
|
447
|
+
|
448
|
+
### 3.3 Blocks
|
449
|
+
|
450
|
+
Inside a _Tag_ or _Interpolation_ body any _Whitespace_ is used to separate adjacent tokens, but is otherwise ignored. The cases where naïvely removing _Whitespace_ would cause ambiguity can be determined by watching for `lookahead` clauses.
|
451
|
+
|
452
|
+
The _Tag_, _TagFirstContinuation_ and _EndTag_ production rules deviate from canonical _LR(1)_ grammar structure. To parse these rules correctly, a _LALR(1)_ parser should maintain a stack of tag identifiers and correctly decide on ambiguous reduction of rules _Identifier_ and _EndTag_.
|
453
|
+
|
454
|
+
When the parser follows the second reduction for rule _TagFirstContinuation_, it should push the corresponding _Tag_ _Identifier_ on the top of the tag stack.
|
455
|
+
|
456
|
+
When the parser is about to decide whether it should reduce the sequence satisfying _Identifier_ to _EndTag_ or leave it as is, it should only reduce the sequence to _EndTag_ if the _Identifier_ part of the _EndTag_ rule equals the value at the top of the tag stack. If this is the case, the topmost value is popped from the tag stack.
|
457
|
+
|
458
|
+
Block
|
459
|
+
: _Plaintext_ _Block_
|
460
|
+
: _Interpolation_ _Block_
|
461
|
+
: _Tag_ _Block_
|
462
|
+
: _Comment_ _Block_
|
463
|
+
: empty
|
464
|
+
|
465
|
+
Comment
|
466
|
+
: **{!** ( _Comment_ \| _Any_* )+ **!}**
|
467
|
+
|
468
|
+
Plaintext
|
469
|
+
: ( _Any_ except **{** \| **{** _Any_ except ( **{** \| **%** ) )+
|
470
|
+
|
471
|
+
Interpolation
|
472
|
+
: **{{** ( _Expression_ \| _FilterChain_ ) **}}**
|
473
|
+
|
474
|
+
Tag
|
475
|
+
: **{%** _Identifier_ _Expression_? _KeywordArguments_ _TagFirstContinuation_
|
476
|
+
|
477
|
+
TagFirstContinuation
|
478
|
+
: **%}**
|
479
|
+
: _TagBlock_ _TagNextContinuation_
|
480
|
+
|
481
|
+
TagNextContinuation
|
482
|
+
: _KeywordArguments_ _TagBlock_ _TagNextContinuation_
|
483
|
+
: _EndTag_ **%}**
|
484
|
+
|
485
|
+
TagBlock
|
486
|
+
: _Keyword_ **%}** _Block_ **{%**
|
487
|
+
|
488
|
+
EndTag
|
489
|
+
: **end** **U+0020** _Identifier_ at the top of tag stack
|
490
|
+
|
491
|
+
4 Compile-time Behavior
|
492
|
+
-----------------------
|
493
|
+
|
494
|
+
Liquor compiling process consists of three distinct parts: _parsing_, _scope resolution_ and _translation_. Each stage includes exhaustive error checking; additionally, translation and scope resolution are heavily dependent on the defined tags and their behavior.
|
495
|
+
|
496
|
+
### 4.1 Errors {#compile-time-errors}
|
497
|
+
|
498
|
+
To ease development process, an implementation generally should not stop compilation after encountering an error. As an exception to the general rule, implementation must stop parsing and abandon any intermediate result after encountering a syntax error. Rationale to this behavior is that with Liquor's interleaved structure, successful error recovery after parsing errors is unlikely.
|
499
|
+
|
500
|
+
Every error must carry precise location information: in particular, an error location must feature _line_, _start column_ and _end column_.
|
501
|
+
|
502
|
+
The following algorithm can be used to calculate precise location information for every Unicode character in the source code:
|
503
|
+
|
504
|
+
1. The initial line and column numbers equal 1.
|
505
|
+
2. For each character in the source, in order, perform the following:
|
506
|
+
1. If the character is **U+000A**, increase line number by 1 and set column number to 1.
|
507
|
+
2. If the character is **U+0007**, increase column number by 1 until it equals zero modulo 8. If the column number already equals zero modulo 8, increase it by 8.
|
508
|
+
3. If the character is a combining character, the implementation may recognize this fact and do nothing.
|
509
|
+
4. If nothing of the above applies, increase column number by 1.
|
510
|
+
|
511
|
+
This algorithm, unlike the rest of Liquor, is specified in terms of characters and not codepoints. This means that an implementation must recognize surrogate pairs and compose them into one character.
|
512
|
+
|
513
|
+
#### 4.1.1 Syntax Error
|
514
|
+
|
515
|
+
Syntax error will be signaled upon encountering any of the following conditions:
|
516
|
+
|
517
|
+
1. Parsing failure (section [Grammar](#grammar))
|
518
|
+
2. Duplicate function keyword arguments (section [Function Calls](#function-calls))
|
519
|
+
3. Incorrect tag syntax (section [Tags](#tags))
|
520
|
+
|
521
|
+
Syntax errors must include source location information and point to the exact token which caused the error.
|
522
|
+
|
523
|
+
#### 4.1.2 Argument Error
|
524
|
+
|
525
|
+
Argument error will be signaled upon encountering any of the following conditions:
|
526
|
+
|
527
|
+
1. Absence of a mandatory parameter, or presence of non-accepted parameter (sections [Function Calls](#function-calls), [Tags](#tags))
|
528
|
+
|
529
|
+
Argument errors must include source location information and point either to the exact parameter which caused the error, or to the argument list in case of a missing parameter.
|
530
|
+
|
531
|
+
#### 4.1.3 Name Error
|
532
|
+
|
533
|
+
Name error will be signaled upon encountering any of the following conditions:
|
534
|
+
|
535
|
+
1. Referencing an undefined variable (sections [Variable Access](#variable-access), [Scope Resolution](#scope-resolution))
|
536
|
+
2. Referencing an undefined function (section [Function Calls](#function-calls))
|
537
|
+
3. Encountering an undefined tag (section [Tags](#tags))
|
538
|
+
4. Trying to bind an already bound identifier (section [Scope Resolution](#scope-resolution))
|
539
|
+
|
540
|
+
Name errors must include source location information and point to the exact token which caused the error.
|
541
|
+
|
542
|
+
### 4.2 Scope Resolution
|
543
|
+
|
544
|
+
[Tags](#tags) control every aspect of scope construction and resolution.
|
545
|
+
|
546
|
+
Basically, tags can perform three scope-related actions: _declare_ a variable, _assign_ a variable and create a _nested scope_.
|
547
|
+
|
548
|
+
Declaring a variable binds the identifier to a value. To declare a variable, the identifier should not be bound in the current scope. If this is not the case, a compile-time error ([name error](#name-error)) is raised. If the identifier is bound in an outer scope, it will be rebound in the current scope. Such a binding ceases to exist when the current scope is left.
|
549
|
+
|
550
|
+
Assigning a variable, similarly to accessing, requires the variable to be declared in any of the scopes. Assigning a variable changes its value in the innermost scope.
|
551
|
+
|
552
|
+
Creating a nested scope allows for shadowing of the variables. Tags must only execute contents of the passed blocks in a nested scope. Passed expressions are always executed in the tag's scope. A tag must ensure that every scope it created will be left before the tag will finish executing.
|
553
|
+
|
554
|
+
An implementation should have a way to inject variables into the outermost scope.
|
555
|
+
|
556
|
+
5 Runtime Behavior
|
557
|
+
------------------
|
558
|
+
|
559
|
+
TODO
|
560
|
+
|
561
|
+
6 Builtins
|
562
|
+
----------
|
563
|
+
|
564
|
+
Implementations must implement every builtin tag and function mentioned in this section. Implementations may implement any additional tags, but must not alter behavior of the described ones.
|
565
|
+
|
566
|
+
### 6.1 Required tags {#builtin-tags}
|
567
|
+
|
568
|
+
#### 6.1.1 declare
|
569
|
+
|
570
|
+
Tag _declare_ has one valid syntactic form:
|
571
|
+
|
572
|
+
<pre><code>{% declare <em>var</em> = <em>expr</em> %}</code></pre>
|
573
|
+
|
574
|
+
_Declare_ binds the name _var_ to the result of executing _expr_ in the current scope. If _var_ is already bound in current scope, _declare_ mutates the binding. If _var_ is already bound in an outer scope, _declare_ creates a new binding in the current scope.
|
575
|
+
|
576
|
+
The _declare_ tag itself evaluates to an empty string.
|
577
|
+
|
578
|
+
#### 6.1.2 assign
|
579
|
+
|
580
|
+
Tag _assign_ has one valid syntactic form:
|
581
|
+
|
582
|
+
<pre><code>{% assign <em>var</em> = <em>expr</em> %}</code></pre>
|
583
|
+
|
584
|
+
_Assign_ binds the name _var_ to the result of executing _expr_ in the current scope. If _var_ is already bound, _assign_ mutates the binding.
|
585
|
+
|
586
|
+
The _assign_ tag itself evaluates to an empty string.
|
587
|
+
|
588
|
+
#### 6.1.3 for
|
589
|
+
|
590
|
+
Tag _for_ has two valid syntactic forms:
|
591
|
+
|
592
|
+
<pre><code>{% for <em>var</em> in: <em>list</em> do: %}
|
593
|
+
<em>code</em>
|
594
|
+
{% end for %}</code></pre>
|
595
|
+
|
596
|
+
<pre><code>{% for <em>var</em> from: <em>lower-limit</em> to: <em>upper-limit</em> do: %}
|
597
|
+
<em>code</em>
|
598
|
+
{% end for %}</code></pre>
|
599
|
+
|
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.
|
601
|
+
|
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.
|
603
|
+
|
604
|
+
The _for_ tag returns the concatenation of the values its _code_ has evaluated to.
|
605
|
+
|
606
|
+
#### 6.1.4 if
|
607
|
+
|
608
|
+
Tag _if_ has one valid syntactic form:
|
609
|
+
|
610
|
+
<pre><code>{% if <em>cond-1</em> then: %}
|
611
|
+
<em>code-1</em>
|
612
|
+
<em>[</em>{% elsif: <em>cond-2</em> then: %}
|
613
|
+
<em>code-2</em><em>] ...</em>
|
614
|
+
<em>[</em>{% else: %}
|
615
|
+
<em>code-else</em><em>]</em>
|
616
|
+
{% end if %}
|
617
|
+
</code></pre>
|
618
|
+
|
619
|
+
This tag can optionally have any amount of _elsif_ clauses and only one _else_ clause.
|
620
|
+
|
621
|
+
The _if_ tag sequentally evaluates each passed condition _cond-1_, _cond-2_, ... until a [truthful](#boolean-operators) value is computed. Then, it executes the corresponding code. If none of the conditions evaluate to a truthful value, the tag executes _code-else_ if it exists.
|
622
|
+
|
623
|
+
The _if_ tag returns the result of evaluating the corresponding code block, or an empty string if none of the blocks were executed.
|
624
|
+
|
625
|
+
#### 6.1.5 unless
|
626
|
+
|
627
|
+
Tag _unless_ has one valid syntactic form:
|
628
|
+
|
629
|
+
<pre><code>{% unless <em>cond</em> then: %}
|
630
|
+
<em>code</em>
|
631
|
+
{% end unless %}</code></pre>
|
632
|
+
|
633
|
+
The _unless_ tag evaluates _cond_. Unless it yields a [truthful](#boolean-operators), _code_ is also evaluated.
|
634
|
+
|
635
|
+
The _unless_ tag returns the result of evaluating _code_, or an empty string.
|
636
|
+
|
637
|
+
#### 6.1.6 capture
|
638
|
+
|
639
|
+
Tag _capture_ has one valid syntactic form:
|
640
|
+
|
641
|
+
<pre><code>{% capture <em>var</em> = %}
|
642
|
+
<em>code</em>
|
643
|
+
{% end capture %}</code></pre>
|
644
|
+
|
645
|
+
The _capture_ tag evaluates _code_ and binds the name _var_ to the result. If _var_ is already bound, _capture_ mutates the binding.
|
646
|
+
|
647
|
+
The _capture_ tag returns an empty string.
|
648
|
+
|
649
|
+
#### 6.1.7 content_for
|
650
|
+
|
651
|
+
Tag _content_for_ has one valid syntactic form:
|
652
|
+
|
653
|
+
<pre><code>{% content_for <em>"handle"</em> capture: %}
|
654
|
+
<em>code</em>
|
655
|
+
{% end content_for %}</code></pre>
|
656
|
+
|
657
|
+
The _content_for_ tag accepts a **String** handle as an immediate value. It evaluates _code_ and assigns the result to the handle _handle_, which must be stored in an implementation-specific way.
|
658
|
+
|
659
|
+
The _content_for_ tag returns an empty string.
|
660
|
+
|
661
|
+
See also notes on [Layout implementation](#appendix-layouts).
|
662
|
+
|
663
|
+
#### 6.1.8 yield
|
664
|
+
|
665
|
+
Tag _yield_ has three valid syntactic forms:
|
666
|
+
|
667
|
+
<pre><code>{% yield %}</code></pre>
|
668
|
+
|
669
|
+
In this form, the _yield_ tag evaluates to the content of inner template.
|
670
|
+
|
671
|
+
<pre><code>{% yield <em>"handle"</em> %}</code></pre>
|
672
|
+
<pre><code>{% yield <em>"handle"</em> if_none: %}
|
673
|
+
<em>code</em>
|
674
|
+
{% end yield %}</code></pre>
|
675
|
+
|
676
|
+
The _yield_ tag accepts a **String** _handle_ as an immediate value. If a string with handle _handle_ was captured previously with [{% content_for %}](#content_for), then _yield_ returns that string. If there is no captured string with that handle, _yield_ either returns the result of evaluating _if_none_ block if it exists, or an empty string.
|
677
|
+
|
678
|
+
See also notes on [Layout implementation](#appendix-layouts).
|
679
|
+
|
680
|
+
#### 6.1.9 include
|
681
|
+
|
682
|
+
Tag _include_ has one valid syntactic form:
|
683
|
+
|
684
|
+
<pre><code>{% include <em>"partial_name"</em> %}</code></pre>
|
685
|
+
|
686
|
+
The _include_ tag accepts a **String** _partial_name_ as an immediate value. It lexically includes the code of partial template _partial_name_ in a newly created scope.
|
687
|
+
|
688
|
+
The _include_ tag must not allow infinite recursion to happen. If such a condition is encountered, a compile-time error ([syntax error](#syntax-error)) is signaled.
|
689
|
+
|
690
|
+
See also notes on [Layout implementation](#appendix-layouts).
|
691
|
+
|
692
|
+
### 6.2 Functions
|
693
|
+
|
694
|
+
TODO
|
695
|
+
|
696
|
+
## Appendix A: Layouts {#appendix-layouts}
|
697
|
+
|
698
|
+
TODO
|