diff --git a/hornbeam_grammar/src/lib.rs b/hornbeam_grammar/src/lib.rs
index 0ca6034..3eacfa6 100644
--- a/hornbeam_grammar/src/lib.rs
+++ b/hornbeam_grammar/src/lib.rs
@@ -1,6 +1,8 @@
pub mod ast;
mod parser;
+
use serde::Serialize;
+use std::fmt::{Display, Formatter};
use arc_interner::ArcIntern;
pub use parser::parse_template;
@@ -31,4 +33,30 @@ impl Locator {
column: col.min(u16::MAX as usize) as u16,
}
}
+
+ pub fn empty() -> Locator {
+ Locator {
+ filename: intern(""),
+ line: 0,
+ column: 0,
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.filename.is_empty() && self.line == 0 && self.column == 0
+ }
+}
+
+impl Display for Locator {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ if self.is_empty() {
+ write!(f, "unknown location")
+ } else {
+ write!(
+ f,
+ "file {:?} @ line {} (col {})",
+ self.filename, self.line, self.column
+ )
+ }
+ }
}
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__fragments_and_slots.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__fragments_and_slots.snap
index c071cb8..9cc7605 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__fragments_and_slots.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__fragments_and_slots.snap
@@ -1,6 +1,6 @@
---
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 \"#).unwrap()"
+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()"
---
blocks:
- HtmlElement:
@@ -18,22 +18,50 @@ blocks:
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 4
+ column: 9
+ loc:
+ filename: inp
+ line: 3
+ column: 5
- DefineFragment:
name: MainPart
blocks:
- DefineExpandSlot:
name: main
optional: false
+ loc:
+ filename: inp
+ line: 7
+ column: 9
- HtmlElement:
name: div
children:
- DefineExpandSlot:
name: footer
optional: true
+ loc:
+ filename: inp
+ line: 9
+ column: 13
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 8
+ column: 9
+ loc:
+ filename: inp
+ line: 6
+ column: 5
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks-2.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks-2.snap
index a608306..c15ad5b 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks-2.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks-2.snap
@@ -1,6 +1,6 @@
---
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 \"#).unwrap()"
+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()"
---
blocks:
- IfBlock:
@@ -27,6 +27,10 @@ blocks:
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 3
+ column: 3
else_blocks:
- IfBlock:
condition:
@@ -56,8 +60,20 @@ blocks:
pieces:
- Literal: "Not quite, but fairly close. What kind of world is this?"
attributes: {}
+ loc:
+ filename: inp
+ line: 6
+ column: 3
else_blocks:
- Text:
pieces:
- Literal: "Not even close, eh?"
+ loc:
+ filename: inp
+ line: 5
+ column: 6
+ loc:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks.snap
index 1268c91..4817ad0 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__if_blocks.snap
@@ -1,6 +1,6 @@
---
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 \"#).unwrap()"
+expression: "parse_template(r#\"\nif 10 / 2 == 5 or $point.x == 42 // div for a div?\n div\n \"#,\n \"inp\").unwrap()"
---
blocks:
- IfBlock:
@@ -26,7 +26,15 @@ blocks:
obj:
Variable:
name: point
+ loc:
+ filename: inp
+ line: 2
+ column: 19
ident: x
+ loc:
+ filename: inp
+ line: 2
+ column: 25
right:
IntLiteral:
val: 42
@@ -37,5 +45,13 @@ blocks:
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 3
+ column: 5
else_blocks: []
+ loc:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__localisation.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__localisation.snap
index 0667bd3..ec07c13 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__localisation.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__localisation.snap
@@ -1,6 +1,6 @@
---
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 \"#).unwrap()"
+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()"
---
blocks:
- HtmlElement:
@@ -17,6 +17,10 @@ blocks:
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 3
+ column: 5
- ComponentElement:
name: MainBody
slots:
@@ -35,9 +39,21 @@ blocks:
obj:
Variable:
name: messages
+ loc:
+ filename: inp
+ line: 8
+ column: 27
ident: len
args: []
+ loc:
+ filename: inp
+ line: 8
+ column: 36
attributes: {}
+ loc:
+ filename: inp
+ line: 5
+ column: 5
- ComponentElement:
name: Footer
slots:
@@ -53,9 +69,25 @@ blocks:
FunctionCall:
name: today
args: []
+ loc:
+ filename: inp
+ line: 11
+ column: 34
ident: year
+ loc:
+ filename: inp
+ line: 11
+ column: 41
attributes: {}
+ loc:
+ filename: inp
+ line: 10
+ column: 5
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__simple_parses_correct.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__simple_parses_correct.snap
index f7340cf..7b03b4a 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__simple_parses_correct.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__simple_parses_correct.snap
@@ -1,6 +1,6 @@
---
source: hornbeam_grammar/src/parser.rs
-expression: "parse_template(r#\"\n// This is a simple Hornbeam template that just shows a
\ndiv\n \"#).unwrap()"
+expression: "parse_template(r#\"\n// This is a simple Hornbeam template that just shows a
\ndiv\n \"#,\n \"inp\").unwrap()"
---
blocks:
- HtmlElement:
@@ -9,4 +9,8 @@ blocks:
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 3
+ column: 1
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__string_interpolations_nested.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__string_interpolations_nested.snap
index eebf03b..514f802 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__string_interpolations_nested.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__string_interpolations_nested.snap
@@ -1,6 +1,6 @@
---
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 \"#).unwrap()"
+expression: "parse_template(r#\"\nMyComponent\n :someslot\n ''\n ${\"abc\" + \"def${ 1 + 1 }\"}\n Not too bad now.\n ''\n \"#,\n \"inp\").unwrap()"
---
blocks:
- ComponentElement:
@@ -30,4 +30,8 @@ blocks:
- Literal: "\n"
- Literal: Not too bad now.
attributes: {}
+ loc:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__supply_slots_to_components_only-2.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__supply_slots_to_components_only-2.snap
index 717dc0b..5d620cb 100644
--- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__supply_slots_to_components_only-2.snap
+++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__supply_slots_to_components_only-2.snap
@@ -1,6 +1,6 @@
---
source: hornbeam_grammar/src/parser.rs
-expression: "parse_template(r#\"\nMyComponent\n :someslot\n \"That's better!\"\n \"#).unwrap()"
+expression: "parse_template(r#\"\nMyComponent\n :someslot\n \"That's better!\"\n \"#,\n \"inp\").unwrap()"
---
blocks:
- ComponentElement:
@@ -11,4 +11,8 @@ blocks:
pieces:
- Literal: "That's better!"
attributes: {}
+ loc:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_interpreter/src/engine.rs b/hornbeam_interpreter/src/engine.rs
index 39be15f..9ec8ab4 100644
--- a/hornbeam_interpreter/src/engine.rs
+++ b/hornbeam_interpreter/src/engine.rs
@@ -3,6 +3,7 @@ use crate::InterpreterError;
use async_recursion::async_recursion;
use bevy_reflect::{FromReflect, Reflect, ReflectRef};
use fluent_templates::lazy_static::lazy_static;
+use hornbeam_grammar::Locator;
use hornbeam_ir::ir::{Expression, Step, StepDef, StringPiece};
use itertools::Itertools;
use std::any::{Any, TypeId};
@@ -12,22 +13,22 @@ use std::fmt::Write;
use std::ops::Deref;
use std::sync::Arc;
-pub(crate) struct FilledSlot<'a, L> {
+pub(crate) struct FilledSlot<'a> {
pub scope_idx: usize,
- pub steps: &'a [Step
],
+ pub steps: &'a [Step],
}
-pub(crate) struct Scope<'a, L> {
+pub(crate) struct Scope<'a> {
pub variables: BTreeMap,
- pub slots: BTreeMap>,
+ pub slots: BTreeMap>,
}
-pub(crate) struct Interpreter<'a, L, O, LS> {
+pub(crate) struct Interpreter<'a, O, LS> {
pub(crate) entrypoint: String,
- pub(crate) program: &'a BTreeMap>>>,
+ pub(crate) program: &'a BTreeMap>>,
pub(crate) output: O,
pub(crate) localisation: Arc,
- pub(crate) scopes: Vec>,
+ pub(crate) scopes: Vec>,
}
#[derive(Debug)]
@@ -90,14 +91,12 @@ impl Clone for Value {
}
}
-impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send>
- Interpreter<'a, L, O, LS>
-{
+impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpreter<'a, O, LS> {
#[async_recursion]
pub async fn run_steps(
&mut self,
scope_idx: usize,
- steps: &'a [Step],
+ steps: &'a [Step],
) -> Result<(), InterpreterError> {
for step in steps {
self.run_step(scope_idx, step).await?;
@@ -108,7 +107,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
pub async fn run_step(
&mut self,
scope_idx: usize,
- step: &'a Step,
+ step: &'a Step,
) -> Result<(), InterpreterError> {
match &step.def {
StepDef::WriteLiteral { escape, text } => {
@@ -128,9 +127,9 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
}
}
StepDef::WriteEval { escape, expr } => {
- let val = self.evaluate_expression(scope_idx, expr)?;
+ let val = self.evaluate_expression(scope_idx, expr, &step.locator)?;
let mut buf = String::new();
- self.write_out_value_as_display_string(val, &mut buf)?;
+ self.write_out_value_as_display_string(val, &mut buf, &step.locator)?;
let to_write = if *escape {
html_escape::encode_safe(&buf)
@@ -148,7 +147,8 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
true_steps,
false_steps,
} => {
- let evaled_condition = self.evaluate_expression(scope_idx, condition)?;
+ let evaled_condition =
+ self.evaluate_expression(scope_idx, condition, &step.locator)?;
let steps = match evaled_condition {
Value::Bool(true) => true_steps,
Value::Bool(false) => false_steps,
@@ -156,7 +156,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
return Err(InterpreterError::TypeError {
context: "If".to_string(),
conflict: format!("condition {other:?} is not a boolean."),
- location: "".to_string(),
+ location: step.locator.clone(),
});
}
};
@@ -169,7 +169,8 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
body_steps,
empty_steps,
} => {
- let iterable_evaled = self.evaluate_expression(scope_idx, iterable)?;
+ let iterable_evaled =
+ self.evaluate_expression(scope_idx, iterable, &step.locator)?;
match iterable_evaled {
Value::List(list) => {
@@ -191,7 +192,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
return Err(InterpreterError::TypeError {
context: "For".to_string(),
conflict: format!("not iterable: {other:?}."),
- location: "".to_string(),
+ location: step.locator.clone(),
});
}
}
@@ -201,7 +202,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
for (key, expr) in args {
evaled_args.insert(
String::from(key as &str),
- self.evaluate_expression(scope_idx, expr)?,
+ self.evaluate_expression(scope_idx, expr, &step.locator)?,
);
}
@@ -228,7 +229,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
return Err(InterpreterError::TypeError {
context: "Call".to_string(),
conflict: format!("no entrypoint for {name:?}."),
- location: "".to_string(),
+ location: step.locator.clone(),
});
};
@@ -246,7 +247,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Err(InterpreterError::TypeError {
context: format!("Required slot '{name}' not filled"),
conflict: format!("slot was left empty."),
- location: "".to_string(),
+ location: step.locator.clone(),
})
} else {
Ok(())
@@ -266,113 +267,114 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
&self,
scope_idx: usize,
expr: &'a Expression,
+ loc: &Locator,
) -> Result> {
match expr {
Expression::Add { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint + rint)),
(lother, rother) => Err(InterpreterError::TypeError {
context: "Add".to_string(),
conflict: format!("can't add {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::Sub { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint - rint)),
(lother, rother) => Err(InterpreterError::TypeError {
context: "Sub".to_string(),
conflict: format!("can't sub {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::Mul { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint * rint)),
(lother, rother) => Err(InterpreterError::TypeError {
context: "Mul".to_string(),
conflict: format!("can't mul {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::Div { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint / rint)),
(lother, rother) => Err(InterpreterError::TypeError {
context: "Div".to_string(),
conflict: format!("can't div {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::Negate { sub } => {
- let sval = self.evaluate_expression(scope_idx, &sub)?;
+ let sval = self.evaluate_expression(scope_idx, &sub, loc)?;
match sval {
Value::Int(sint) => Ok(Value::Int(-sint)),
sother => Err(InterpreterError::TypeError {
context: "Negate".to_string(),
conflict: format!("can't negate {sother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::BAnd { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool && rbool)),
(lother, rother) => Err(InterpreterError::TypeError {
context: "BAnd".to_string(),
conflict: format!("can't and together {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::BOr { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool || rbool)),
(lother, rother) => Err(InterpreterError::TypeError {
context: "BOr".to_string(),
conflict: format!("can't or together {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::BNot { sub } => {
- let sval = self.evaluate_expression(scope_idx, &sub)?;
+ let sval = self.evaluate_expression(scope_idx, &sub, loc)?;
match sval {
Value::Bool(sbool) => Ok(Value::Bool(!sbool)),
sother => Err(InterpreterError::TypeError {
context: "BNot".to_string(),
conflict: format!("can't invert {sother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::Equals { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool == rbool)),
@@ -380,13 +382,13 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
(lother, rother) => Err(InterpreterError::TypeError {
context: "Equals".to_string(),
conflict: format!("can't test {lother:?} and {rother:?} for equality!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::ListAdd { left, right } => {
- let lval = self.evaluate_expression(scope_idx, &left)?;
- let rval = self.evaluate_expression(scope_idx, &right)?;
+ let lval = self.evaluate_expression(scope_idx, &left, loc)?;
+ let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) {
(Value::List(mut llist), Value::List(rlist)) => {
@@ -396,14 +398,14 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
(lother, rother) => Err(InterpreterError::TypeError {
context: "ListAdd".to_string(),
conflict: format!("can't list-add {lother:?} and {rother:?}!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
Expression::List { elements } => {
let mut result = Vec::with_capacity(elements.len());
for expr in elements {
- result.push(self.evaluate_expression(scope_idx, expr)?);
+ result.push(self.evaluate_expression(scope_idx, expr, loc)?);
}
Ok(Value::List(result))
}
@@ -411,12 +413,12 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Expression::StringExpr(sexpr) => {
let mut output = String::new();
for piece in &sexpr.pieces {
- self.evaluate_string_piece(scope_idx, piece, &mut output)?;
+ self.evaluate_string_piece(scope_idx, piece, &mut output, loc)?;
}
Ok(Value::Str(Arc::new(output)))
}
Expression::FieldLookup { obj, ident, loc } => {
- let obj_val = self.evaluate_expression(scope_idx, obj)?;
+ let obj_val = self.evaluate_expression(scope_idx, obj, loc)?;
match obj_val {
Value::Reflective(reflective) => match reflective.reflect_ref() {
ReflectRef::Struct(ss) => {
@@ -426,7 +428,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"),
conflict: format!("{reflective:?} is a reflective struct that does not have that field!"),
- location: "".to_string(),
+ location: loc.clone(),
})
}
}
@@ -439,14 +441,14 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"),
conflict: format!("{reflective:?} is a reflective tuple struct that does not have that field!"),
- location: "".to_string(),
+ location: loc.clone(),
})
}
} else {
Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"),
conflict: format!("{reflective:?} is a reflective tuple struct that does not have names for field!"),
- location: "".to_string(),
+ location: loc.clone(),
})
}
}
@@ -455,13 +457,13 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
conflict: format!(
"{reflective:?} is of a reflective type that has no fields!"
),
- location: "".to_string(),
+ location: loc.clone(),
}),
},
other => Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"),
conflict: format!("{other:?} is of a type that has no fields!"),
- location: "".to_string(),
+ location: loc.clone(),
}),
}
}
@@ -479,7 +481,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
conflict: format!(
"No local by that name; the only locals are: {locals_list}."
),
- location: "".to_string(),
+ location: loc.clone(),
})
}
}
@@ -494,6 +496,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
&self,
val: Value,
output: &mut String,
+ loc: &Locator,
) -> Result<(), InterpreterError> {
match val {
Value::Str(s) => {
@@ -516,7 +519,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
conflict: format!(
"Don't know how to write {reflective:?} as a sensible string output."
),
- location: "".to_string(),
+ location: loc.clone(),
})
}
Value::List(_) => {
@@ -526,7 +529,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
conflict: format!(
"Don't know how to write List[...] as a sensible string output."
),
- location: "".to_string(),
+ location: loc.clone(),
})
}
}
@@ -537,6 +540,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
scope_idx: usize,
piece: &StringPiece,
output: &mut String,
+ loc: &Locator,
) -> Result<(), InterpreterError> {
match piece {
StringPiece::Literal(lit) => {
@@ -544,8 +548,8 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Ok(())
}
StringPiece::Interpolation(expr) => {
- let val = self.evaluate_expression(scope_idx, expr)?;
- self.write_out_value_as_display_string(val, output)?;
+ let val = self.evaluate_expression(scope_idx, expr, loc)?;
+ self.write_out_value_as_display_string(val, output, loc)?;
Ok(())
}
StringPiece::Localise {
@@ -555,7 +559,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
let mut parameters_evaluated = BTreeMap::new();
for (key, expr) in parameters {
parameters_evaluated
- .insert(key as &str, self.evaluate_expression(scope_idx, expr)?);
+ .insert(key as &str, self.evaluate_expression(scope_idx, expr, loc)?);
}
let localised_text = self
@@ -564,7 +568,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
.map_err(|underlying| InterpreterError::Localisation {
underlying,
trans_key: (trans_key as &str).to_owned(),
- location: "".to_string(),
+ location: loc.clone(),
})?;
output.push_str(&localised_text);
@@ -581,7 +585,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
return Err(InterpreterError::TypeError {
context: format!("No entrypoint called {:?}", self.entrypoint),
conflict: "".to_string(),
- location: "".to_string(),
+ location: Locator::empty(),
});
};
self.run_steps(0, main).await?;
diff --git a/hornbeam_interpreter/src/interface.rs b/hornbeam_interpreter/src/interface.rs
index 5b084ca..40e44f3 100644
--- a/hornbeam_interpreter/src/interface.rs
+++ b/hornbeam_interpreter/src/interface.rs
@@ -37,10 +37,10 @@ pub trait OutputSystem {
pub use crate::engine::Value;
use crate::InterpreterError;
-pub struct LoadedTemplates {
+pub struct LoadedTemplates {
// todo might be tempted to use e.g. ouroboros here, to keep the file source adjacent?
// or do we just staticify?
- template_functions: BTreeMap>>>,
+ template_functions: BTreeMap>>,
localisation: Arc,
}
@@ -56,7 +56,7 @@ impl Params {
}
}
-impl<'a, LS> LoadedTemplates<(), LS> {
+impl<'a, LS> LoadedTemplates {
pub fn new(localisation_system: LS) -> Self {
LoadedTemplates {
template_functions: Default::default(),
@@ -103,7 +103,7 @@ impl<'a, LS> LoadedTemplates<(), LS> {
template_name: &str,
fragment_name: Option<&str>,
params: Params,
- ) -> PreparedTemplate<'a, (), LS> {
+ ) -> PreparedTemplate<'a, LS> {
PreparedTemplate {
all_instructions: Arc::new(self.template_functions.clone()),
entrypoint: if let Some(frag) = fragment_name {
@@ -120,14 +120,14 @@ impl<'a, LS> LoadedTemplates<(), LS> {
}
}
-pub struct PreparedTemplate<'a, L, LS> {
- pub(crate) all_instructions: Arc>>>>,
+pub struct PreparedTemplate<'a, LS> {
+ pub(crate) all_instructions: Arc>>>,
pub(crate) entrypoint: String,
- pub(crate) scope: Scope<'a, L>,
+ pub(crate) scope: Scope<'a>,
pub localisation: Arc,
}
-impl<'a, L: Sync + Send, LS: LocalisationSystem + Sync + Send> PreparedTemplate<'a, L, LS> {
+impl<'a, LS: LocalisationSystem + Sync + Send> PreparedTemplate<'a, LS> {
pub async fn render_to_output(
self,
output: O,
diff --git a/hornbeam_interpreter/src/lib.rs b/hornbeam_interpreter/src/lib.rs
index 5aef200..74f8443 100644
--- a/hornbeam_interpreter/src/lib.rs
+++ b/hornbeam_interpreter/src/lib.rs
@@ -5,7 +5,7 @@ pub(crate) mod interface;
pub mod localisation;
-use hornbeam_grammar::ParseError;
+use hornbeam_grammar::{Locator, ParseError};
use hornbeam_ir::AstToIrError;
use thiserror::Error;
@@ -15,13 +15,13 @@ pub enum InterpreterError {
TypeError {
context: String,
conflict: String,
- location: String,
+ location: Locator,
},
#[error("localisation lookup of {trans_key:?} at {location} failed: {underlying:?}")]
Localisation {
underlying: LE,
trans_key: String,
- location: String,
+ location: Locator,
},
#[error("failed to write to output: {underlying:?}")]
OutputError { underlying: OE },
diff --git a/hornbeam_ir/src/ast_to_ir.rs b/hornbeam_ir/src/ast_to_ir.rs
index 86ab2a7..5b4913a 100644
--- a/hornbeam_ir/src/ast_to_ir.rs
+++ b/hornbeam_ir/src/ast_to_ir.rs
@@ -1,6 +1,6 @@
use crate::ir::{Step, StepDef};
use hornbeam_grammar::ast::{Block, Expression, StringExpr, StringPiece, Template};
-use hornbeam_grammar::intern;
+use hornbeam_grammar::{intern, Locator};
use std::borrow::Cow;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
@@ -115,7 +115,7 @@ fn pull_out_entrypoints_from_block<'a>(
/// Step 2. Compile the AST to IR steps.
pub(crate) fn compile_functions<'a>(
functions: &BTreeMap>,
-) -> Result>>, AstToIrError> {
+) -> Result>, AstToIrError> {
let mut result = BTreeMap::new();
for (func_name, func_blocks) in functions {
let mut steps = Vec::new();
@@ -129,7 +129,7 @@ pub(crate) fn compile_functions<'a>(
fn compile_ast_block_to_steps<'a>(
block: &Block,
- instructions: &mut Vec>,
+ instructions: &mut Vec,
) -> Result<(), AstToIrError> {
match block {
Block::HtmlElement(he) => {
@@ -147,7 +147,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false,
text: intern(text),
},
- locator: (),
+ locator: he.loc.clone(),
});
// Write attributes
@@ -157,7 +157,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false,
text: intern(format!(" {attr_name}=\"")),
},
- locator: (),
+ locator: he.loc.clone(),
});
instructions.push(Step {
@@ -165,7 +165,7 @@ fn compile_ast_block_to_steps<'a>(
escape: true,
expr: attr_expr.clone(),
},
- locator: (),
+ locator: he.loc.clone(),
});
instructions.push(Step {
@@ -173,7 +173,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false,
text: intern("\""),
},
- locator: (),
+ locator: he.loc.clone(),
});
}
@@ -183,7 +183,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false,
text: intern(">"),
},
- locator: (),
+ locator: he.loc.clone(),
});
for child in &he.children {
@@ -195,7 +195,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false,
text: intern(format!("{}>", he.name)),
},
- locator: (),
+ locator: he.loc.clone(),
});
}
Block::ComponentElement(ce) => {
@@ -214,7 +214,7 @@ fn compile_ast_block_to_steps<'a>(
args: ce.attributes.clone(),
slots: all_slots_steps,
},
- locator: (),
+ locator: ce.loc.clone(),
})
}
Block::IfBlock(ifb) => {
@@ -235,7 +235,7 @@ fn compile_ast_block_to_steps<'a>(
true_steps: true_instrs,
false_steps: false_instrs,
},
- locator: (),
+ locator: ifb.loc.clone(),
});
}
Block::Text(text) => {
@@ -247,7 +247,7 @@ fn compile_ast_block_to_steps<'a>(
text: lit.clone(),
escape: true,
},
- locator: (),
+ locator: Locator::empty(),
});
}
StringPiece::Interpolation(expr) => {
@@ -256,7 +256,7 @@ fn compile_ast_block_to_steps<'a>(
expr: expr.clone(),
escape: true,
},
- locator: (),
+ locator: Locator::empty(),
});
}
piece @ StringPiece::Localise { .. } => {
@@ -267,7 +267,7 @@ fn compile_ast_block_to_steps<'a>(
}),
escape: true,
},
- locator: (),
+ locator: Locator::empty(),
});
}
}
@@ -279,7 +279,7 @@ fn compile_ast_block_to_steps<'a>(
name: slot.name.clone(),
optional: slot.optional,
},
- locator: (),
+ locator: slot.loc.clone(),
});
}
Block::DefineFragment(frag) => {
@@ -290,7 +290,7 @@ fn compile_ast_block_to_steps<'a>(
args: Default::default(),
slots: BTreeMap::new(),
},
- locator: (),
+ locator: frag.loc.clone(),
});
}
}
diff --git a/hornbeam_ir/src/ir.rs b/hornbeam_ir/src/ir.rs
index 8aa144a..dd9049f 100644
--- a/hornbeam_ir/src/ir.rs
+++ b/hornbeam_ir/src/ir.rs
@@ -1,24 +1,24 @@
pub use hornbeam_grammar::ast::{Expression, StringPiece};
-use hornbeam_grammar::IStr;
+use hornbeam_grammar::{IStr, Locator};
use serde::Serialize;
use std::collections::BTreeMap;
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
-pub struct Function {
+pub struct Function {
pub name: IStr,
- pub locator: L,
- pub steps: Vec>,
+ pub locator: Option,
+ pub steps: Vec,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
-pub struct Step {
+pub struct Step {
#[serde(flatten)]
- pub def: StepDef,
- pub locator: L,
+ pub def: StepDef,
+ pub locator: Locator,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
-pub enum StepDef {
+pub enum StepDef {
WriteLiteral {
escape: bool,
text: IStr,
@@ -29,20 +29,20 @@ pub enum StepDef {
},
If {
condition: Expression,
- true_steps: Vec>,
- false_steps: Vec>,
+ true_steps: Vec,
+ false_steps: Vec,
},
For {
iterable: Expression,
// TODO!
binding: IStr,
- body_steps: Vec>,
- empty_steps: Vec>,
+ body_steps: Vec,
+ empty_steps: Vec,
},
Call {
name: IStr,
args: BTreeMap,
- slots: BTreeMap>>,
+ slots: BTreeMap>,
},
CallSlotWithParentScope {
name: IStr,
diff --git a/hornbeam_ir/src/lib.rs b/hornbeam_ir/src/lib.rs
index 7f28e52..92cf1be 100644
--- a/hornbeam_ir/src/lib.rs
+++ b/hornbeam_ir/src/lib.rs
@@ -22,7 +22,7 @@ pub use ast_to_ir::AstToIrError;
pub fn ast_to_optimised_ir(
template_name: &str,
template: Template,
-) -> Result>>, AstToIrError> {
+) -> Result>, AstToIrError> {
let entrypoints = pull_out_entrypoints(template, template_name)?;
let mut compiled_funcs = ast_to_ir::compile_functions(&entrypoints)?;
for steps in compiled_funcs.values_mut() {
diff --git a/hornbeam_ir/src/peephole.rs b/hornbeam_ir/src/peephole.rs
index db58392..79ed416 100644
--- a/hornbeam_ir/src/peephole.rs
+++ b/hornbeam_ir/src/peephole.rs
@@ -48,7 +48,7 @@ fn peephole_opt]) -> ()>(
*steps = result;
}
-fn apply_peephole_pass<'a, L, F: Fn(&mut Vec>)>(steps: &mut Vec>, pass: &F) {
+fn apply_peephole_pass)>(steps: &mut Vec, pass: &F) {
pass(steps);
for step in steps {
match &mut step.def {
@@ -83,7 +83,7 @@ fn apply_peephole_pass<'a, L, F: Fn(&mut Vec>)>(steps: &mut Vec>
//// Peephole Passes
/// Given a WriteEval step that just writes literals, convert it to a WriteLiteral step.
-fn pass_write_eval_literal_to_write_literal(steps: &mut Vec>) {
+fn pass_write_eval_literal_to_write_literal(steps: &mut Vec) {
'next_step: for step in steps {
if let StepDef::WriteEval {
escape,
@@ -110,7 +110,7 @@ fn pass_write_eval_literal_to_write_literal(steps: &mut Vec>) {
}
/// Given a WriteLiteral step that escapes the HTML as it is written, precompute the escaped HTML.
-fn pass_write_literal_preescape(steps: &mut Vec>) {
+fn pass_write_literal_preescape(steps: &mut Vec) {
for step in steps {
if let StepDef::WriteLiteral { escape, text } = &mut step.def {
if *escape {
@@ -125,7 +125,7 @@ fn pass_write_literal_preescape(steps: &mut Vec>) {
}
/// Given two adjacent WriteLiteral steps, combine them together.
-fn pass_combine_write_literals(steps: &mut Vec>) {
+fn pass_combine_write_literals(steps: &mut Vec) {
peephole_opt(steps, 2, |steps| {
let (l, r) = steps.split_at_mut(1);
let left = l[0].as_mut().unwrap();
@@ -153,7 +153,7 @@ fn pass_combine_write_literals(steps: &mut Vec>) {
//// Apply all passes in the preferred order
-pub fn apply_all_peephole_passes(steps: &mut Vec>) {
+pub fn apply_all_peephole_passes(steps: &mut Vec) {
apply_peephole_pass(steps, &pass_write_eval_literal_to_write_literal);
apply_peephole_pass(steps, &pass_write_literal_preescape);
apply_peephole_pass(steps, &pass_combine_write_literals);
@@ -167,7 +167,7 @@ mod tests {
use insta::assert_yaml_snapshot;
use std::collections::BTreeMap;
- fn parse_ir_and_peephole(text: &str) -> BTreeMap>> {
+ fn parse_ir_and_peephole(text: &str) -> BTreeMap> {
let template = parse_template(text, "inp").unwrap();
let entrypoints = pull_out_entrypoints(template, "TemplateName").unwrap();
let mut compiled = compile_functions(&entrypoints).unwrap();
diff --git a/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile.snap b/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile.snap
index 392fbbb..358638d 100644
--- a/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile.snap
+++ b/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile.snap
@@ -6,67 +6,112 @@ TemplateName:
- WriteLiteral:
escape: false
text: ""
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
- Call:
name: TemplateName__Frag1
args: {}
slots: {}
- locator: ~
+ locator:
+ filename: inp
+ line: 3
+ column: 5
- Call:
name: TemplateName__Footer
args: {}
slots: {}
- locator: ~
+ locator:
+ filename: inp
+ line: 9
+ column: 5
- WriteLiteral:
escape: false
text: "
"
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
TemplateName__Footer:
- WriteLiteral:
escape: true
text: Or even adjacent ones
- locator: ~
+ locator:
+ filename: ""
+ line: 0
+ column: 0
TemplateName__Frag1:
- WriteLiteral:
escape: false
text: ""
- locator: ~
+ locator:
+ filename: inp
+ line: 4
+ column: 9
- WriteLiteral:
escape: true
text: This is a fragment!!
- locator: ~
+ locator:
+ filename: ""
+ line: 0
+ column: 0
- WriteLiteral:
escape: false
text: ""
- locator: ~
+ locator:
+ filename: inp
+ line: 4
+ column: 9
- Call:
name: TemplateName__Frag2
args: {}
slots: {}
- locator: ~
+ locator:
+ filename: inp
+ line: 6
+ column: 9
TemplateName__Frag2:
- WriteLiteral:
escape: false
text: ""
- locator: ~
+ locator:
+ filename: inp
+ line: 7
+ column: 13
- WriteLiteral:
escape: true
text: "There's no problem having nested fragments!"
- locator: ~
+ locator:
+ filename: ""
+ line: 0
+ column: 0
- WriteLiteral:
escape: false
text: "
"
- locator: ~
+ locator:
+ filename: inp
+ line: 7
+ column: 13
diff --git a/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile_attributes.snap b/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile_attributes.snap
index d533e54..500d76c 100644
--- a/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile_attributes.snap
+++ b/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__compile_attributes.snap
@@ -6,58 +6,98 @@ TemplateName:
- WriteLiteral:
escape: false
text: ""
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
- WriteLiteral:
escape: true
text: This is a div with a few extras
- locator: ~
+ locator:
+ filename: ""
+ line: 0
+ column: 0
- Call:
name: OtherComponent
args:
@@ -71,11 +111,21 @@ TemplateName:
param3:
Variable:
name: three
+ loc:
+ filename: inp
+ line: 4
+ column: 52
slots:
main: []
- locator: ~
+ locator:
+ filename: inp
+ line: 4
+ column: 5
- WriteLiteral:
escape: false
text: "
"
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
diff --git a/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__pull_out_entrypoints.snap b/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__pull_out_entrypoints.snap
index 7ea0e41..12dc799 100644
--- a/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__pull_out_entrypoints.snap
+++ b/hornbeam_ir/src/snapshots/hornbeam_ir__ast_to_ir__tests__pull_out_entrypoints.snap
@@ -9,12 +9,24 @@ TemplateName:
- 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:
@@ -29,9 +41,17 @@ TemplateName__Frag1:
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
@@ -42,4 +62,8 @@ TemplateName__Frag2:
classes: []
dom_id: ~
attributes: {}
+ loc:
+ filename: inp
+ line: 7
+ column: 13
diff --git a/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__adjacent_text.snap b/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__adjacent_text.snap
index d45cd67..d7618ab 100644
--- a/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__adjacent_text.snap
+++ b/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__adjacent_text.snap
@@ -6,39 +6,63 @@ TemplateName:
- WriteLiteral:
escape: false
text: ""
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
- Call:
name: TemplateName__Frag1
args: {}
slots: {}
- locator: ~
+ locator:
+ filename: inp
+ line: 3
+ column: 5
- Call:
name: TemplateName__Footer
args: {}
slots: {}
- locator: ~
+ locator:
+ filename: inp
+ line: 9
+ column: 5
- WriteLiteral:
escape: false
text: "
"
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
TemplateName__Footer:
- WriteLiteral:
escape: false
text: Or even adjacent ones
- locator: ~
+ locator:
+ filename: ""
+ line: 0
+ column: 0
TemplateName__Frag1:
- WriteLiteral:
escape: false
text: "This is a fragment!!"
- locator: ~
+ locator:
+ filename: inp
+ line: 4
+ column: 9
- Call:
name: TemplateName__Frag2
args: {}
slots: {}
- locator: ~
+ locator:
+ filename: inp
+ line: 6
+ column: 9
TemplateName__Frag2:
- WriteLiteral:
escape: false
text: "There's no problem having <<nested>> fragments!
"
- locator: ~
+ locator:
+ filename: inp
+ line: 7
+ column: 13
diff --git a/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__with_attributes.snap b/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__with_attributes.snap
index ca1f0b3..32acdef 100644
--- a/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__with_attributes.snap
+++ b/hornbeam_ir/src/snapshots/hornbeam_ir__peephole__tests__with_attributes.snap
@@ -6,27 +6,46 @@ TemplateName:
- WriteLiteral:
escape: false
text: "This is a div with a few extras"
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1
- Call:
name: OtherComponent
args:
@@ -40,11 +59,21 @@ TemplateName:
param3:
Variable:
name: three
+ loc:
+ filename: inp
+ line: 4
+ column: 52
slots:
main: []
- locator: ~
+ locator:
+ filename: inp
+ line: 4
+ column: 5
- WriteLiteral:
escape: false
text: "
"
- locator: ~
+ locator:
+ filename: inp
+ line: 2
+ column: 1