Add function asting

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-13 09:08:06 +01:00
parent 70f616d60c
commit cb55c00739
4 changed files with 97 additions and 4 deletions

View file

@ -13,6 +13,7 @@ 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::separated;
use winnow::combinator::trace; use winnow::combinator::trace;
use winnow::error::AddContext; use winnow::error::AddContext;
use winnow::error::FromRecoverableError; use winnow::error::FromRecoverableError;
@ -268,6 +269,10 @@ pub enum TemplateAstExpr<'input> {
source: TemplateToken, source: TemplateToken,
value: NomoValue, value: NomoValue,
}, },
FunctionCall {
name: Box<TemplateAstExpr<'input>>,
args: Vec<TemplateAstExpr<'input>>,
},
} }
fn parse_asts<'input>(input: &mut Input<'input>) -> Result<Vec<TemplateAstExpr<'input>>, AstError> { fn parse_asts<'input>(input: &mut Input<'input>) -> Result<Vec<TemplateAstExpr<'input>>, AstError> {
@ -559,6 +564,22 @@ fn parse_end<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input
.parse_next(input) .parse_next(input)
} }
fn parse_function<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
trace(
"variable_access",
(
TokenKind::Ident
.map(TemplateAstExpr::VariableAccess)
.map(Box::new),
TokenKind::LeftArgList,
separated(0.., parse_expression, TokenKind::ArgSeperator),
TokenKind::RightArgList,
)
.map(|(name, _left, args, _right)| TemplateAstExpr::FunctionCall { name, args }),
)
.parse_next(input)
}
fn parse_variable_access<'input>( fn parse_variable_access<'input>(
input: &mut Input<'input>, input: &mut Input<'input>,
) -> Result<TemplateAstExpr<'input>, AstError> { ) -> Result<TemplateAstExpr<'input>, AstError> {
@ -620,7 +641,11 @@ where
} }
fn parse_operand<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> { fn parse_operand<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
trace("operand", alt((parse_variable_access, parse_literal))).parse_next(input) trace(
"operand",
alt((parse_function, parse_variable_access, parse_literal)),
)
.parse_next(input)
} }
fn parse_literal<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> { fn parse_literal<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
@ -658,7 +683,11 @@ fn parse_expression<'input>(
($parser:expr => [ $($side:tt $val:tt => $prec:expr),* $(,)? ]) => { ($parser:expr => [ $($side:tt $val:tt => $prec:expr),* $(,)? ]) => {
dispatch! { surrounded(ws, parse_operator); dispatch! { surrounded(ws, parse_operator);
$( $(
TokenOperator::$val => $side($prec, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::$val, lhs: Box::new(lhs), rhs: Box::new(rhs) })) TokenOperator::$val => $side($prec, |_, lhs, rhs| Ok(TemplateAstExpr::Operation {
op: TokenOperator::$val,
lhs: Box::new(lhs),
rhs: Box::new(rhs)
}))
),* ),*
} }
}; };
@ -1008,4 +1037,15 @@ mod tests {
insta::assert_debug_snapshot!(ast); insta::assert_debug_snapshot!(ast);
} }
#[test]
fn check_function_call() {
let input = "{{= foo(2 * 3, bar(2 + baz)) }}";
let parsed = crate::parser::parse(input.into()).unwrap();
let ast = panic_pretty(input, parse(parsed.tokens()));
insta::assert_debug_snapshot!(ast);
}
} }

View file

@ -0,0 +1,53 @@
---
source: src/ast/mod.rs
expression: ast
---
TemplateAst {
root: [
Interpolation {
prev_whitespace_content: None,
expression: FunctionCall {
name: VariableAccess(
[Ident]"foo" (4..7),
),
args: [
Operation {
op: Times,
lhs: Literal {
source: [Literal(Integer(2))]"2" (8..9),
value: Integer {
value: 2,
},
},
rhs: Literal {
source: [Literal(Integer(3))]"3" (12..13),
value: Integer {
value: 3,
},
},
},
FunctionCall {
name: VariableAccess(
[Ident]"bar" (15..18),
),
args: [
Operation {
op: Plus,
lhs: Literal {
source: [Literal(Integer(2))]"2" (19..20),
value: Integer {
value: 2,
},
},
rhs: VariableAccess(
[Ident]"baz" (23..26),
),
},
],
},
],
},
post_whitespace_content: None,
},
],
}

View file

@ -417,6 +417,7 @@ fn emit_ast_expr(
| TemplateAstExpr::ForElse | TemplateAstExpr::ForElse
| TemplateAstExpr::Invalid { .. } | TemplateAstExpr::Invalid { .. }
| TemplateAstExpr::Literal { .. } | TemplateAstExpr::Literal { .. }
| TemplateAstExpr::FunctionCall { .. }
| TemplateAstExpr::Operation { .. } | TemplateAstExpr::Operation { .. }
| TemplateAstExpr::VariableAccess { .. } => eval.push(Instruction::Abort), | TemplateAstExpr::VariableAccess { .. } => eval.push(Instruction::Abort),
} }
@ -459,6 +460,7 @@ fn emit_expr_load(
TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => { TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => {
unreachable!("Invalid AST here") unreachable!("Invalid AST here")
} }
TemplateAstExpr::FunctionCall { .. } => todo!(),
TemplateAstExpr::ConditionalChain { .. } => todo!(), TemplateAstExpr::ConditionalChain { .. } => todo!(),
TemplateAstExpr::ElseConditional { .. } => todo!(), TemplateAstExpr::ElseConditional { .. } => todo!(),
TemplateAstExpr::EndBlock => todo!(), TemplateAstExpr::EndBlock => todo!(),

View file

@ -5,8 +5,6 @@ use std::collections::BTreeMap;
use displaydoc::Display; use displaydoc::Display;
use thiserror::Error; use thiserror::Error;
use crate::Nomo;
#[derive(Clone)] #[derive(Clone)]
pub enum NomoValue { pub enum NomoValue {
String { String {