Allow trimming of whitespace
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
587cfdea53
commit
59f92e31fe
8 changed files with 243 additions and 9 deletions
|
|
@ -264,16 +264,32 @@ fn parse_interpolation<'input>(
|
|||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||
let expr_parser = resume_after_cut(
|
||||
parse_value_expression,
|
||||
repeat_till(0.., any, peek(TokenKind::RightDelim)).map(|((), _)| ()),
|
||||
repeat_till(
|
||||
0..,
|
||||
any,
|
||||
peek(preceded(
|
||||
opt(TokenKind::TrimWhitespace),
|
||||
TokenKind::RightDelim,
|
||||
)),
|
||||
)
|
||||
.map(|((), _)| ()),
|
||||
)
|
||||
.with_taken()
|
||||
.map(|(expr, taken)| expr.unwrap_or(TemplateAstExpr::Invalid(taken)));
|
||||
let (prev_whitespace, _left, _wants_output, (expression, _right, post_whitespace)) = (
|
||||
let (
|
||||
prev_whitespace,
|
||||
_left,
|
||||
left_trim,
|
||||
_wants_output,
|
||||
(expression, right_trim, _right, post_whitespace),
|
||||
) = (
|
||||
opt(TokenKind::Whitespace),
|
||||
TokenKind::LeftDelim,
|
||||
opt(TokenKind::TrimWhitespace),
|
||||
TokenKind::WantsOutput,
|
||||
cut_err((
|
||||
surrounded(ws, expr_parser).map(Box::new),
|
||||
opt(TokenKind::TrimWhitespace),
|
||||
TokenKind::RightDelim,
|
||||
opt(TokenKind::Whitespace),
|
||||
)),
|
||||
|
|
@ -281,9 +297,17 @@ fn parse_interpolation<'input>(
|
|||
.parse_next(input)?;
|
||||
|
||||
Ok(TemplateAstExpr::Interpolation {
|
||||
prev_whitespace_content: prev_whitespace,
|
||||
prev_whitespace_content: if left_trim.is_some() {
|
||||
None
|
||||
} else {
|
||||
prev_whitespace
|
||||
},
|
||||
expression,
|
||||
post_whitespace_content: post_whitespace,
|
||||
post_whitespace_content: if right_trim.is_some() {
|
||||
None
|
||||
} else {
|
||||
post_whitespace
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -439,19 +463,35 @@ where
|
|||
(
|
||||
opt(TokenKind::Whitespace),
|
||||
TokenKind::LeftDelim,
|
||||
opt(TokenKind::TrimWhitespace),
|
||||
not(TokenKind::WantsOutput),
|
||||
(
|
||||
surrounded(ws, expr_parser.map(Box::new)),
|
||||
opt(TokenKind::TrimWhitespace),
|
||||
TokenKind::RightDelim,
|
||||
opt(TokenKind::Whitespace),
|
||||
),
|
||||
)
|
||||
.map(
|
||||
|(prev_whitespace, _left, _not_token, (expression, _right, post_whitespace))| {
|
||||
|(
|
||||
prev_whitespace,
|
||||
_left,
|
||||
left_trim,
|
||||
_not_token,
|
||||
(expression, right_trim, _right, post_whitespace),
|
||||
)| {
|
||||
TemplateAstExpr::Block {
|
||||
prev_whitespace_content: prev_whitespace,
|
||||
prev_whitespace_content: if left_trim.is_some() {
|
||||
None
|
||||
} else {
|
||||
prev_whitespace
|
||||
},
|
||||
expression,
|
||||
post_whitespace_content: post_whitespace,
|
||||
post_whitespace_content: if right_trim.is_some() {
|
||||
None
|
||||
} else {
|
||||
post_whitespace
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -736,4 +776,15 @@ mod tests {
|
|||
|
||||
insta::assert_debug_snapshot!(ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_trim_whitespace() {
|
||||
let input = "{{ if foo -}} foo {{- else if bar -}} bar {{- end }}";
|
||||
|
||||
let parsed = crate::parser::parse(input.into()).unwrap();
|
||||
|
||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
||||
|
||||
insta::assert_debug_snapshot!(ast);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
source: src/ast/mod.rs
|
||||
expression: ast
|
||||
---
|
||||
TemplateAst {
|
||||
root: [
|
||||
ConditionalChain {
|
||||
chain: [
|
||||
Block {
|
||||
prev_whitespace_content: None,
|
||||
expression: IfConditional {
|
||||
expression: VariableAccess(
|
||||
[Ident]"foo" (6..9),
|
||||
),
|
||||
},
|
||||
post_whitespace_content: None,
|
||||
},
|
||||
ConditionalContent {
|
||||
content: [
|
||||
StaticContent(
|
||||
[Content]"foo" (14..17),
|
||||
),
|
||||
],
|
||||
},
|
||||
Block {
|
||||
prev_whitespace_content: None,
|
||||
expression: ElseConditional {
|
||||
expression: Some(
|
||||
VariableAccess(
|
||||
[Ident]"bar" (30..33),
|
||||
),
|
||||
),
|
||||
},
|
||||
post_whitespace_content: None,
|
||||
},
|
||||
ConditionalContent {
|
||||
content: [
|
||||
StaticContent(
|
||||
[Content]"bar" (38..41),
|
||||
),
|
||||
],
|
||||
},
|
||||
Block {
|
||||
prev_whitespace_content: None,
|
||||
expression: EndBlock,
|
||||
post_whitespace_content: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -53,11 +53,12 @@ pub fn execute(
|
|||
Instruction::PushScope { inherit_parent: _ } => todo!(),
|
||||
Instruction::Abort => return Err(EvaluationError::ExplicitAbort),
|
||||
Instruction::JumpIfNotTrue { emit_slot, jump } => {
|
||||
let jump = if *jump == 0 { 1 } else { *jump };
|
||||
let dont_jump = scopes.get(emit_slot).unwrap().as_bool().unwrap();
|
||||
if dont_jump {
|
||||
// We are done
|
||||
} else {
|
||||
let (new_ip, overflow) = ip.overflowing_add_signed(*jump);
|
||||
let (new_ip, overflow) = ip.overflowing_add_signed(jump);
|
||||
|
||||
if overflow {
|
||||
return Err(EvaluationError::InstructionPointerOverflow);
|
||||
|
|
@ -68,7 +69,8 @@ pub fn execute(
|
|||
}
|
||||
}
|
||||
Instruction::Jump { jump } => {
|
||||
let (new_ip, overflow) = ip.overflowing_add_signed(*jump);
|
||||
let jump = if *jump == 0 { 1 } else { *jump };
|
||||
let (new_ip, overflow) = ip.overflowing_add_signed(jump);
|
||||
|
||||
if overflow {
|
||||
return Err(EvaluationError::InstructionPointerOverflow);
|
||||
|
|
|
|||
34
tests/cases/1-parsed@trim_whitespace.snap
Normal file
34
tests/cases/1-parsed@trim_whitespace.snap
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: tests/file_tests.rs
|
||||
expression: parsed
|
||||
input_file: tests/cases/trim_whitespace.nomo
|
||||
---
|
||||
ParsedTemplate {
|
||||
tokens: [
|
||||
[LeftDelim]"{{" (0..2),
|
||||
[Whitespace]" " (2..3),
|
||||
[ConditionalIf]"if" (3..5),
|
||||
[Whitespace]" " (5..6),
|
||||
[Ident]"test" (6..10),
|
||||
[Whitespace]" " (10..11),
|
||||
[TrimWhitespace]"-" (11..12),
|
||||
[RightDelim]"}}" (12..14),
|
||||
[Whitespace]"\n " (14..19),
|
||||
[Content]"Hello" (19..24),
|
||||
[Whitespace]" " (24..25),
|
||||
[LeftDelim]"{{" (25..27),
|
||||
[WantsOutput]"=" (27..28),
|
||||
[Whitespace]" " (28..29),
|
||||
[Ident]"stuff" (29..34),
|
||||
[Whitespace]" " (34..35),
|
||||
[TrimWhitespace]"-" (35..36),
|
||||
[RightDelim]"}}" (36..38),
|
||||
[Whitespace]"\n" (38..39),
|
||||
[LeftDelim]"{{" (39..41),
|
||||
[TrimWhitespace]"-" (41..42),
|
||||
[Whitespace]" " (42..43),
|
||||
[End]"end" (43..46),
|
||||
[Whitespace]" " (46..47),
|
||||
[RightDelim]"}}" (47..49),
|
||||
],
|
||||
}
|
||||
43
tests/cases/2-ast@trim_whitespace.snap
Normal file
43
tests/cases/2-ast@trim_whitespace.snap
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
source: tests/file_tests.rs
|
||||
expression: ast
|
||||
input_file: tests/cases/trim_whitespace.nomo
|
||||
---
|
||||
TemplateAst {
|
||||
root: [
|
||||
ConditionalChain {
|
||||
chain: [
|
||||
Block {
|
||||
prev_whitespace_content: None,
|
||||
expression: IfConditional {
|
||||
expression: VariableAccess(
|
||||
[Ident]"test" (6..10),
|
||||
),
|
||||
},
|
||||
post_whitespace_content: None,
|
||||
},
|
||||
ConditionalContent {
|
||||
content: [
|
||||
StaticContent(
|
||||
[Content]"Hello" (19..24),
|
||||
),
|
||||
Interpolation {
|
||||
prev_whitespace_content: Some(
|
||||
[Whitespace]" " (24..25),
|
||||
),
|
||||
expression: VariableAccess(
|
||||
[Ident]"stuff" (29..34),
|
||||
),
|
||||
post_whitespace_content: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
Block {
|
||||
prev_whitespace_content: None,
|
||||
expression: EndBlock,
|
||||
post_whitespace_content: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
39
tests/cases/3-instructions@trim_whitespace.snap
Normal file
39
tests/cases/3-instructions@trim_whitespace.snap
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
source: tests/file_tests.rs
|
||||
expression: emit
|
||||
input_file: tests/cases/trim_whitespace.nomo
|
||||
---
|
||||
[
|
||||
LoadFromContextToSlot {
|
||||
name: "test" (6..10),
|
||||
slot: VariableSlot {
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
JumpIfNotTrue {
|
||||
emit_slot: VariableSlot {
|
||||
index: 0,
|
||||
},
|
||||
jump: 5,
|
||||
},
|
||||
AppendContent {
|
||||
content: "Hello" (19..24),
|
||||
},
|
||||
AppendContent {
|
||||
content: " " (24..25),
|
||||
},
|
||||
LoadFromContextToSlot {
|
||||
name: "stuff" (29..34),
|
||||
slot: VariableSlot {
|
||||
index: 1,
|
||||
},
|
||||
},
|
||||
EmitFromSlot {
|
||||
slot: VariableSlot {
|
||||
index: 1,
|
||||
},
|
||||
},
|
||||
Jump {
|
||||
jump: 0,
|
||||
},
|
||||
]
|
||||
6
tests/cases/4-output@trim_whitespace.snap
Normal file
6
tests/cases/4-output@trim_whitespace.snap
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: tests/file_tests.rs
|
||||
expression: output
|
||||
input_file: tests/cases/trim_whitespace.nomo
|
||||
---
|
||||
"Hello Hemera"
|
||||
8
tests/cases/trim_whitespace.nomo
Normal file
8
tests/cases/trim_whitespace.nomo
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"test": true,
|
||||
"stuff": "Hemera"
|
||||
}
|
||||
---
|
||||
{{ if test -}}
|
||||
Hello {{= stuff -}}
|
||||
{{- end }}
|
||||
Loading…
Add table
Add a link
Reference in a new issue