3 #[cfg(feature="partial_legacy")]
6 whitespace
= _{ [" "]|["\t"]|["\n"]|["\r"] }
8 raw_text
= @{ ( ["\\{{{{"]? ~ ["\\{{"]? ~ !["{{"] ~ any )+ }
9 raw_block_text
= @{ ( !["{{{{"] ~ any )* }
11 // Note: this is not full and strict json literal definition, just for tokenize string,
12 // array and object types which may contains whitespace. We will use a real json parser
13 // for real json processing
14 literal
= { string_literal
|
21 null_literal
= { ["null"] }
22 boolean_literal
= { ["true"]|["false"] }
23 number_literal
= @{ ["-"]? ~ ['0'..'9']+ ~ ["."]? ~ ['0'..'9']* ~ (["E"] ~ ["-"]? ~ ['0'..'9']+)? }
24 string_literal
= @{ ["\""] ~ (!["\""] ~ (["\\\""] | any))* ~ ["\""] }
25 array_literal
= { ["["] ~ literal? ~ ([","] ~ literal)* ~ ["]"] }
26 object_literal
= { ["{"] ~ (string_literal ~ [":"] ~ literal)? ~ ([","] ~ string_literal ~ [":"] ~ literal)* ~ ["}
"] }
29 symbol_char
= _{ ['a'..'z']|['A'..'Z']|['0'..'9']|["_"]|["."]|["@"]|["$"]|["-"]|["<"]|[">"] }
30 path_char
= _{ ["/"] }
32 identifier
= @{ symbol_char ~ ( symbol_char | path_char )* }
33 reference
= @{ identifier ~ (["["] ~ (string_literal|['0'..'9']+) ~ ["]"])* ~ ["-"]* ~ reference* }
34 name
= _{ subexpression | reference }
36 param
= { !["as"] ~ (literal | reference | subexpression) }
37 hash
= { identifier ~ ["="] ~ param }
38 block_param
= { ["as"] ~ ["|"] ~ identifier ~ identifier? ~ ["|"]}
39 exp_line
= _{ identifier ~ (hash|param)* ~ block_param?}
40 partial_exp_line
= _{ name ~ (hash|param)* }
42 subexpression
= { ["("] ~ name ~ (hash|param)* ~ [")"] }
44 pre_whitespace_omitter
= { ["~"] }
45 pro_whitespace_omitter
= { ["~"] }
48 expression
= { !escape
~ !invert_tag
~ ["{{"] ~ pre_whitespace_omitter?
~ name
~
49 pro_whitespace_omitter?
~ ["}}"] }
51 html_expression
= { !escape
~ ["{{{"] ~ pre_whitespace_omitter?
~ name
~
52 pro_whitespace_omitter?
~ ["}}}"] }
54 helper_expression
= { !escape
~ !invert_tag
~ ["{{"] ~ pre_whitespace_omitter?
~ exp_line
~
55 pro_whitespace_omitter?
~ ["}}"] }
57 directive_expression
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["*"] ~ exp_line
~
58 pro_whitespace_omitter?
~ ["}}"] }
59 partial_expression
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ [">"] ~ partial_exp_line
~
60 pro_whitespace_omitter?
~ ["}}"] }
62 invert_tag_item
= { ["else"]|["^"] }
63 invert_tag
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ invert_tag_item
64 ~ pro_whitespace_omitter?
~ ["}}"]}
66 helper_block_start
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["#"] ~ exp_line
~
67 pro_whitespace_omitter?
~ ["}}"] }
68 helper_block_end
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
69 pro_whitespace_omitter?
~ ["}}"] }
70 helper_block
= _
{ helper_block_start
~ template
~
71 (invert_tag
~ template
)?
~
74 directive_block_start
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["#"] ~ ["*"] ~ exp_line
~
75 pro_whitespace_omitter?
~ ["}}"] }
76 directive_block_end
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
77 pro_whitespace_omitter?
~ ["}}"] }
78 directive_block
= _
{ directive_block_start
~ template
~
81 partial_block_start
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["#"] ~ [">"] ~ partial_exp_line
~
82 pro_whitespace_omitter?
~ ["}}"] }
83 partial_block_end
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
84 pro_whitespace_omitter?
~ ["}}"] }
85 partial_block
= _{ partial_block_start ~ template ~ partial_block_end }
87 raw_block_start
= { !escape
~ ["{{{{"] ~ pre_whitespace_omitter?
~ exp_line
~
88 pro_whitespace_omitter?
~ ["}}}}"] }
89 raw_block_end
= { !escape
~ ["{{{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
90 pro_whitespace_omitter?
~ ["}}}}"] }
91 raw_block
= _{ raw_block_start ~ raw_block_text ~ raw_block_end }
93 hbs_comment
= { !escape ~ ["{{!"] ~ (!["}
}"] ~ any)* ~ ["}}"] }
103 directive_expression |
107 parameter = _{ param ~ eoi }
108 handlebars = _{ template ~ eoi }
111 path_ident = _{ ['a'..'z']|['A'..'Z']|['0'..'9']|["_"]|["@"]|["$"]|["<"]|[">"]|["-"]}
112 path_id = { path_ident+ }
113 path_num_id = { ['0'..'9']+ }
114 path_raw_id = { (path_ident|["/"])* }
115 path_sep = _{ ["/"] | ["."] }
117 path_var = { path_id }
118 path_key = { ["["] ~ (["\""]|["'"])? ~ path_raw_id ~ (["\""]|["'"])? ~ ["]"] }
119 path_idx = { ["["] ~ path_num_id ~ ["]"]}
120 path_item = _{ path_up|path_var }
121 path = _{ ["./"]? ~ path_item ~ ((path_sep ~ path_item) | (path_sep? ~ (path_key | path_idx)))* ~ eoi }
125 #[cfg(not(feature="partial_legacy
"))]
128 whitespace = _{ [" "]|["\t"]|["\n"]|["\r"] }
130 raw_text = @{ ( ["\\{{{{"]? ~ ["\\{{"]? ~ !["{{"] ~ any )+ }
131 raw_block_text = @{ ( !["{{{{"] ~ any )* }
133 // Note: this is not full and strict json literal definition, just for tokenize string,
134 // array and object types which may contains whitespace. We will use a real json parser
135 // for real json processing
136 literal = { string_literal |
143 null_literal = { ["null"] }
144 boolean_literal = { ["true"]|["false"] }
145 number_literal = @{ ["-"]? ~ ['0'..'9']+ ~ ["."]? ~ ['0'..'9']* ~ (["E"] ~ ["-"]? ~ ['0'..'9']+)? }
146 string_literal = @{ ["\""] ~ (!["\""] ~ (["\\\""] | any))* ~ ["\""] }
147 array_literal = { ["["] ~ literal? ~ ([","] ~ literal)* ~ ["]"] }
148 object_literal = { ["{"] ~ (string_literal ~ [":"] ~ literal)? ~ ([","] ~ string_literal ~ [":"] ~ literal)* ~ ["}"] }
150 // FIXME: a[0], a["b]
151 symbol_char
= _{ ['a'..'z']|['A'..'Z']|['0'..'9']|["_"]|["."]|["@"]|["$"]|["-"] }
152 path_char
= _{ ["/"] }
154 identifier
= @{ symbol_char ~ ( symbol_char | path_char )* }
155 reference
= @{ identifier ~ (["["] ~ (string_literal|['0'..'9']+) ~ ["]"])* ~ ["-"]* ~ reference* }
156 name
= _{ subexpression | reference }
158 param
= { !["as"] ~ (literal | reference | subexpression) }
159 hash
= { identifier ~ ["="] ~ param }
160 block_param
= { ["as"] ~ ["|"] ~ identifier ~ identifier? ~ ["|"]}
161 exp_line
= _{ identifier ~ (hash|param)* ~ block_param?}
162 partial_exp_line
= _{ name ~ (hash|param)* }
164 subexpression
= { ["("] ~ name ~ (hash|param)* ~ [")"] }
166 pre_whitespace_omitter
= { ["~"] }
167 pro_whitespace_omitter
= { ["~"] }
170 expression
= { !escape
~ !invert_tag
~ ["{{"] ~ pre_whitespace_omitter?
~ name
~
171 pro_whitespace_omitter?
~ ["}}"] }
173 html_expression
= { !escape
~ ["{{{"] ~ pre_whitespace_omitter?
~ name
~
174 pro_whitespace_omitter?
~ ["}}}"] }
176 helper_expression
= { !invert_tag
~ ["{{"] ~ pre_whitespace_omitter?
~ exp_line
~
177 pro_whitespace_omitter?
~ ["}}"] }
179 directive_expression
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["*"] ~ exp_line
~
180 pro_whitespace_omitter?
~ ["}}"] }
181 partial_expression
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ [">"] ~ partial_exp_line
~
182 pro_whitespace_omitter?
~ ["}}"] }
183 invert_tag_item
= { ["else"]|["^"] }
184 invert_tag
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ invert_tag_item
185 ~ pro_whitespace_omitter?
~ ["}}"]}
186 helper_block_start
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["#"] ~ exp_line
~
187 pro_whitespace_omitter?
~ ["}}"] }
188 helper_block_end
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
189 pro_whitespace_omitter?
~ ["}}"] }
190 helper_block
= _
{ helper_block_start
~ template
~
191 (invert_tag
~ template
)?
~
194 directive_block_start
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["#"] ~ ["*"] ~ exp_line
~
195 pro_whitespace_omitter?
~ ["}}"] }
196 directive_block_end
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
197 pro_whitespace_omitter?
~ ["}}"] }
198 directive_block
= _
{ directive_block_start
~ template
~
199 directive_block_end
}
201 partial_block_start
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["#"] ~ [">"] ~ partial_exp_line
~
202 pro_whitespace_omitter?
~ ["}}"] }
203 partial_block_end
= { !escape
~ ["{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
204 pro_whitespace_omitter?
~ ["}}"] }
205 partial_block
= _{ partial_block_start ~ template ~ partial_block_end }
207 raw_block_start
= { !escape
~ ["{{{{"] ~ pre_whitespace_omitter?
~ exp_line
~
208 pro_whitespace_omitter?
~ ["}}}}"] }
209 raw_block_end
= { !escape
~ ["{{{{"] ~ pre_whitespace_omitter?
~ ["/"] ~ name
~
210 pro_whitespace_omitter?
~ ["}}}}"] }
211 raw_block
= _{ raw_block_start ~ raw_block_text ~ raw_block_end }
213 hbs_comment
= { !escape ~ ["{{!"] ~ (!["}
}"] ~ any)* ~ ["}}"] }
223 directive_expression |
229 parameter = _{ param ~ eoi }
230 handlebars = _{ template ~ eoi }
233 path_ident = _{ ['a'..'z']|['A'..'Z']|['0'..'9']|["_"]|["@"]|["$"]|["<"]|[">"]|["-"]}
234 path_id = { path_ident+ }
235 path_num_id = { ['0'..'9']+ }
236 path_raw_id = { (path_ident|["/"])* }
237 path_sep = _{ ["/"] | ["."] }
239 path_var = { path_id }
240 path_key = { ["["] ~ (["\""]|["'"])? ~ path_raw_id ~ (["\""]|["'"])? ~ ["]"] }
241 path_idx = { ["["] ~ path_num_id ~ ["]"]}
242 path_item = _{ path_up|path_var }
243 path = _{ ["./"]? ~ path_item ~ ((path_sep ~ path_item) | (path_sep? ~ (path_key | path_idx)))* ~ eoi }
249 let s = vec!["<h1
> helloworld
</h1
> ",
251 "hello
\\{{#if world}
}nice
\\{{/if}
}",
252 "hello
\\{{{{raw}
}}}hello
\\{{{{/raw}
}}}"];
254 let mut rdp = Rdp::new(StringInput::new(i));
255 assert!(rdp.raw_text());
261 fn test_raw_block_text() {
262 let mut rdp = Rdp::new(StringInput::new("<h1
> {{hello}
} </h1
>"));
263 assert!(rdp.raw_block_text());
268 fn test_reference() {
280 let mut rdp = Rdp::new(StringInput::new(i));
281 assert!(rdp.reference());
288 let s = vec!["if", "(abc
)"];
290 let mut rdp = Rdp::new(StringInput::new(i));
298 let s = vec!["hello
", "\"json literal
\""];
300 let mut rdp = Rdp::new(StringInput::new(i));
301 assert!(rdp.param());
308 let s = vec!["hello
=world
", "hello
=\"world
\"", "hello
=(world
)", "hello
=(world
0)"];
310 let mut rdp = Rdp::new(StringInput::new(i));
317 fn test_json_literal() {
318 let s = vec!["\"json string
\"",
323 "{\"hello\": \"world\"}
",
325 "{\"a\":1, \"b\":2 }
"];
327 let mut rdp = Rdp::new(StringInput::new(i));
328 assert!(rdp.literal());
335 let s = vec!["{{! hello }
}"];
337 let mut rdp = Rdp::new(StringInput::new(i));
338 assert!(rdp.hbs_comment());
344 fn test_subexpression() {
345 let s = vec!["(sub
)", "(sub
0)", "(sub a
=1)"];
347 let mut rdp = Rdp::new(StringInput::new(i));
348 assert!(rdp.subexpression());
354 fn test_expression() {
355 let s = vec!["{{exp}
}", "{{(exp)}
}", "{{this.name}
}", "{{this.[0].name}
}"];
357 let mut rdp = Rdp::new(StringInput::new(i));
358 assert!(rdp.expression());
364 fn test_helper_expression() {
365 let s = vec!["{{exp 1}
}",
366 "{{exp \"literal\"}
}",
375 "{{exp key=(sub 0)}
}"];
377 let mut rdp = Rdp::new(StringInput::new(i));
378 assert!(rdp.helper_expression());
385 fn test_identifier_with_dash() {
386 let s = vec!["{{exp-foo}
}"];
388 let mut rdp = Rdp::new(StringInput::new(i));
389 assert!(rdp.expression());
396 fn test_html_expression() {
397 let s = vec!["{{{html}
}}", "{{{(html)}
}}", "{{{(html)}
}}"];
399 let mut rdp = Rdp::new(StringInput::new(i));
400 assert!(rdp.html_expression());
406 fn test_helper_start() {
407 let s = vec!["{{#if hello}
}",
409 "{{#if hello=world}
}",
410 "{{#if hello hello=world}
}",
415 "{{#each people as |person|}
}",
416 "{{#each-obj obj as |key val|}
}"];
418 let mut rdp = Rdp::new(StringInput::new(i));
419 assert!(rdp.helper_block_start());
425 fn test_helper_end() {
426 let s = vec!["{{/if}
}", "{{~/if}
}", "{{~/if ~}
}", "{{/if ~}
}"];
428 let mut rdp = Rdp::new(StringInput::new(i));
429 assert!(rdp.helper_block_end());
435 fn test_helper_block() {
436 let s = vec!["{{#if hello}
}hello{{/if}
}",
437 "{{#if true}
}hello{{/if}
}",
438 "{{#if nice ok=1}
}hello{{/if}
}",
439 "{{#if}
}hello{{else}
}world{{/if}
}",
440 "{{#if}
}hello{{^}
}world{{/if}
}",
441 "{{#if}
}{{#if}
}hello{{/if}
}{{/if}
}",
442 "{{#if}
}hello{{~else}
}world{{/if}
}",
443 "{{#if}
}hello{{else~}
}world{{/if}
}",
444 "{{#if}
}hello{{~^~}
}world{{/if}
}",
447 let mut rdp = Rdp::new(StringInput::new(i));
448 assert!(rdp.helper_block());
454 fn test_raw_block() {
455 let s = vec!["{{{{if hello}
}}}good {{hello}
}{{{{/if}
}}}",
456 "{{{{if hello}
}}}{{#if nice}
}{{/if}
}{{{{/if}
}}}"];
458 let mut rdp = Rdp::new(StringInput::new(i));
459 assert!(rdp.raw_block());
465 fn test_block_param() {
466 let s = vec!["as |person
|", "as |key val
|"];
468 let mut rdp = Rdp::new(StringInput::new(i));
469 assert!(rdp.block_param());
482 "a
[\"bbc
\"]/b
/c
/../d
",
484 "./this
[0][1]/this
/../a
",
488 let mut rdp = Rdp::new(StringInput::new(i));
495 fn test_directive_expression() {
496 let s = vec!["{{* ssh}
}", "{{~* ssh}
}"];
498 let mut rdp = Rdp::new(StringInput::new(i));
499 assert!(rdp.directive_expression());
505 fn test_directive_block() {
506 let s = vec!["{{#* inline}
}something{{/inline}
}",
507 "{{~#* inline}
}hello{{/inline}
}",
508 "{{#* inline \"partialname\"}
}something{{/inline}
}"];
510 let mut rdp = Rdp::new(StringInput::new(i));
511 assert!(rdp.directive_block());
517 fn test_partial_expression() {
518 let s = vec!["{{> hello}
}", "{{> (hello)}
}", "{{~> hello a}
}", "{{> hello a=1}
}"];
520 let mut rdp = Rdp::new(StringInput::new(i));
521 assert!(rdp.partial_expression());
527 fn test_partial_block() {
528 let s = vec!["{{#> hello}
}nice{{/hello}
}"];
530 let mut rdp = Rdp::new(StringInput::new(i));
531 assert!(rdp.partial_block());