Add parsing for conditionals (cont.)
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
974086a877
commit
8afc2d1bde
29 changed files with 994 additions and 746 deletions
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -63,11 +63,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772593411,
|
"lastModified": 1772939270,
|
||||||
"narHash": "sha256-47WOnCSyOL6AghZiMIJaTLWM359DHe3be9R1cNCdGUE=",
|
"narHash": "sha256-HbxD5DJAKxzo0G8on5wdY+OZNiUWt3FTvGmXmVEmg7g=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "a741b36b77440f5db15fcf2ab6d7d592d2f9ee8f",
|
"rev": "bb93f191a07c0165992ed6d0b4197ee5c7e6e641",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.93.1"
|
channel = "1.94.0"
|
||||||
|
|
|
||||||
327
src/ast/mod.rs
327
src/ast/mod.rs
|
|
@ -6,18 +6,22 @@ use winnow::combinator::cut_err;
|
||||||
use winnow::combinator::delimited;
|
use winnow::combinator::delimited;
|
||||||
use winnow::combinator::not;
|
use winnow::combinator::not;
|
||||||
use winnow::combinator::opt;
|
use winnow::combinator::opt;
|
||||||
|
use winnow::combinator::peek;
|
||||||
use winnow::combinator::preceded;
|
use winnow::combinator::preceded;
|
||||||
use winnow::combinator::repeat;
|
use winnow::combinator::repeat;
|
||||||
use winnow::combinator::repeat_till;
|
use winnow::combinator::repeat_till;
|
||||||
|
use winnow::combinator::trace;
|
||||||
use winnow::error::AddContext;
|
use winnow::error::AddContext;
|
||||||
use winnow::error::FromRecoverableError;
|
use winnow::error::FromRecoverableError;
|
||||||
use winnow::error::ModalError;
|
use winnow::error::ModalError;
|
||||||
use winnow::error::ParserError;
|
use winnow::error::ParserError;
|
||||||
|
use winnow::stream::Offset;
|
||||||
use winnow::stream::Recoverable;
|
use winnow::stream::Recoverable;
|
||||||
use winnow::stream::Stream;
|
use winnow::stream::Stream;
|
||||||
use winnow::stream::TokenSlice;
|
use winnow::stream::TokenSlice;
|
||||||
use winnow::token::any;
|
use winnow::token::any;
|
||||||
|
|
||||||
|
use crate::SourceSpan;
|
||||||
use crate::parser::TemplateToken;
|
use crate::parser::TemplateToken;
|
||||||
use crate::parser::TokenKind;
|
use crate::parser::TokenKind;
|
||||||
use crate::resume_after_cut;
|
use crate::resume_after_cut;
|
||||||
|
|
@ -83,6 +87,29 @@ impl FromRecoverableError<Input<'_>, AstError> for AstError {
|
||||||
input: &Input,
|
input: &Input,
|
||||||
mut e: AstError,
|
mut e: AstError,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
e.span = e.span.or_else(|| {
|
||||||
|
let offset = input.offset_from(token_start);
|
||||||
|
|
||||||
|
let mut tokens = input
|
||||||
|
.previous_tokens()
|
||||||
|
.take(offset)
|
||||||
|
.filter(|t| t.kind() != TokenKind::Whitespace);
|
||||||
|
let last = tokens.next();
|
||||||
|
let first = tokens.last();
|
||||||
|
match (last, first) {
|
||||||
|
(None, None) => None,
|
||||||
|
(None, Some(single)) | (Some(single), None) => Some(SourceSpan {
|
||||||
|
range: single.source().get_range(),
|
||||||
|
}),
|
||||||
|
(Some(last), Some(first)) => {
|
||||||
|
let start = first.source().get_range().start;
|
||||||
|
let end = last.source().get_range().end;
|
||||||
|
|
||||||
|
Some(SourceSpan { range: start..end })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +144,9 @@ impl ParserError<Input<'_>> for AstError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub struct AstFailure {}
|
pub struct AstFailure {
|
||||||
|
errors: Vec<AstError>,
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for AstFailure {
|
impl std::fmt::Display for AstFailure {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
@ -126,8 +155,40 @@ impl std::fmt::Display for AstFailure {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstFailure {
|
impl AstFailure {
|
||||||
fn from_errors(_errors: Vec<AstError>, _input: &[TemplateToken]) -> AstFailure {
|
fn from_errors(errors: Vec<AstError>) -> AstFailure {
|
||||||
AstFailure {}
|
AstFailure { errors }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_report(&self, source: &str) -> String {
|
||||||
|
let reports = self
|
||||||
|
.errors
|
||||||
|
.iter()
|
||||||
|
.map(|error| {
|
||||||
|
annotate_snippets::Level::ERROR
|
||||||
|
.primary_title(
|
||||||
|
error
|
||||||
|
.message
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("An error occurred while producing an Ast"),
|
||||||
|
)
|
||||||
|
.element(
|
||||||
|
annotate_snippets::Snippet::source(source).annotation(
|
||||||
|
annotate_snippets::AnnotationKind::Primary
|
||||||
|
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.elements(
|
||||||
|
error
|
||||||
|
.help
|
||||||
|
.as_ref()
|
||||||
|
.map(|help| annotate_snippets::Level::HELP.message(help)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let renderer = annotate_snippets::Renderer::styled()
|
||||||
|
.decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
||||||
|
renderer.render(&reports)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +210,7 @@ pub fn parse(input: &[TemplateToken]) -> Result<TemplateAst<'_>, AstFailure> {
|
||||||
{
|
{
|
||||||
Ok(TemplateAst { root: val })
|
Ok(TemplateAst { root: val })
|
||||||
} else {
|
} else {
|
||||||
Err(AstFailure::from_errors(errors, input))
|
Err(AstFailure::from_errors(errors))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,12 +218,6 @@ pub fn parse(input: &[TemplateToken]) -> Result<TemplateAst<'_>, AstFailure> {
|
||||||
pub enum TemplateAstExpr<'input> {
|
pub enum TemplateAstExpr<'input> {
|
||||||
StaticContent(TemplateToken),
|
StaticContent(TemplateToken),
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: Option<TemplateToken>,
|
|
||||||
wants_output: TemplateToken,
|
|
||||||
expression: Box<TemplateAstExpr<'input>>,
|
|
||||||
post_whitespace_content: Option<TemplateToken>,
|
|
||||||
},
|
|
||||||
Action {
|
|
||||||
prev_whitespace_content: Option<TemplateToken>,
|
prev_whitespace_content: Option<TemplateToken>,
|
||||||
expression: Box<TemplateAstExpr<'input>>,
|
expression: Box<TemplateAstExpr<'input>>,
|
||||||
post_whitespace_content: Option<TemplateToken>,
|
post_whitespace_content: Option<TemplateToken>,
|
||||||
|
|
@ -172,7 +227,7 @@ pub enum TemplateAstExpr<'input> {
|
||||||
chain: Vec<TemplateAstExpr<'input>>,
|
chain: Vec<TemplateAstExpr<'input>>,
|
||||||
},
|
},
|
||||||
IfConditional {
|
IfConditional {
|
||||||
expression: Box<TemplateAstExpr<'input>>,
|
if_block: Box<TemplateAstExpr<'input>>,
|
||||||
content: Vec<TemplateAstExpr<'input>>,
|
content: Vec<TemplateAstExpr<'input>>,
|
||||||
end_block: Box<TemplateAstExpr<'input>>,
|
end_block: Box<TemplateAstExpr<'input>>,
|
||||||
},
|
},
|
||||||
|
|
@ -193,8 +248,11 @@ fn parse_asts<'input>(input: &mut Input<'input>) -> Result<Vec<TemplateAstExpr<'
|
||||||
}
|
}
|
||||||
fn parse_ast<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
fn parse_ast<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
alt((
|
alt((
|
||||||
|
trace(
|
||||||
|
"content",
|
||||||
TokenKind::Content.map(TemplateAstExpr::StaticContent),
|
TokenKind::Content.map(TemplateAstExpr::StaticContent),
|
||||||
parse_interpolation,
|
),
|
||||||
|
trace("interpolation", parse_interpolation),
|
||||||
parse_action,
|
parse_action,
|
||||||
))
|
))
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
|
|
@ -205,16 +263,16 @@ fn parse_interpolation<'input>(
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
let expr_parser = resume_after_cut(
|
let expr_parser = resume_after_cut(
|
||||||
parse_value_expression,
|
parse_value_expression,
|
||||||
repeat_till(1.., any, TokenKind::RightDelim).map(|((), _)| ()),
|
repeat_till(0.., any, peek(TokenKind::RightDelim)).map(|((), _)| ()),
|
||||||
)
|
)
|
||||||
.with_taken()
|
.with_taken()
|
||||||
.map(|(expr, taken)| expr.unwrap_or(TemplateAstExpr::Invalid(taken)));
|
.map(|(expr, taken)| expr.unwrap_or(TemplateAstExpr::Invalid(taken)));
|
||||||
let (prev_whitespace, _left, wants_output, (expression, _right, post_whitespace)) = (
|
let (prev_whitespace, _left, _wants_output, (expression, _right, post_whitespace)) = (
|
||||||
opt(TokenKind::Whitespace),
|
opt(TokenKind::Whitespace),
|
||||||
TokenKind::LeftDelim,
|
TokenKind::LeftDelim,
|
||||||
TokenKind::WantsOutput,
|
TokenKind::WantsOutput,
|
||||||
cut_err((
|
cut_err((
|
||||||
delimited(ignore_ws, expr_parser, ignore_ws).map(Box::new),
|
surrounded(ws, expr_parser).map(Box::new),
|
||||||
TokenKind::RightDelim,
|
TokenKind::RightDelim,
|
||||||
opt(TokenKind::Whitespace),
|
opt(TokenKind::Whitespace),
|
||||||
)),
|
)),
|
||||||
|
|
@ -223,52 +281,99 @@ fn parse_interpolation<'input>(
|
||||||
|
|
||||||
Ok(TemplateAstExpr::Interpolation {
|
Ok(TemplateAstExpr::Interpolation {
|
||||||
prev_whitespace_content: prev_whitespace,
|
prev_whitespace_content: prev_whitespace,
|
||||||
wants_output,
|
|
||||||
expression,
|
expression,
|
||||||
post_whitespace_content: post_whitespace,
|
post_whitespace_content: post_whitespace,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_value_expression<'input>(
|
|
||||||
input: &mut Input<'input>,
|
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
|
||||||
alt((parse_variable_access,)).parse_next(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_action<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
fn parse_action<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
alt((parse_conditional_chain,)).parse_next(input)
|
trace(
|
||||||
|
"action",
|
||||||
|
alt((
|
||||||
|
parse_conditional_chain,
|
||||||
|
(parse_block(
|
||||||
|
cut_err(not(repeat_till(
|
||||||
|
0..,
|
||||||
|
any,
|
||||||
|
peek((ws, TokenKind::RightDelim)),
|
||||||
|
)
|
||||||
|
.map(|((), _)| ())))
|
||||||
|
.context(
|
||||||
|
AstError::ctx()
|
||||||
|
.msg("Standlone action block")
|
||||||
|
.help("If you want to output this expression, add a '=' to the block"),
|
||||||
|
)
|
||||||
|
.take()
|
||||||
|
.map(TemplateAstExpr::Invalid),
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_conditional_chain<'input>(
|
fn parse_conditional_chain<'input>(
|
||||||
input: &mut Input<'input>,
|
input: &mut Input<'input>,
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
let if_expression = parse_conditional.parse_next(input)?;
|
trace("conditional_chain", |input: &mut Input<'input>| {
|
||||||
|
let if_block = parse_conditional.map(Box::new).parse_next(input)?;
|
||||||
let mut chain = vec![];
|
let mut chain = vec![];
|
||||||
|
|
||||||
let (content, end_block): (Vec<_>, _) =
|
let (content, end_block): (Vec<_>, _) =
|
||||||
repeat_till(1.., parse_ast, parse_end).parse_next(input)?;
|
repeat_till(0.., parse_ast, parse_end.map(Box::new)).parse_next(input)?;
|
||||||
|
|
||||||
chain.push(TemplateAstExpr::IfConditional {
|
chain.push(TemplateAstExpr::IfConditional {
|
||||||
expression: Box::new(if_expression),
|
if_block,
|
||||||
content,
|
content,
|
||||||
end_block: Box::new(end_block),
|
end_block,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(TemplateAstExpr::ConditionalChain { chain })
|
Ok(TemplateAstExpr::ConditionalChain { chain })
|
||||||
|
})
|
||||||
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_conditional<'input>(
|
fn parse_conditional<'input>(
|
||||||
input: &mut Input<'input>,
|
input: &mut Input<'input>,
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
trace(
|
||||||
|
"conditional",
|
||||||
parse_block(preceded(
|
parse_block(preceded(
|
||||||
TokenKind::ConditionalIf,
|
TokenKind::ConditionalIf,
|
||||||
surrounded(ignore_ws, parse_value_expression),
|
cut_err(
|
||||||
))
|
surrounded(ws, parse_value_expression)
|
||||||
|
.context(AstError::ctx().msg("Expected an expression after 'if'")),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
)
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_end<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
fn parse_end<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
parse_block(TokenKind::End.value(TemplateAstExpr::EndBlock)).parse_next(input)
|
trace(
|
||||||
|
"end",
|
||||||
|
parse_block(
|
||||||
|
TokenKind::End
|
||||||
|
.value(TemplateAstExpr::EndBlock)
|
||||||
|
.context(AstError::ctx().msg("Expected an end block here")),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.parse_next(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_value_expression<'input>(
|
||||||
|
input: &mut Input<'input>,
|
||||||
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
trace("value_expression", alt((parse_variable_access,))).parse_next(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_variable_access<'input>(
|
||||||
|
input: &mut Input<'input>,
|
||||||
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
trace(
|
||||||
|
"variable_access",
|
||||||
|
TokenKind::Ident.map(TemplateAstExpr::VariableAccess),
|
||||||
|
)
|
||||||
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block<'input, ParseNext>(
|
fn parse_block<'input, ParseNext>(
|
||||||
|
|
@ -279,7 +384,7 @@ where
|
||||||
{
|
{
|
||||||
let expr_parser = resume_after_cut(
|
let expr_parser = resume_after_cut(
|
||||||
parser,
|
parser,
|
||||||
repeat_till(1.., any, TokenKind::RightDelim).map(|((), _)| ()),
|
repeat_till(0.., any, peek(TokenKind::RightDelim)).map(|((), _)| ()),
|
||||||
)
|
)
|
||||||
.with_taken()
|
.with_taken()
|
||||||
.map(|(expr, taken)| expr.unwrap_or(TemplateAstExpr::Invalid(taken)));
|
.map(|(expr, taken)| expr.unwrap_or(TemplateAstExpr::Invalid(taken)));
|
||||||
|
|
@ -288,11 +393,11 @@ where
|
||||||
opt(TokenKind::Whitespace),
|
opt(TokenKind::Whitespace),
|
||||||
TokenKind::LeftDelim,
|
TokenKind::LeftDelim,
|
||||||
not(TokenKind::WantsOutput),
|
not(TokenKind::WantsOutput),
|
||||||
cut_err((
|
(
|
||||||
delimited(ignore_ws, expr_parser.map(Box::new), ignore_ws),
|
surrounded(ws, expr_parser.map(Box::new)),
|
||||||
TokenKind::RightDelim,
|
TokenKind::RightDelim,
|
||||||
opt(TokenKind::Whitespace),
|
opt(TokenKind::Whitespace),
|
||||||
)),
|
),
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
|(prev_whitespace, _left, _not_token, (expression, _right, post_whitespace))| {
|
|(prev_whitespace, _left, _not_token, (expression, _right, post_whitespace))| {
|
||||||
|
|
@ -305,15 +410,7 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_variable_access<'input>(
|
fn ws<'input>(input: &mut Input<'input>) -> Result<(), AstError> {
|
||||||
input: &mut Input<'input>,
|
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
|
||||||
TokenKind::Ident
|
|
||||||
.map(TemplateAstExpr::VariableAccess)
|
|
||||||
.parse_next(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ignore_ws<'input>(input: &mut Input<'input>) -> Result<(), AstError> {
|
|
||||||
repeat(.., TokenKind::Whitespace).parse_next(input)
|
repeat(.., TokenKind::Whitespace).parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +430,19 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use winnow::Parser;
|
||||||
|
use winnow::combinator::alt;
|
||||||
|
use winnow::combinator::fail;
|
||||||
|
use winnow::stream::TokenSlice;
|
||||||
|
|
||||||
|
use crate::ast::AstError;
|
||||||
|
use crate::ast::AstFailure;
|
||||||
|
use crate::ast::TemplateAst;
|
||||||
|
use crate::ast::TemplateAstExpr;
|
||||||
use crate::ast::parse;
|
use crate::ast::parse;
|
||||||
|
use crate::ast::parse_block;
|
||||||
|
use crate::ast::parse_end;
|
||||||
|
use crate::parser::TokenKind;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_only_content() {
|
fn check_only_content() {
|
||||||
|
|
@ -347,10 +456,7 @@ mod tests {
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
root: [
|
root: [
|
||||||
StaticContent(
|
StaticContent(
|
||||||
TemplateToken {
|
"Hello World" (0..11),
|
||||||
kind: Content,
|
|
||||||
source: "Hello World",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
@ -369,27 +475,14 @@ mod tests {
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
root: [
|
root: [
|
||||||
StaticContent(
|
StaticContent(
|
||||||
TemplateToken {
|
"Hello" (0..5),
|
||||||
kind: Content,
|
|
||||||
source: "Hello",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: Some(
|
prev_whitespace_content: Some(
|
||||||
TemplateToken {
|
" " (5..6),
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"world" (10..15),
|
||||||
kind: Ident,
|
|
||||||
source: "world",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: None,
|
post_whitespace_content: None,
|
||||||
},
|
},
|
||||||
|
|
@ -412,35 +505,23 @@ mod tests {
|
||||||
ConditionalChain {
|
ConditionalChain {
|
||||||
chain: [
|
chain: [
|
||||||
IfConditional {
|
IfConditional {
|
||||||
expression: Block {
|
if_block: Block {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"foo" (6..9),
|
||||||
kind: Ident,
|
|
||||||
source: "foo",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
" " (12..13),
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
content: [
|
content: [
|
||||||
StaticContent(
|
StaticContent(
|
||||||
TemplateToken {
|
"Hiii" (13..17),
|
||||||
kind: Content,
|
|
||||||
source: "Hiii",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
end_block: Block {
|
end_block: Block {
|
||||||
prev_whitespace_content: Some(
|
prev_whitespace_content: Some(
|
||||||
TemplateToken {
|
" " (17..18),
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
expression: EndBlock,
|
expression: EndBlock,
|
||||||
post_whitespace_content: None,
|
post_whitespace_content: None,
|
||||||
|
|
@ -453,18 +534,98 @@ mod tests {
|
||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn panic_pretty<'a>(
|
||||||
|
input: &'_ str,
|
||||||
|
tokens: Result<TemplateAst<'a>, AstFailure>,
|
||||||
|
) -> TemplateAst<'a> {
|
||||||
|
match tokens {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(failure) => {
|
||||||
|
panic!("{}", failure.to_report(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_invalid_action() {
|
||||||
|
let input = r#"{{ value }}
|
||||||
|
{{ value }}
|
||||||
|
{{ value }}
|
||||||
|
{{ value }}
|
||||||
|
{{ value }}"#;
|
||||||
|
|
||||||
|
let parsed = crate::parser::parse(input.into()).unwrap();
|
||||||
|
|
||||||
|
let ast = parse(parsed.tokens()).unwrap_err();
|
||||||
|
|
||||||
|
insta::assert_snapshot!(ast.to_report(input));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_nested_simple_if() {
|
fn check_nested_simple_if() {
|
||||||
let input = r#"{{ if foo }}
|
let input = r#"{{ if foo }}
|
||||||
{{ if bar }}
|
{{ if bar }}
|
||||||
Hiii
|
Hiii
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}"#;
|
{{ end }}
|
||||||
|
|
||||||
|
{{= value }}
|
||||||
|
"#;
|
||||||
|
|
||||||
let parsed = crate::parser::parse(input.into()).unwrap();
|
let parsed = crate::parser::parse(input.into()).unwrap();
|
||||||
|
|
||||||
let ast = parse(parsed.tokens()).unwrap();
|
insta::assert_debug_snapshot!("simple_if_tokens", parsed);
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
let ast = panic_pretty(input, parse(parsed.tokens()));
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!("simple_if_ast", ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_parsing_block() {
|
||||||
|
use winnow::RecoverableParser;
|
||||||
|
|
||||||
|
let input = "{{ foo }}";
|
||||||
|
|
||||||
|
let parsed = crate::parser::parse(input.into()).unwrap();
|
||||||
|
|
||||||
|
let result = alt((
|
||||||
|
parse_end,
|
||||||
|
parse_block(
|
||||||
|
(TokenKind::Ident.void(), fail::<_, (), _>)
|
||||||
|
.void()
|
||||||
|
.context(AstError::ctx().msg("No ident allowed"))
|
||||||
|
.take()
|
||||||
|
.map(TemplateAstExpr::Invalid),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.recoverable_parse(TokenSlice::new(parsed.tokens()));
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!(result, @r#"
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"{{" (0..2),
|
||||||
|
" " (2..3),
|
||||||
|
"foo" (3..6),
|
||||||
|
" " (6..7),
|
||||||
|
"}}" (7..9),
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AstError {
|
||||||
|
message: Some(
|
||||||
|
"No ident allowed",
|
||||||
|
),
|
||||||
|
help: None,
|
||||||
|
span: Some(
|
||||||
|
SourceSpan {
|
||||||
|
range: 0..6,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
is_fatal: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
source: src/ast/mod.rs
|
||||||
|
expression: ast.to_report(input)
|
||||||
|
---
|
||||||
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
|
[1m[94m ╭▸ [0m
|
||||||
|
[1m[94m1[0m [1m[94m│[0m {{ value }}
|
||||||
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m│[0m
|
||||||
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
|
[1m[94m ╭▸ [0m
|
||||||
|
[1m[94m2[0m [1m[94m│[0m {{ value }}
|
||||||
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
|
[1m[94m ╭▸ [0m
|
||||||
|
[1m[94m3[0m [1m[94m│[0m {{ value }}
|
||||||
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
|
[1m[94m ╭▸ [0m
|
||||||
|
[1m[94m4[0m [1m[94m│[0m {{ value }}
|
||||||
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
|
[1m[94m ╭▸ [0m
|
||||||
|
[1m[94m5[0m [1m[94m│[0m {{ value }}
|
||||||
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
|
@ -1,84 +1,152 @@
|
||||||
---
|
---
|
||||||
source: src/ast/mod.rs
|
source: src/ast/mod.rs
|
||||||
expression: ast
|
expression: parsed
|
||||||
---
|
---
|
||||||
TemplateAst {
|
ParsedTemplate {
|
||||||
root: [
|
tokens: [
|
||||||
ConditionalChain {
|
|
||||||
chain: [
|
|
||||||
IfConditional {
|
|
||||||
expression: Block {
|
|
||||||
prev_whitespace_content: None,
|
|
||||||
expression: VariableAccess(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Ident,
|
kind: LeftDelim,
|
||||||
source: "foo",
|
source: "{{" (0..2),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
post_whitespace_content: Some(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Whitespace,
|
kind: Whitespace,
|
||||||
source: "
|
source: " " (2..3),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
),
|
|
||||||
},
|
|
||||||
content: [
|
|
||||||
ConditionalChain {
|
|
||||||
chain: [
|
|
||||||
IfConditional {
|
|
||||||
expression: Block {
|
|
||||||
prev_whitespace_content: None,
|
|
||||||
expression: VariableAccess(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Ident,
|
kind: ConditionalIf,
|
||||||
source: "bar",
|
source: "if" (3..5),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
post_whitespace_content: Some(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Whitespace,
|
kind: Whitespace,
|
||||||
source: "
|
source: " " (5..6),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
),
|
TemplateToken {
|
||||||
|
kind: Ident,
|
||||||
|
source: "foo" (6..9),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (9..10),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: RightDelim,
|
||||||
|
source: "}}" (10..12),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: "\n " (12..25),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: LeftDelim,
|
||||||
|
source: "{{" (25..27),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (27..28),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: ConditionalIf,
|
||||||
|
source: "if" (28..30),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (30..31),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Ident,
|
||||||
|
source: "bar" (31..34),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (34..35),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: RightDelim,
|
||||||
|
source: "}}" (35..37),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: "\n " (37..54),
|
||||||
},
|
},
|
||||||
content: [
|
|
||||||
StaticContent(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Content,
|
kind: Content,
|
||||||
source: "Hiii",
|
source: "Hiii" (54..58),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
],
|
|
||||||
end_block: Block {
|
|
||||||
prev_whitespace_content: Some(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Whitespace,
|
kind: Whitespace,
|
||||||
source: "
|
source: "\n " (58..71),
|
||||||
",
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: LeftDelim,
|
||||||
|
source: "{{" (71..73),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
expression: EndBlock,
|
|
||||||
post_whitespace_content: Some(
|
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: Whitespace,
|
kind: Whitespace,
|
||||||
source: "
|
source: " " (73..74),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
),
|
TemplateToken {
|
||||||
|
kind: End,
|
||||||
|
source: "end" (74..77),
|
||||||
},
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (77..78),
|
||||||
},
|
},
|
||||||
],
|
TemplateToken {
|
||||||
|
kind: RightDelim,
|
||||||
|
source: "}}" (78..80),
|
||||||
},
|
},
|
||||||
],
|
TemplateToken {
|
||||||
end_block: Block {
|
kind: Whitespace,
|
||||||
prev_whitespace_content: None,
|
source: "\n " (80..89),
|
||||||
expression: EndBlock,
|
|
||||||
post_whitespace_content: None,
|
|
||||||
},
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: LeftDelim,
|
||||||
|
source: "{{" (89..91),
|
||||||
},
|
},
|
||||||
],
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (91..92),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: End,
|
||||||
|
source: "end" (92..95),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (95..96),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: RightDelim,
|
||||||
|
source: "}}" (96..98),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: "\n\n " (98..108),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: LeftDelim,
|
||||||
|
source: "{{" (108..110),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (110..111),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Ident,
|
||||||
|
source: "value" (111..116),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: " " (116..117),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: RightDelim,
|
||||||
|
source: "}}" (117..119),
|
||||||
|
},
|
||||||
|
TemplateToken {
|
||||||
|
kind: Whitespace,
|
||||||
|
source: "\n " (119..128),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
70
src/ast/snapshots/nomo__ast__tests__simple_if_ast.snap
Normal file
70
src/ast/snapshots/nomo__ast__tests__simple_if_ast.snap
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
---
|
||||||
|
source: src/ast/mod.rs
|
||||||
|
expression: ast
|
||||||
|
---
|
||||||
|
TemplateAst {
|
||||||
|
root: [
|
||||||
|
ConditionalChain {
|
||||||
|
chain: [
|
||||||
|
IfConditional {
|
||||||
|
if_block: Block {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: VariableAccess(
|
||||||
|
"foo" (6..9),
|
||||||
|
),
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n " (12..25),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
ConditionalChain {
|
||||||
|
chain: [
|
||||||
|
IfConditional {
|
||||||
|
if_block: Block {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: VariableAccess(
|
||||||
|
"bar" (31..34),
|
||||||
|
),
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n " (37..54),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
StaticContent(
|
||||||
|
"Hiii" (54..58),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
end_block: Block {
|
||||||
|
prev_whitespace_content: Some(
|
||||||
|
"\n " (58..71),
|
||||||
|
),
|
||||||
|
expression: EndBlock,
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n " (80..89),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
end_block: Block {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: EndBlock,
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n\n " (98..108),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Interpolation {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: VariableAccess(
|
||||||
|
"value" (112..117),
|
||||||
|
),
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n " (120..129),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
45
src/ast/snapshots/nomo__ast__tests__simple_if_tokens.snap
Normal file
45
src/ast/snapshots/nomo__ast__tests__simple_if_tokens.snap
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
source: src/ast/mod.rs
|
||||||
|
expression: parsed
|
||||||
|
---
|
||||||
|
ParsedTemplate {
|
||||||
|
tokens: [
|
||||||
|
"{{" (0..2),
|
||||||
|
" " (2..3),
|
||||||
|
"if" (3..5),
|
||||||
|
" " (5..6),
|
||||||
|
"foo" (6..9),
|
||||||
|
" " (9..10),
|
||||||
|
"}}" (10..12),
|
||||||
|
"\n " (12..25),
|
||||||
|
"{{" (25..27),
|
||||||
|
" " (27..28),
|
||||||
|
"if" (28..30),
|
||||||
|
" " (30..31),
|
||||||
|
"bar" (31..34),
|
||||||
|
" " (34..35),
|
||||||
|
"}}" (35..37),
|
||||||
|
"\n " (37..54),
|
||||||
|
"Hiii" (54..58),
|
||||||
|
"\n " (58..71),
|
||||||
|
"{{" (71..73),
|
||||||
|
" " (73..74),
|
||||||
|
"end" (74..77),
|
||||||
|
" " (77..78),
|
||||||
|
"}}" (78..80),
|
||||||
|
"\n " (80..89),
|
||||||
|
"{{" (89..91),
|
||||||
|
" " (91..92),
|
||||||
|
"end" (92..95),
|
||||||
|
" " (95..96),
|
||||||
|
"}}" (96..98),
|
||||||
|
"\n\n " (98..108),
|
||||||
|
"{{" (108..110),
|
||||||
|
"=" (110..111),
|
||||||
|
" " (111..112),
|
||||||
|
"value" (112..117),
|
||||||
|
" " (117..118),
|
||||||
|
"}}" (118..120),
|
||||||
|
"\n " (120..129),
|
||||||
|
],
|
||||||
|
}
|
||||||
159
src/emit/mod.rs
159
src/emit/mod.rs
|
|
@ -24,11 +24,24 @@ pub struct VariableSlot {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
AppendContent { content: NomoInput },
|
AppendContent {
|
||||||
LoadFromContextToSlot { name: NomoInput, slot: VariableSlot },
|
content: NomoInput,
|
||||||
EmitFromSlot { slot: VariableSlot },
|
},
|
||||||
PushScope { inherit_parent: bool },
|
LoadFromContextToSlot {
|
||||||
|
name: NomoInput,
|
||||||
|
slot: VariableSlot,
|
||||||
|
},
|
||||||
|
EmitFromSlot {
|
||||||
|
slot: VariableSlot,
|
||||||
|
},
|
||||||
|
PushScope {
|
||||||
|
inherit_parent: bool,
|
||||||
|
},
|
||||||
Abort,
|
Abort,
|
||||||
|
JumpIfNotTrue {
|
||||||
|
emit_slot: VariableSlot,
|
||||||
|
jump: isize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> Vec<Instruction> {
|
pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> Vec<Instruction> {
|
||||||
|
|
@ -55,53 +68,110 @@ fn emit_ast_expr(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
TemplateAstExpr::Interpolation {
|
TemplateAstExpr::Interpolation {
|
||||||
prev_whitespace_content: prev_whitespace,
|
prev_whitespace_content,
|
||||||
wants_output,
|
|
||||||
expression,
|
expression,
|
||||||
post_whitespace_content: post_whitespace,
|
post_whitespace_content,
|
||||||
} => {
|
} => {
|
||||||
if let Some(ws) = prev_whitespace {
|
if let Some(ws) = prev_whitespace_content {
|
||||||
eval.push(Instruction::AppendContent {
|
eval.push(Instruction::AppendContent {
|
||||||
content: ws.source().clone(),
|
content: ws.source().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let emit_slot = machine.reserve_slot();
|
let emit_slot = machine.reserve_slot();
|
||||||
emit_expr(machine, eval, emit_slot, expression);
|
emit_expr_load(machine, eval, emit_slot, expression);
|
||||||
eval.push(Instruction::EmitFromSlot { slot: emit_slot });
|
eval.push(Instruction::EmitFromSlot { slot: emit_slot });
|
||||||
|
|
||||||
if let Some(ws) = post_whitespace {
|
if let Some(ws) = post_whitespace_content {
|
||||||
eval.push(Instruction::AppendContent {
|
eval.push(Instruction::AppendContent {
|
||||||
content: ws.source().clone(),
|
content: ws.source().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TemplateAstExpr::Invalid { .. } | TemplateAstExpr::VariableAccess { .. } => {
|
TemplateAstExpr::ConditionalChain { chain } => {
|
||||||
eval.push(Instruction::Abort)
|
let mut chain = chain.iter();
|
||||||
}
|
let Some(TemplateAstExpr::IfConditional {
|
||||||
TemplateAstExpr::ConditionalChain { chain } => todo!(),
|
if_block: expression,
|
||||||
TemplateAstExpr::ElseConditional { expression } => todo!(),
|
|
||||||
TemplateAstExpr::Action {
|
|
||||||
prev_whitespace_content,
|
|
||||||
expression,
|
|
||||||
post_whitespace_content,
|
|
||||||
} => todo!(),
|
|
||||||
TemplateAstExpr::EndBlock => todo!(),
|
|
||||||
TemplateAstExpr::Block {
|
|
||||||
prev_whitespace_content,
|
|
||||||
expression,
|
|
||||||
post_whitespace_content,
|
|
||||||
} => todo!(),
|
|
||||||
TemplateAstExpr::IfConditional {
|
|
||||||
expression,
|
|
||||||
content,
|
content,
|
||||||
end_block,
|
end_block,
|
||||||
} => todo!(),
|
}) = chain.next()
|
||||||
|
else {
|
||||||
|
unreachable!("First element in conditional chain should be an IfConditional");
|
||||||
|
};
|
||||||
|
|
||||||
|
let TemplateAstExpr::Block {
|
||||||
|
prev_whitespace_content,
|
||||||
|
expression,
|
||||||
|
post_whitespace_content,
|
||||||
|
} = expression.as_ref()
|
||||||
|
else {
|
||||||
|
unreachable!("The end of an IfConditional must be a Block");
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ws) = prev_whitespace_content {
|
||||||
|
eval.push(Instruction::AppendContent {
|
||||||
|
content: ws.source().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let emit_slot = machine.reserve_slot();
|
||||||
|
emit_expr_load(machine, eval, emit_slot, expression);
|
||||||
|
|
||||||
|
let index = eval.len();
|
||||||
|
eval.push(Instruction::JumpIfNotTrue {
|
||||||
|
emit_slot,
|
||||||
|
jump: isize::MAX,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(ws) = post_whitespace_content {
|
||||||
|
eval.push(Instruction::AppendContent {
|
||||||
|
content: ws.source().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for ast in content {
|
||||||
|
emit_ast_expr(machine, eval, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
let TemplateAstExpr::Block {
|
||||||
|
prev_whitespace_content,
|
||||||
|
post_whitespace_content,
|
||||||
|
..
|
||||||
|
} = end_block.as_ref()
|
||||||
|
else {
|
||||||
|
unreachable!("The end of an IfConditional must be a Block");
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ws) = prev_whitespace_content {
|
||||||
|
eval.push(Instruction::AppendContent {
|
||||||
|
content: ws.source().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let jump = eval.len() - index - 1;
|
||||||
|
eval[index] = Instruction::JumpIfNotTrue {
|
||||||
|
emit_slot,
|
||||||
|
jump: jump as isize,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ws) = post_whitespace_content {
|
||||||
|
eval.push(Instruction::AppendContent {
|
||||||
|
content: ws.source().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateAstExpr::Block { .. }
|
||||||
|
| TemplateAstExpr::EndBlock
|
||||||
|
| TemplateAstExpr::IfConditional { .. }
|
||||||
|
| TemplateAstExpr::ElseConditional { .. }
|
||||||
|
| TemplateAstExpr::Invalid { .. }
|
||||||
|
| TemplateAstExpr::VariableAccess { .. } => eval.push(Instruction::Abort),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_expr(
|
fn emit_expr_load(
|
||||||
machine: &mut EmitMachine,
|
_machine: &mut EmitMachine,
|
||||||
eval: &mut Vec<Instruction>,
|
eval: &mut Vec<Instruction>,
|
||||||
emit_slot: VariableSlot,
|
emit_slot: VariableSlot,
|
||||||
expression: &TemplateAstExpr<'_>,
|
expression: &TemplateAstExpr<'_>,
|
||||||
|
|
@ -117,24 +187,11 @@ fn emit_expr(
|
||||||
TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => {
|
TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => {
|
||||||
unreachable!("Invalid AST here")
|
unreachable!("Invalid AST here")
|
||||||
}
|
}
|
||||||
TemplateAstExpr::ConditionalChain { chain } => todo!(),
|
TemplateAstExpr::ConditionalChain { .. } => todo!(),
|
||||||
TemplateAstExpr::ElseConditional { expression } => todo!(),
|
TemplateAstExpr::ElseConditional { .. } => todo!(),
|
||||||
TemplateAstExpr::Action {
|
|
||||||
prev_whitespace_content,
|
|
||||||
expression,
|
|
||||||
post_whitespace_content,
|
|
||||||
} => todo!(),
|
|
||||||
TemplateAstExpr::EndBlock => todo!(),
|
TemplateAstExpr::EndBlock => todo!(),
|
||||||
TemplateAstExpr::Block {
|
TemplateAstExpr::Block { .. } => todo!(),
|
||||||
prev_whitespace_content,
|
TemplateAstExpr::IfConditional { .. } => todo!(),
|
||||||
expression,
|
|
||||||
post_whitespace_content,
|
|
||||||
} => todo!(),
|
|
||||||
TemplateAstExpr::IfConditional {
|
|
||||||
expression,
|
|
||||||
content,
|
|
||||||
end_block,
|
|
||||||
} => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,13 +212,13 @@ mod tests {
|
||||||
insta::assert_debug_snapshot!(emit, @r#"
|
insta::assert_debug_snapshot!(emit, @r#"
|
||||||
[
|
[
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "Hello",
|
content: "Hello" (0..5),
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: " ",
|
content: " " (5..6),
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "world",
|
name: "world" (10..15),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 0,
|
index: 0,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@ pub enum EvaluationError {
|
||||||
UnknownVariable(NomoInput),
|
UnknownVariable(NomoInput),
|
||||||
/// An explicit abort was requested
|
/// An explicit abort was requested
|
||||||
ExplicitAbort,
|
ExplicitAbort,
|
||||||
|
/** The instruction pointer overflowed
|
||||||
|
**
|
||||||
|
** This is an internal error and is a bug that should be reported
|
||||||
|
*/
|
||||||
|
InstructionPointerOverflow,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
|
|
@ -23,7 +28,14 @@ pub fn execute(
|
||||||
|
|
||||||
let mut scopes: HashMap<crate::emit::VariableSlot, serde_json::Value> = HashMap::new();
|
let mut scopes: HashMap<crate::emit::VariableSlot, serde_json::Value> = HashMap::new();
|
||||||
|
|
||||||
for instr in instructions {
|
let mut ip = 0;
|
||||||
|
loop {
|
||||||
|
if ip >= instructions.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let instr = instructions.get(ip).unwrap();
|
||||||
|
|
||||||
match instr {
|
match instr {
|
||||||
Instruction::AppendContent { content } => output.push_str(content),
|
Instruction::AppendContent { content } => output.push_str(content),
|
||||||
Instruction::LoadFromContextToSlot { name, slot } => {
|
Instruction::LoadFromContextToSlot { name, slot } => {
|
||||||
|
|
@ -40,8 +52,25 @@ pub fn execute(
|
||||||
}
|
}
|
||||||
Instruction::PushScope { inherit_parent: _ } => todo!(),
|
Instruction::PushScope { inherit_parent: _ } => todo!(),
|
||||||
Instruction::Abort => return Err(EvaluationError::ExplicitAbort),
|
Instruction::Abort => return Err(EvaluationError::ExplicitAbort),
|
||||||
|
Instruction::JumpIfNotTrue { emit_slot, 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);
|
||||||
|
|
||||||
|
if overflow {
|
||||||
|
return Err(EvaluationError::InstructionPointerOverflow);
|
||||||
|
} else {
|
||||||
|
ip = new_ip;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip += 1;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/input.rs
29
src/input.rs
|
|
@ -22,11 +22,20 @@ impl NomoInput {
|
||||||
pub fn into_parts(self) -> (Arc<str>, Range<usize>) {
|
pub fn into_parts(self) -> (Arc<str>, Range<usize>) {
|
||||||
(self.backing, self.range)
|
(self.backing, self.range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_range(&self) -> Range<usize> {
|
||||||
|
self.range.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NomoInputCheckpoint {
|
||||||
|
range: Range<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for NomoInput {
|
impl std::fmt::Debug for NomoInput {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "\"{}\"", self.as_str())
|
write!(f, "{:?} ({:?})", self.as_str(), self.range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,6 +83,18 @@ impl NomoInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Offset for NomoInputCheckpoint {
|
||||||
|
fn offset_from(&self, start: &Self) -> usize {
|
||||||
|
self.range.start - start.range.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Offset<NomoInputCheckpoint> for NomoInput {
|
||||||
|
fn offset_from(&self, start: &NomoInputCheckpoint) -> usize {
|
||||||
|
self.range.start - start.range.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Offset for NomoInput {
|
impl Offset for NomoInput {
|
||||||
fn offset_from(&self, start: &Self) -> usize {
|
fn offset_from(&self, start: &Self) -> usize {
|
||||||
self.as_str().offset_from(&start.as_str())
|
self.as_str().offset_from(&start.as_str())
|
||||||
|
|
@ -116,7 +137,7 @@ impl Stream for NomoInput {
|
||||||
|
|
||||||
type IterOffsets = NomoInputIter;
|
type IterOffsets = NomoInputIter;
|
||||||
|
|
||||||
type Checkpoint = NomoInput;
|
type Checkpoint = NomoInputCheckpoint;
|
||||||
|
|
||||||
fn iter_offsets(&self) -> Self::IterOffsets {
|
fn iter_offsets(&self) -> Self::IterOffsets {
|
||||||
NomoInputIter {
|
NomoInputIter {
|
||||||
|
|
@ -167,7 +188,9 @@ impl Stream for NomoInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checkpoint(&self) -> Self::Checkpoint {
|
fn checkpoint(&self) -> Self::Checkpoint {
|
||||||
self.clone()
|
NomoInputCheckpoint {
|
||||||
|
range: self.get_range(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
|
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
|
||||||
|
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -65,13 +65,8 @@ impl Nomo {
|
||||||
|
|
||||||
let instructions = emit::emit_machine(ast);
|
let instructions = emit::emit_machine(ast);
|
||||||
|
|
||||||
self.templates.insert(
|
self.templates
|
||||||
name.into(),
|
.insert(name.into(), Template { instructions });
|
||||||
Template {
|
|
||||||
source,
|
|
||||||
instructions,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +84,6 @@ impl Nomo {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Template {
|
struct Template {
|
||||||
source: NomoInput,
|
|
||||||
instructions: Vec<Instruction>,
|
instructions: Vec<Instruction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -245,12 +245,28 @@ impl<const LEN: usize> winnow::stream::ContainsToken<&'_ TemplateToken> for [Tok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct TemplateToken {
|
pub struct TemplateToken {
|
||||||
kind: TokenKind,
|
kind: TokenKind,
|
||||||
source: NomoInput,
|
source: NomoInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for TemplateToken {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self.source())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Location for TemplateToken {
|
||||||
|
fn previous_token_end(&self) -> usize {
|
||||||
|
NomoInput::get_range(&self.source).start
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_token_start(&self) -> usize {
|
||||||
|
NomoInput::get_range(&self.source).start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_token_kind_builders {
|
macro_rules! impl_token_kind_builders {
|
||||||
($($name:ident => $kind:expr),+ $(,)?) => {
|
($($name:ident => $kind:expr),+ $(,)?) => {
|
||||||
$(
|
$(
|
||||||
|
|
@ -459,10 +475,7 @@ mod tests {
|
||||||
Ok(
|
Ok(
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"Hello There" (0..11),
|
||||||
kind: Content,
|
|
||||||
source: "Hello There",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -478,34 +491,13 @@ mod tests {
|
||||||
Ok(
|
Ok(
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"Hello" (0..5),
|
||||||
kind: Content,
|
" " (5..6),
|
||||||
source: "Hello",
|
"{{" (6..8),
|
||||||
},
|
" " (8..9),
|
||||||
TemplateToken {
|
"there" (9..14),
|
||||||
kind: Whitespace,
|
" " (14..15),
|
||||||
source: " ",
|
"}}" (15..17),
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "there",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -555,70 +547,21 @@ mod tests {
|
||||||
Ok(
|
Ok(
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"{{" (0..2),
|
||||||
kind: LeftDelim,
|
" " (2..3),
|
||||||
source: "{{",
|
"if" (3..5),
|
||||||
},
|
" " (5..6),
|
||||||
TemplateToken {
|
"true" (6..10),
|
||||||
kind: Whitespace,
|
" " (10..11),
|
||||||
source: " ",
|
"}}" (11..13),
|
||||||
},
|
" " (13..14),
|
||||||
TemplateToken {
|
"Hello!" (14..20),
|
||||||
kind: ConditionalIf,
|
" " (20..21),
|
||||||
source: "if",
|
"{{" (21..23),
|
||||||
},
|
" " (23..24),
|
||||||
TemplateToken {
|
"end" (24..27),
|
||||||
kind: Whitespace,
|
" " (27..28),
|
||||||
source: " ",
|
"}}" (28..30),
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Literal(
|
|
||||||
Bool(
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
source: "true",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Content,
|
|
||||||
source: "Hello!",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: End,
|
|
||||||
source: "end",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
31
tests/cases/1-parsed@condition.snap
Normal file
31
tests/cases/1-parsed@condition.snap
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
source: tests/file_tests.rs
|
||||||
|
expression: parsed
|
||||||
|
input_file: tests/cases/condition.nomo
|
||||||
|
---
|
||||||
|
ParsedTemplate {
|
||||||
|
tokens: [
|
||||||
|
"{{" (0..2),
|
||||||
|
" " (2..3),
|
||||||
|
"if" (3..5),
|
||||||
|
" " (5..6),
|
||||||
|
"test" (6..10),
|
||||||
|
" " (10..11),
|
||||||
|
"}}" (11..13),
|
||||||
|
"\n " (13..18),
|
||||||
|
"Hello World!" (18..30),
|
||||||
|
"\n" (30..31),
|
||||||
|
"{{" (31..33),
|
||||||
|
" " (33..34),
|
||||||
|
"end" (34..37),
|
||||||
|
" " (37..38),
|
||||||
|
"}}" (38..40),
|
||||||
|
"\n\n" (40..42),
|
||||||
|
"{{" (42..44),
|
||||||
|
"=" (44..45),
|
||||||
|
" " (45..46),
|
||||||
|
"stuff" (46..51),
|
||||||
|
" " (51..52),
|
||||||
|
"}}" (52..54),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -5,174 +5,46 @@ input_file: tests/cases/identifiers.nomo
|
||||||
---
|
---
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"{{" (0..2),
|
||||||
kind: LeftDelim,
|
"=" (2..3),
|
||||||
source: "{{",
|
" " (3..4),
|
||||||
},
|
"_name" (4..9),
|
||||||
TemplateToken {
|
" " (9..10),
|
||||||
kind: WantsOutput,
|
"}}" (10..12),
|
||||||
source: "=",
|
"\n" (12..13),
|
||||||
},
|
"{{" (13..15),
|
||||||
TemplateToken {
|
"=" (15..16),
|
||||||
kind: Whitespace,
|
" " (16..17),
|
||||||
source: " ",
|
"a_name" (17..23),
|
||||||
},
|
" " (23..24),
|
||||||
TemplateToken {
|
"}}" (24..26),
|
||||||
kind: Ident,
|
"\n" (26..27),
|
||||||
source: "_name",
|
"{{" (27..29),
|
||||||
},
|
"=" (29..30),
|
||||||
TemplateToken {
|
" " (30..31),
|
||||||
kind: Whitespace,
|
"1name" (31..36),
|
||||||
source: " ",
|
" " (36..37),
|
||||||
},
|
"}}" (37..39),
|
||||||
TemplateToken {
|
"\n" (39..40),
|
||||||
kind: RightDelim,
|
"{{" (40..42),
|
||||||
source: "}}",
|
"=" (42..43),
|
||||||
},
|
" " (43..44),
|
||||||
TemplateToken {
|
"_name1" (44..50),
|
||||||
kind: Whitespace,
|
" " (50..51),
|
||||||
source: "
|
"}}" (51..53),
|
||||||
",
|
"\n" (53..54),
|
||||||
},
|
"{{" (54..56),
|
||||||
TemplateToken {
|
"=" (56..57),
|
||||||
kind: LeftDelim,
|
" " (57..58),
|
||||||
source: "{{",
|
"_namE" (58..63),
|
||||||
},
|
" " (63..64),
|
||||||
TemplateToken {
|
"}}" (64..66),
|
||||||
kind: WantsOutput,
|
"\n" (66..67),
|
||||||
source: "=",
|
"{{" (67..69),
|
||||||
},
|
"=" (69..70),
|
||||||
TemplateToken {
|
" " (70..71),
|
||||||
kind: Whitespace,
|
"name1" (71..76),
|
||||||
source: " ",
|
" " (76..77),
|
||||||
},
|
"}}" (77..79),
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "a_name",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "1name",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "_name1",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "_namE",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "name1",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,13 @@ input_file: tests/cases/interpolation.nomo
|
||||||
---
|
---
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"Hello! I'm" (0..10),
|
||||||
kind: Content,
|
" " (10..11),
|
||||||
source: "Hello! I'm",
|
"{{" (11..13),
|
||||||
},
|
"=" (13..14),
|
||||||
TemplateToken {
|
" " (14..15),
|
||||||
kind: Whitespace,
|
"name" (15..19),
|
||||||
source: " ",
|
" " (19..20),
|
||||||
},
|
"}}" (20..22),
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "name",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,65 +5,20 @@ input_file: tests/cases/multiple.nomo
|
||||||
---
|
---
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"Hi there! My name is" (0..20),
|
||||||
kind: Content,
|
" " (20..21),
|
||||||
source: "Hi there! My name is",
|
"{{" (21..23),
|
||||||
},
|
"=" (23..24),
|
||||||
TemplateToken {
|
" " (24..25),
|
||||||
kind: Whitespace,
|
"name" (25..29),
|
||||||
source: " ",
|
" " (29..30),
|
||||||
},
|
"}}" (30..32),
|
||||||
TemplateToken {
|
" " (32..33),
|
||||||
kind: LeftDelim,
|
"{{" (33..35),
|
||||||
source: "{{",
|
"=" (35..36),
|
||||||
},
|
" " (36..37),
|
||||||
TemplateToken {
|
"lastname" (37..45),
|
||||||
kind: WantsOutput,
|
" " (45..46),
|
||||||
source: "=",
|
"}}" (46..48),
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "name",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: LeftDelim,
|
|
||||||
source: "{{",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Ident,
|
|
||||||
source: "lastname",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
TemplateToken {
|
|
||||||
kind: RightDelim,
|
|
||||||
source: "}}",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@ input_file: tests/cases/simple.nomo
|
||||||
---
|
---
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
tokens: [
|
tokens: [
|
||||||
TemplateToken {
|
"Hello World!" (0..12),
|
||||||
kind: Content,
|
|
||||||
source: "Hello World!",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
45
tests/cases/2-ast@condition.snap
Normal file
45
tests/cases/2-ast@condition.snap
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
source: tests/file_tests.rs
|
||||||
|
expression: ast
|
||||||
|
input_file: tests/cases/condition.nomo
|
||||||
|
---
|
||||||
|
TemplateAst {
|
||||||
|
root: [
|
||||||
|
ConditionalChain {
|
||||||
|
chain: [
|
||||||
|
IfConditional {
|
||||||
|
if_block: Block {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: VariableAccess(
|
||||||
|
"test" (6..10),
|
||||||
|
),
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n " (13..18),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
StaticContent(
|
||||||
|
"Hello World!" (18..30),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
end_block: Block {
|
||||||
|
prev_whitespace_content: Some(
|
||||||
|
"\n" (30..31),
|
||||||
|
),
|
||||||
|
expression: EndBlock,
|
||||||
|
post_whitespace_content: Some(
|
||||||
|
"\n\n" (40..42),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Interpolation {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: VariableAccess(
|
||||||
|
"stuff" (46..51),
|
||||||
|
),
|
||||||
|
post_whitespace_content: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -7,115 +7,53 @@ TemplateAst {
|
||||||
root: [
|
root: [
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"_name" (4..9),
|
||||||
kind: Ident,
|
|
||||||
source: "_name",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
"\n" (12..13),
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"a_name" (17..23),
|
||||||
kind: Ident,
|
|
||||||
source: "a_name",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
"\n" (26..27),
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"1name" (31..36),
|
||||||
kind: Ident,
|
|
||||||
source: "1name",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
"\n" (39..40),
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"_name1" (44..50),
|
||||||
kind: Ident,
|
|
||||||
source: "_name1",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
"\n" (53..54),
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"_namE" (58..63),
|
||||||
kind: Ident,
|
|
||||||
source: "_namE",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
"\n" (66..67),
|
||||||
kind: Whitespace,
|
|
||||||
source: "
|
|
||||||
",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"name1" (71..76),
|
||||||
kind: Ident,
|
|
||||||
source: "name1",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: None,
|
post_whitespace_content: None,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,14 @@ input_file: tests/cases/interpolation.nomo
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
root: [
|
root: [
|
||||||
StaticContent(
|
StaticContent(
|
||||||
TemplateToken {
|
"Hello! I'm" (0..10),
|
||||||
kind: Content,
|
|
||||||
source: "Hello! I'm",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: Some(
|
prev_whitespace_content: Some(
|
||||||
TemplateToken {
|
" " (10..11),
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"name" (15..19),
|
||||||
kind: Ident,
|
|
||||||
source: "name",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: None,
|
post_whitespace_content: None,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,46 +6,23 @@ input_file: tests/cases/multiple.nomo
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
root: [
|
root: [
|
||||||
StaticContent(
|
StaticContent(
|
||||||
TemplateToken {
|
"Hi there! My name is" (0..20),
|
||||||
kind: Content,
|
|
||||||
source: "Hi there! My name is",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: Some(
|
prev_whitespace_content: Some(
|
||||||
TemplateToken {
|
" " (20..21),
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"name" (25..29),
|
||||||
kind: Ident,
|
|
||||||
source: "name",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: Some(
|
post_whitespace_content: Some(
|
||||||
TemplateToken {
|
" " (32..33),
|
||||||
kind: Whitespace,
|
|
||||||
source: " ",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace_content: None,
|
prev_whitespace_content: None,
|
||||||
wants_output: TemplateToken {
|
|
||||||
kind: WantsOutput,
|
|
||||||
source: "=",
|
|
||||||
},
|
|
||||||
expression: VariableAccess(
|
expression: VariableAccess(
|
||||||
TemplateToken {
|
"lastname" (37..45),
|
||||||
kind: Ident,
|
|
||||||
source: "lastname",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
post_whitespace_content: None,
|
post_whitespace_content: None,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ input_file: tests/cases/simple.nomo
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
root: [
|
root: [
|
||||||
StaticContent(
|
StaticContent(
|
||||||
TemplateToken {
|
"Hello World!" (0..12),
|
||||||
kind: Content,
|
|
||||||
source: "Hello World!",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
tests/cases/3-instructions@condition.snap
Normal file
42
tests/cases/3-instructions@condition.snap
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
source: tests/file_tests.rs
|
||||||
|
expression: emit
|
||||||
|
input_file: tests/cases/condition.nomo
|
||||||
|
---
|
||||||
|
[
|
||||||
|
LoadFromContextToSlot {
|
||||||
|
name: "test" (6..10),
|
||||||
|
slot: VariableSlot {
|
||||||
|
index: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
JumpIfNotTrue {
|
||||||
|
emit_slot: VariableSlot {
|
||||||
|
index: 0,
|
||||||
|
},
|
||||||
|
jump: 3,
|
||||||
|
},
|
||||||
|
AppendContent {
|
||||||
|
content: "\n " (13..18),
|
||||||
|
},
|
||||||
|
AppendContent {
|
||||||
|
content: "Hello World!" (18..30),
|
||||||
|
},
|
||||||
|
AppendContent {
|
||||||
|
content: "\n" (30..31),
|
||||||
|
},
|
||||||
|
AppendContent {
|
||||||
|
content: "\n\n" (40..42),
|
||||||
|
},
|
||||||
|
LoadFromContextToSlot {
|
||||||
|
name: "stuff" (46..51),
|
||||||
|
slot: VariableSlot {
|
||||||
|
index: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EmitFromSlot {
|
||||||
|
slot: VariableSlot {
|
||||||
|
index: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
@ -5,7 +5,7 @@ input_file: tests/cases/identifiers.nomo
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "_name",
|
name: "_name" (4..9),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 0,
|
index: 0,
|
||||||
},
|
},
|
||||||
|
|
@ -16,11 +16,10 @@ input_file: tests/cases/identifiers.nomo
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "
|
content: "\n" (12..13),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "a_name",
|
name: "a_name" (17..23),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
|
|
@ -31,11 +30,10 @@ input_file: tests/cases/identifiers.nomo
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "
|
content: "\n" (26..27),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "1name",
|
name: "1name" (31..36),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 2,
|
index: 2,
|
||||||
},
|
},
|
||||||
|
|
@ -46,11 +44,10 @@ input_file: tests/cases/identifiers.nomo
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "
|
content: "\n" (39..40),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "_name1",
|
name: "_name1" (44..50),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 3,
|
index: 3,
|
||||||
},
|
},
|
||||||
|
|
@ -61,11 +58,10 @@ input_file: tests/cases/identifiers.nomo
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "
|
content: "\n" (53..54),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "_namE",
|
name: "_namE" (58..63),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 4,
|
index: 4,
|
||||||
},
|
},
|
||||||
|
|
@ -76,11 +72,10 @@ input_file: tests/cases/identifiers.nomo
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "
|
content: "\n" (66..67),
|
||||||
",
|
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "name1",
|
name: "name1" (71..76),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 5,
|
index: 5,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ input_file: tests/cases/interpolation.nomo
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "Hello! I'm",
|
content: "Hello! I'm" (0..10),
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: " ",
|
content: " " (10..11),
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "name",
|
name: "name" (15..19),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 0,
|
index: 0,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ input_file: tests/cases/multiple.nomo
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "Hi there! My name is",
|
content: "Hi there! My name is" (0..20),
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: " ",
|
content: " " (20..21),
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "name",
|
name: "name" (25..29),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 0,
|
index: 0,
|
||||||
},
|
},
|
||||||
|
|
@ -22,10 +22,10 @@ input_file: tests/cases/multiple.nomo
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: " ",
|
content: " " (32..33),
|
||||||
},
|
},
|
||||||
LoadFromContextToSlot {
|
LoadFromContextToSlot {
|
||||||
name: "lastname",
|
name: "lastname" (37..45),
|
||||||
slot: VariableSlot {
|
slot: VariableSlot {
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ input_file: tests/cases/simple.nomo
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
AppendContent {
|
AppendContent {
|
||||||
content: "Hello World!",
|
content: "Hello World!" (0..12),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
6
tests/cases/4-output@condition.snap
Normal file
6
tests/cases/4-output@condition.snap
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: tests/file_tests.rs
|
||||||
|
expression: output
|
||||||
|
input_file: tests/cases/condition.nomo
|
||||||
|
---
|
||||||
|
"\n Hello World!\n\n\nmore"
|
||||||
10
tests/cases/condition.nomo
Normal file
10
tests/cases/condition.nomo
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"test": true,
|
||||||
|
"stuff": "more"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
{{ if test }}
|
||||||
|
Hello World!
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{= stuff }}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue