Add optional parameter declarations (including default values)
This commit is contained in:
parent
5a115b3a20
commit
7cb653ab67
@ -4,9 +4,17 @@ use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct Template {
|
||||
pub param_defs: Option<Vec<ParameterDefinition>>,
|
||||
pub blocks: Vec<Block>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct ParameterDefinition {
|
||||
pub name: IStr,
|
||||
pub loc: Locator,
|
||||
pub default: Option<Expression>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub enum Block {
|
||||
HtmlElement(HtmlElement),
|
||||
|
@ -1,5 +1,21 @@
|
||||
//
|
||||
Hornbeam = { SOI ~ wsnl* ~ BlockContent* ~ ws* ~ EOI }
|
||||
Hornbeam = { SOI ~ wsnl* ~ PreambleList? ~ wsnl* ~ HornbeamBlockList ~ ws* ~ EOI }
|
||||
|
||||
// Will eventually expand to other types of preamble content
|
||||
PreambleList = {
|
||||
// accept `declare` keyword. We expect `PEEK_ALL` to be empty, but future definitions might change this.
|
||||
PEEK_ALL ~ "declare" ~ lineEnd ~
|
||||
// then accept the first definition. We must have at least one definition.
|
||||
// We accept the new indentation level with the `PUSH` here
|
||||
PEEK_ALL ~ PUSH(" "+ | "\t"+) ~ ParameterDefinition ~
|
||||
// Now accept any number of extra definitions at the same indentation level.
|
||||
(PEEK_ALL ~ ParameterDefinition)* ~
|
||||
// Drop the indentation when exiting the preamble block
|
||||
DROP
|
||||
}
|
||||
|
||||
|
||||
HornbeamBlockList = { BlockContent* }
|
||||
|
||||
NewBlock = _{
|
||||
PEEK_ALL ~ PUSH(" "+ | "\t"+) ~ BlockContent ~
|
||||
@ -18,6 +34,13 @@ BlockContent = _{
|
||||
DefineFragment
|
||||
}
|
||||
|
||||
// `param $x`
|
||||
// `param $x = 1 + 1`
|
||||
// TODO: type annotation: `param $x: int`
|
||||
ParameterDefinition = {
|
||||
"param" ~ ws+ ~ "$" ~ Identifier ~ (ws* ~ "=" ~ ws* ~ Expr)? ~ lineEnd
|
||||
}
|
||||
|
||||
Element = {
|
||||
ElementName ~ cssClass* ~ domId? ~ (ws+ ~ AttrMapLiteral)? ~ lineEnd ~ (NewBlock | NewSlotBlock)?
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::ast::{
|
||||
Binding, Block, ComponentElement, DefineExpandSlot, DefineFragment, ElementAttributeFlags,
|
||||
Expression, ForBlock, HtmlElement, IfBlock, MatchBinding, MatchBlock, SetStatement, StringExpr,
|
||||
StringPiece, Template,
|
||||
Expression, ForBlock, HtmlElement, IfBlock, MatchBinding, MatchBlock, ParameterDefinition,
|
||||
SetStatement, StringExpr, StringPiece, Template,
|
||||
};
|
||||
use crate::{intern, IStr, Locator};
|
||||
use lazy_static::lazy_static;
|
||||
@ -61,8 +61,51 @@ lazy_static! {
|
||||
#[pest_consume::parser]
|
||||
impl HornbeamParser {
|
||||
fn Hornbeam(input: Node) -> PCResult<Template> {
|
||||
let blocks = HornbeamParser::helper_blocks(input.into_children())?;
|
||||
Ok(Template { blocks })
|
||||
Ok(match_nodes!(input.into_children();
|
||||
[PreambleList(param_defs), HornbeamBlockList(blocks), EOI(_)] => {
|
||||
Template { param_defs, blocks }
|
||||
},
|
||||
[HornbeamBlockList(blocks), EOI(_)] => {
|
||||
Template { param_defs: None, blocks }
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fn PreambleList(input: Node) -> PCResult<Option<Vec<ParameterDefinition>>> {
|
||||
let param_defs: Vec<ParameterDefinition> = match_nodes!(input.into_children();
|
||||
[ParameterDefinition(param_defs)..] => param_defs.collect()
|
||||
);
|
||||
// Templates with no parameter definitions are treated as untyped.
|
||||
// (Mostly for backwards compat)
|
||||
Ok(if param_defs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(param_defs)
|
||||
})
|
||||
}
|
||||
|
||||
fn ParameterDefinition(input: Node) -> PCResult<ParameterDefinition> {
|
||||
let loc = nodeloc(&input);
|
||||
Ok(match_nodes!(input.into_children();
|
||||
[Identifier(name), Expr(default)] => {
|
||||
ParameterDefinition {
|
||||
name,
|
||||
loc,
|
||||
default: Some(default),
|
||||
}
|
||||
},
|
||||
[Identifier(name)] => {
|
||||
ParameterDefinition {
|
||||
name,
|
||||
loc,
|
||||
default: None,
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn HornbeamBlockList(input: Node) -> PCResult<Vec<Block>> {
|
||||
HornbeamParser::helper_blocks(input.into_children())
|
||||
}
|
||||
|
||||
fn Element(input: Node) -> PCResult<Block> {
|
||||
@ -617,6 +660,23 @@ div
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_defs() {
|
||||
assert_yaml_snapshot!(parse_template(
|
||||
r#"
|
||||
declare
|
||||
// We need form information
|
||||
param $form
|
||||
|
||||
// Also wouldn't hurt to have a default value on this other parameter
|
||||
|
||||
param $user = None
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supply_slots_to_components_only() {
|
||||
assert_debug_snapshot!(parse_template(
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\nfor $x in $xs\n for $y in $ys\n \"Woot\"\n empty\n \"no ys\"\nempty\n \"no xs\"\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- ForBlock:
|
||||
binding:
|
||||
@ -44,4 +45,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\ndiv\n fragment BobbyDazzler\n span\n \"Wow!\"\n fragment MainPart\n slot :main\n div\n optional slot :footer\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
@ -64,4 +65,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\nif 1 + 1 == 2\n div\n \"Phew, safe!\"\nelse if 1 + 2 - 1 == 3 // not too far off, I suppose\n Warning\n \"Not quite, but fairly close. What kind of world is this?\"\nelse // peculiar.\n \"Not even close, eh?\"\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- IfBlock:
|
||||
condition:
|
||||
@ -76,4 +77,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\nif 10 / 2 == 5 or $point.x == 42 // div for a div?\n div\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- IfBlock:
|
||||
condition:
|
||||
@ -54,4 +55,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\ndiv\n span\n @header-wow\n MainBody\n ''\n @body-welcome\n @body-msg{count = $messages.len()}\n ''\n Footer\n @footer-copyright{year = today().year}\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
@ -90,4 +91,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\nif $x\n match $y\n None =>\n \"None\"\n Some($z) =>\n \"Some(${$z})\"\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- IfBlock:
|
||||
condition:
|
||||
@ -50,4 +51,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\ndeclare\n // We need form information\n param $form\n\n // Also wouldn't hurt to have a default value on this other parameter\n\n param $user = None\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs:
|
||||
- name: form
|
||||
loc:
|
||||
filename: inp
|
||||
line: 4
|
||||
column: 5
|
||||
default: ~
|
||||
- name: user
|
||||
loc:
|
||||
filename: inp
|
||||
line: 8
|
||||
column: 5
|
||||
default: NoneLiteral
|
||||
blocks: []
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\ndiv\n span\n raw \"<u>wow $x ${$x} @wowage{}</u>\"\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
@ -42,4 +43,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\n// This is a simple Hornbeam template that just shows a <div>\ndiv\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
@ -13,4 +14,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 3
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\nMyComponent\n :someslot\n ''\n ${\"abc\" + \"def${ 1 + 1 }\"}\n Not too bad now.\n ''\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- ComponentElement:
|
||||
name: MyComponent
|
||||
@ -34,4 +35,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
source: hornbeam_grammar/src/parser.rs
|
||||
expression: "parse_template(r#\"\nMyComponent\n :someslot\n \"That's better!\"\n \"#,\n \"inp\").unwrap()"
|
||||
---
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- ComponentElement:
|
||||
name: MyComponent
|
||||
@ -15,4 +16,3 @@ blocks:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -475,12 +475,61 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
|
||||
// TODO check slots
|
||||
// ...
|
||||
|
||||
// check params
|
||||
// check params and evaluate defaults
|
||||
if let Some(param_defs) = &module.param_defs {
|
||||
for param_def in param_defs {
|
||||
if evaled_args.contains_key(param_def.name.as_str()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(default_expr) = ¶m_def.default else {
|
||||
return Err(InterpreterError::TypeError {
|
||||
context: "Call".to_string(),
|
||||
conflict: format!(
|
||||
"missing required parameter {} for {name:?}.",
|
||||
param_def.name
|
||||
),
|
||||
location: step.locator.clone(),
|
||||
});
|
||||
};
|
||||
|
||||
// Push a temporary empty scope
|
||||
// TODO in the future allow evaluation with some scope if desired.
|
||||
self.scopes.push(Scope {
|
||||
variables: BTreeMap::new(),
|
||||
slots: BTreeMap::new(),
|
||||
});
|
||||
evaled_args.insert(
|
||||
String::from(param_def.name.as_str()),
|
||||
self.evaluate_expression(
|
||||
scope_idx + 1,
|
||||
default_expr,
|
||||
¶m_def.locator,
|
||||
)?,
|
||||
);
|
||||
self.scopes.pop();
|
||||
}
|
||||
for supplied_param in evaled_args.keys() {
|
||||
if !param_defs
|
||||
.iter()
|
||||
.any(|def| def.name.as_str() == supplied_param)
|
||||
{
|
||||
return Err(InterpreterError::TypeError {
|
||||
context: "Call".to_string(),
|
||||
conflict: format!(
|
||||
"provided non-existent parameter {} for {name:?}.",
|
||||
supplied_param
|
||||
),
|
||||
location: Locator::empty(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
self.scopes.push(Scope {
|
||||
variables: evaled_args,
|
||||
slots: filled_in_slots,
|
||||
});
|
||||
|
||||
let next_scope_idx = self.scopes.len() - 1;
|
||||
|
||||
self.run_steps(next_scope_idx, &module.steps).await?;
|
||||
@ -902,7 +951,55 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
|
||||
location: Locator::empty(),
|
||||
});
|
||||
};
|
||||
// TODO slot + params check
|
||||
// TODO deduplicate with `Call` step
|
||||
if let Some(param_defs) = &main.param_defs {
|
||||
for param_def in param_defs {
|
||||
if self.scopes[0]
|
||||
.variables
|
||||
.contains_key(param_def.name.as_str())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(default_expr) = ¶m_def.default else {
|
||||
return Err(InterpreterError::TypeError {
|
||||
context: "main entrypoint".to_string(),
|
||||
conflict: format!(
|
||||
"missing required parameter {} for entrypoint {:?}.",
|
||||
param_def.name, self.entrypoint
|
||||
),
|
||||
location: Locator::empty(),
|
||||
});
|
||||
};
|
||||
|
||||
// Push a temporary empty scope
|
||||
// TODO in the future allow evaluation with some scope if desired.
|
||||
self.scopes.push(Scope {
|
||||
variables: BTreeMap::new(),
|
||||
slots: BTreeMap::new(),
|
||||
});
|
||||
let value = self.evaluate_expression(1, default_expr, ¶m_def.locator)?;
|
||||
self.scopes[0]
|
||||
.variables
|
||||
.insert(String::from(param_def.name.as_str()), value);
|
||||
self.scopes.pop();
|
||||
}
|
||||
for supplied_param in self.scopes[0].variables.keys() {
|
||||
if !param_defs
|
||||
.iter()
|
||||
.any(|def| def.name.as_str() == supplied_param)
|
||||
{
|
||||
return Err(InterpreterError::TypeError {
|
||||
context: "main entrypoint".to_string(),
|
||||
conflict: format!(
|
||||
"provided non-existent parameter {} for entrypoint {:?}.",
|
||||
supplied_param, self.entrypoint
|
||||
),
|
||||
location: Locator::empty(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
self.run_steps(0, &main.steps).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ fn simple_test_struct() -> SimpleTestStruct {
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn simple_render(template: &str) -> String {
|
||||
let mut templates = LoadedTemplates::new(DebugLocalisationSystem);
|
||||
templates
|
||||
@ -112,3 +113,18 @@ match $unused_var
|
||||
"#
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_006() {
|
||||
assert_snapshot!(simple_render(
|
||||
r#"
|
||||
declare
|
||||
param $default_param = "default value!"
|
||||
|
||||
param $five
|
||||
param $sts
|
||||
|
||||
"$default_param"
|
||||
"#
|
||||
))
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: hornbeam_interpreter/tests/snapshots.rs
|
||||
expression: "simple_render(r#\"\ndeclare\n param $default_param = \"default value!\"\n\n\"$default_param\"\n \"#)"
|
||||
---
|
||||
default value!
|
@ -1,4 +1,4 @@
|
||||
use crate::ir::{Step, StepDef, TemplateFunction};
|
||||
use crate::ir::{ParamDef, Step, StepDef, TemplateFunction};
|
||||
use hornbeam_grammar::ast::{
|
||||
Binding, Block, Expression, HtmlElement, MatchBinding, StringExpr, StringPiece, Template,
|
||||
};
|
||||
@ -36,7 +36,7 @@ pub enum AstToIrError {
|
||||
pub(crate) fn pull_out_entrypoints(
|
||||
mut template: Template,
|
||||
template_name: &str,
|
||||
) -> Result<BTreeMap<String, Vec<Block>>, AstToIrError> {
|
||||
) -> Result<BTreeMap<String, Template>, AstToIrError> {
|
||||
let mut functions = BTreeMap::new();
|
||||
|
||||
for child in &mut template.blocks {
|
||||
@ -45,7 +45,7 @@ pub(crate) fn pull_out_entrypoints(
|
||||
|
||||
match functions.entry(template_name.to_owned()) {
|
||||
Entry::Vacant(ve) => {
|
||||
ve.insert(template.blocks);
|
||||
ve.insert(template);
|
||||
}
|
||||
Entry::Occupied(_) => {
|
||||
return Err(AstToIrError::SemanticError {
|
||||
@ -66,7 +66,7 @@ pub(crate) fn pull_out_entrypoints(
|
||||
fn pull_out_entrypoints_from_block(
|
||||
block: &mut Block,
|
||||
template_name: &str,
|
||||
target: &mut BTreeMap<String, Vec<Block>>,
|
||||
target: &mut BTreeMap<String, Template>,
|
||||
) -> Result<(), AstToIrError> {
|
||||
match block {
|
||||
Block::HtmlElement(he) => {
|
||||
@ -122,7 +122,10 @@ fn pull_out_entrypoints_from_block(
|
||||
// function.
|
||||
let mut blocks = Vec::new();
|
||||
std::mem::swap(&mut blocks, &mut frag.blocks);
|
||||
ve.insert(blocks);
|
||||
ve.insert(Template {
|
||||
blocks,
|
||||
param_defs: None,
|
||||
});
|
||||
}
|
||||
Entry::Occupied(_) => {
|
||||
return Err(AstToIrError::SemanticError {
|
||||
@ -143,22 +146,25 @@ fn pull_out_entrypoints_from_block(
|
||||
|
||||
/// Step 2. Compile the AST to IR steps.
|
||||
pub(crate) fn compile_functions(
|
||||
functions: &BTreeMap<String, Vec<Block>>,
|
||||
functions: &BTreeMap<String, Template>,
|
||||
) -> Result<BTreeMap<String, TemplateFunction>, AstToIrError> {
|
||||
let mut result = BTreeMap::new();
|
||||
for (func_name, func_blocks) in functions {
|
||||
for (func_name, func_template) in functions {
|
||||
let mut steps = Vec::new();
|
||||
for block in func_blocks {
|
||||
for block in &func_template.blocks {
|
||||
compile_ast_block_to_steps(block, &mut steps)?;
|
||||
}
|
||||
// TODO save more information
|
||||
result.insert(
|
||||
func_name.clone(),
|
||||
TemplateFunction {
|
||||
param_defs: None,
|
||||
steps,
|
||||
},
|
||||
);
|
||||
|
||||
let param_defs = func_template.param_defs.as_ref().map(|defs| {
|
||||
defs.iter()
|
||||
.map(|def| ParamDef {
|
||||
name: def.name.clone(),
|
||||
default: def.default.clone(),
|
||||
locator: def.loc.clone(),
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
result.insert(func_name.clone(), TemplateFunction { param_defs, steps });
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
@ -642,4 +648,24 @@ div.stylish#myid {size=42, stringy="yup", arb=$ritrary}
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn test_compile_params() {
|
||||
let template = parse_template(
|
||||
r#"
|
||||
declare
|
||||
param $ritary
|
||||
|
||||
param $three = 3
|
||||
|
||||
div {arb=$ritrary}
|
||||
OtherComponent {param3=$three}
|
||||
"#,
|
||||
"inp",
|
||||
)
|
||||
.unwrap();
|
||||
assert_yaml_snapshot!(compile_functions(
|
||||
&pull_out_entrypoints(template, "TemplateName").unwrap()
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ pub struct ParamDef {
|
||||
pub name: IStr,
|
||||
// TODO type information
|
||||
pub default: Option<Expression>,
|
||||
pub locator: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
|
@ -0,0 +1,85 @@
|
||||
---
|
||||
source: hornbeam_ir/src/ast_to_ir.rs
|
||||
expression: "compile_functions(&pull_out_entrypoints(template,\n \"TemplateName\").unwrap()).unwrap()"
|
||||
---
|
||||
TemplateName:
|
||||
param_defs:
|
||||
- name: ritary
|
||||
default: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 3
|
||||
column: 5
|
||||
- name: three
|
||||
default:
|
||||
IntLiteral:
|
||||
val: 3
|
||||
locator:
|
||||
filename: inp
|
||||
line: 5
|
||||
column: 5
|
||||
steps:
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "<div"
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: " arb=\""
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 1
|
||||
- WriteEval:
|
||||
escape: true
|
||||
expr:
|
||||
Variable:
|
||||
name: ritrary
|
||||
loc:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 10
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "\""
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: ">"
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 1
|
||||
- Call:
|
||||
name: OtherComponent
|
||||
args:
|
||||
param3:
|
||||
Variable:
|
||||
name: three
|
||||
loc:
|
||||
filename: inp
|
||||
line: 8
|
||||
column: 28
|
||||
slots:
|
||||
main: []
|
||||
locator:
|
||||
filename: inp
|
||||
line: 8
|
||||
column: 5
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "</div>"
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 1
|
@ -3,67 +3,74 @@ source: hornbeam_ir/src/ast_to_ir.rs
|
||||
expression: "pull_out_entrypoints(template, \"TemplateName\").unwrap()"
|
||||
---
|
||||
TemplateName:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
children:
|
||||
- DefineFragment:
|
||||
name: TemplateName__Frag1
|
||||
blocks: []
|
||||
loc:
|
||||
filename: inp
|
||||
line: 3
|
||||
column: 5
|
||||
- DefineFragment:
|
||||
name: TemplateName__Footer
|
||||
blocks: []
|
||||
loc:
|
||||
filename: inp
|
||||
line: 9
|
||||
column: 5
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
children:
|
||||
- DefineFragment:
|
||||
name: TemplateName__Frag1
|
||||
blocks: []
|
||||
loc:
|
||||
filename: inp
|
||||
line: 3
|
||||
column: 5
|
||||
- DefineFragment:
|
||||
name: TemplateName__Footer
|
||||
blocks: []
|
||||
loc:
|
||||
filename: inp
|
||||
line: 9
|
||||
column: 5
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
TemplateName__Footer:
|
||||
- Text:
|
||||
pieces:
|
||||
- Literal: Or even adjacent ones
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- Text:
|
||||
pieces:
|
||||
- Literal: Or even adjacent ones
|
||||
TemplateName__Frag1:
|
||||
- HtmlElement:
|
||||
name: span
|
||||
children:
|
||||
- Text:
|
||||
pieces:
|
||||
- Literal: This is a fragment!!
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 4
|
||||
column: 9
|
||||
- DefineFragment:
|
||||
name: TemplateName__Frag2
|
||||
blocks: []
|
||||
loc:
|
||||
filename: inp
|
||||
line: 6
|
||||
column: 9
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: span
|
||||
children:
|
||||
- Text:
|
||||
pieces:
|
||||
- Literal: This is a fragment!!
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 4
|
||||
column: 9
|
||||
- DefineFragment:
|
||||
name: TemplateName__Frag2
|
||||
blocks: []
|
||||
loc:
|
||||
filename: inp
|
||||
line: 6
|
||||
column: 9
|
||||
TemplateName__Frag2:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
children:
|
||||
- Text:
|
||||
pieces:
|
||||
- Literal: "There's no problem having nested fragments!"
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 13
|
||||
|
||||
param_defs: ~
|
||||
blocks:
|
||||
- HtmlElement:
|
||||
name: div
|
||||
children:
|
||||
- Text:
|
||||
pieces:
|
||||
- Literal: "There's no problem having nested fragments!"
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 13
|
||||
|
Loading…
x
Reference in New Issue
Block a user