Simplify and use locators in the IR and interpreter

This commit is contained in:
Olivier 'reivilibre' 2023-03-01 21:09:10 +00:00
parent f54f2093de
commit 0370de40a1
20 changed files with 471 additions and 163 deletions

View File

@ -1,6 +1,8 @@
pub mod ast; pub mod ast;
mod parser; mod parser;
use serde::Serialize; use serde::Serialize;
use std::fmt::{Display, Formatter};
use arc_interner::ArcIntern; use arc_interner::ArcIntern;
pub use parser::parse_template; pub use parser::parse_template;
@ -31,4 +33,30 @@ impl Locator {
column: col.min(u16::MAX as usize) as u16, 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
)
}
}
} }

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs 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: blocks:
- HtmlElement: - HtmlElement:
@ -18,22 +18,50 @@ blocks:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 4
column: 9
loc:
filename: inp
line: 3
column: 5
- DefineFragment: - DefineFragment:
name: MainPart name: MainPart
blocks: blocks:
- DefineExpandSlot: - DefineExpandSlot:
name: main name: main
optional: false optional: false
loc:
filename: inp
line: 7
column: 9
- HtmlElement: - HtmlElement:
name: div name: div
children: children:
- DefineExpandSlot: - DefineExpandSlot:
name: footer name: footer
optional: true optional: true
loc:
filename: inp
line: 9
column: 13
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 8
column: 9
loc:
filename: inp
line: 6
column: 5
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 2
column: 1

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs 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: blocks:
- IfBlock: - IfBlock:
@ -27,6 +27,10 @@ blocks:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 3
column: 3
else_blocks: else_blocks:
- IfBlock: - IfBlock:
condition: condition:
@ -56,8 +60,20 @@ blocks:
pieces: pieces:
- Literal: "Not quite, but fairly close. What kind of world is this?" - Literal: "Not quite, but fairly close. What kind of world is this?"
attributes: {} attributes: {}
loc:
filename: inp
line: 6
column: 3
else_blocks: else_blocks:
- Text: - Text:
pieces: pieces:
- Literal: "Not even close, eh?" - Literal: "Not even close, eh?"
loc:
filename: inp
line: 5
column: 6
loc:
filename: inp
line: 2
column: 1

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs 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: blocks:
- IfBlock: - IfBlock:
@ -26,7 +26,15 @@ blocks:
obj: obj:
Variable: Variable:
name: point name: point
loc:
filename: inp
line: 2
column: 19
ident: x ident: x
loc:
filename: inp
line: 2
column: 25
right: right:
IntLiteral: IntLiteral:
val: 42 val: 42
@ -37,5 +45,13 @@ blocks:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 3
column: 5
else_blocks: [] else_blocks: []
loc:
filename: inp
line: 2
column: 1

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs 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: blocks:
- HtmlElement: - HtmlElement:
@ -17,6 +17,10 @@ blocks:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 3
column: 5
- ComponentElement: - ComponentElement:
name: MainBody name: MainBody
slots: slots:
@ -35,9 +39,21 @@ blocks:
obj: obj:
Variable: Variable:
name: messages name: messages
loc:
filename: inp
line: 8
column: 27
ident: len ident: len
args: [] args: []
loc:
filename: inp
line: 8
column: 36
attributes: {} attributes: {}
loc:
filename: inp
line: 5
column: 5
- ComponentElement: - ComponentElement:
name: Footer name: Footer
slots: slots:
@ -53,9 +69,25 @@ blocks:
FunctionCall: FunctionCall:
name: today name: today
args: [] args: []
loc:
filename: inp
line: 11
column: 34
ident: year ident: year
loc:
filename: inp
line: 11
column: 41
attributes: {} attributes: {}
loc:
filename: inp
line: 10
column: 5
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 2
column: 1

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs source: hornbeam_grammar/src/parser.rs
expression: "parse_template(r#\"\n// This is a simple Hornbeam template that just shows a <div>\ndiv\n \"#).unwrap()" expression: "parse_template(r#\"\n// This is a simple Hornbeam template that just shows a <div>\ndiv\n \"#,\n \"inp\").unwrap()"
--- ---
blocks: blocks:
- HtmlElement: - HtmlElement:
@ -9,4 +9,8 @@ blocks:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 3
column: 1

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs 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: blocks:
- ComponentElement: - ComponentElement:
@ -30,4 +30,8 @@ blocks:
- Literal: "\n" - Literal: "\n"
- Literal: Not too bad now. - Literal: Not too bad now.
attributes: {} attributes: {}
loc:
filename: inp
line: 2
column: 1

View File

@ -1,6 +1,6 @@
--- ---
source: hornbeam_grammar/src/parser.rs 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: blocks:
- ComponentElement: - ComponentElement:
@ -11,4 +11,8 @@ blocks:
pieces: pieces:
- Literal: "That's better!" - Literal: "That's better!"
attributes: {} attributes: {}
loc:
filename: inp
line: 2
column: 1

View File

@ -3,6 +3,7 @@ use crate::InterpreterError;
use async_recursion::async_recursion; use async_recursion::async_recursion;
use bevy_reflect::{FromReflect, Reflect, ReflectRef}; use bevy_reflect::{FromReflect, Reflect, ReflectRef};
use fluent_templates::lazy_static::lazy_static; use fluent_templates::lazy_static::lazy_static;
use hornbeam_grammar::Locator;
use hornbeam_ir::ir::{Expression, Step, StepDef, StringPiece}; use hornbeam_ir::ir::{Expression, Step, StepDef, StringPiece};
use itertools::Itertools; use itertools::Itertools;
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
@ -12,22 +13,22 @@ use std::fmt::Write;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
pub(crate) struct FilledSlot<'a, L> { pub(crate) struct FilledSlot<'a> {
pub scope_idx: usize, pub scope_idx: usize,
pub steps: &'a [Step<L>], pub steps: &'a [Step],
} }
pub(crate) struct Scope<'a, L> { pub(crate) struct Scope<'a> {
pub variables: BTreeMap<String, Value>, pub variables: BTreeMap<String, Value>,
pub slots: BTreeMap<String, FilledSlot<'a, L>>, pub slots: BTreeMap<String, FilledSlot<'a>>,
} }
pub(crate) struct Interpreter<'a, L, O, LS> { pub(crate) struct Interpreter<'a, O, LS> {
pub(crate) entrypoint: String, pub(crate) entrypoint: String,
pub(crate) program: &'a BTreeMap<String, Arc<Vec<Step<L>>>>, pub(crate) program: &'a BTreeMap<String, Arc<Vec<Step>>>,
pub(crate) output: O, pub(crate) output: O,
pub(crate) localisation: Arc<LS>, pub(crate) localisation: Arc<LS>,
pub(crate) scopes: Vec<Scope<'a, L>>, pub(crate) scopes: Vec<Scope<'a>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -90,14 +91,12 @@ impl Clone for Value {
} }
} }
impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpreter<'a, O, LS> {
Interpreter<'a, L, O, LS>
{
#[async_recursion] #[async_recursion]
pub async fn run_steps( pub async fn run_steps(
&mut self, &mut self,
scope_idx: usize, scope_idx: usize,
steps: &'a [Step<L>], steps: &'a [Step],
) -> Result<(), InterpreterError<LS::Error, O::Error>> { ) -> Result<(), InterpreterError<LS::Error, O::Error>> {
for step in steps { for step in steps {
self.run_step(scope_idx, step).await?; 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( pub async fn run_step(
&mut self, &mut self,
scope_idx: usize, scope_idx: usize,
step: &'a Step<L>, step: &'a Step,
) -> Result<(), InterpreterError<LS::Error, O::Error>> { ) -> Result<(), InterpreterError<LS::Error, O::Error>> {
match &step.def { match &step.def {
StepDef::WriteLiteral { escape, text } => { StepDef::WriteLiteral { escape, text } => {
@ -128,9 +127,9 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
} }
} }
StepDef::WriteEval { escape, expr } => { 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(); 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 { let to_write = if *escape {
html_escape::encode_safe(&buf) html_escape::encode_safe(&buf)
@ -148,7 +147,8 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
true_steps, true_steps,
false_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 { let steps = match evaled_condition {
Value::Bool(true) => true_steps, Value::Bool(true) => true_steps,
Value::Bool(false) => false_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 { return Err(InterpreterError::TypeError {
context: "If".to_string(), context: "If".to_string(),
conflict: format!("condition {other:?} is not a boolean."), 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, body_steps,
empty_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 { match iterable_evaled {
Value::List(list) => { Value::List(list) => {
@ -191,7 +192,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
return Err(InterpreterError::TypeError { return Err(InterpreterError::TypeError {
context: "For".to_string(), context: "For".to_string(),
conflict: format!("not iterable: {other:?}."), 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 { for (key, expr) in args {
evaled_args.insert( evaled_args.insert(
String::from(key as &str), 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 { return Err(InterpreterError::TypeError {
context: "Call".to_string(), context: "Call".to_string(),
conflict: format!("no entrypoint for {name:?}."), 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 { Err(InterpreterError::TypeError {
context: format!("Required slot '{name}' not filled"), context: format!("Required slot '{name}' not filled"),
conflict: format!("slot was left empty."), conflict: format!("slot was left empty."),
location: "".to_string(), location: step.locator.clone(),
}) })
} else { } else {
Ok(()) Ok(())
@ -266,113 +267,114 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
&self, &self,
scope_idx: usize, scope_idx: usize,
expr: &'a Expression, expr: &'a Expression,
loc: &Locator,
) -> Result<Value, InterpreterError<LS::Error, O::Error>> { ) -> Result<Value, InterpreterError<LS::Error, O::Error>> {
match expr { match expr {
Expression::Add { left, right } => { Expression::Add { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint + rint)), (Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint + rint)),
(lother, rother) => Err(InterpreterError::TypeError { (lother, rother) => Err(InterpreterError::TypeError {
context: "Add".to_string(), context: "Add".to_string(),
conflict: format!("can't add {lother:?} and {rother:?}!"), conflict: format!("can't add {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::Sub { left, right } => { Expression::Sub { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint - rint)), (Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint - rint)),
(lother, rother) => Err(InterpreterError::TypeError { (lother, rother) => Err(InterpreterError::TypeError {
context: "Sub".to_string(), context: "Sub".to_string(),
conflict: format!("can't sub {lother:?} and {rother:?}!"), conflict: format!("can't sub {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::Mul { left, right } => { Expression::Mul { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint * rint)), (Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint * rint)),
(lother, rother) => Err(InterpreterError::TypeError { (lother, rother) => Err(InterpreterError::TypeError {
context: "Mul".to_string(), context: "Mul".to_string(),
conflict: format!("can't mul {lother:?} and {rother:?}!"), conflict: format!("can't mul {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::Div { left, right } => { Expression::Div { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint / rint)), (Value::Int(lint), Value::Int(rint)) => Ok(Value::Int(lint / rint)),
(lother, rother) => Err(InterpreterError::TypeError { (lother, rother) => Err(InterpreterError::TypeError {
context: "Div".to_string(), context: "Div".to_string(),
conflict: format!("can't div {lother:?} and {rother:?}!"), conflict: format!("can't div {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::Negate { sub } => { Expression::Negate { sub } => {
let sval = self.evaluate_expression(scope_idx, &sub)?; let sval = self.evaluate_expression(scope_idx, &sub, loc)?;
match sval { match sval {
Value::Int(sint) => Ok(Value::Int(-sint)), Value::Int(sint) => Ok(Value::Int(-sint)),
sother => Err(InterpreterError::TypeError { sother => Err(InterpreterError::TypeError {
context: "Negate".to_string(), context: "Negate".to_string(),
conflict: format!("can't negate {sother:?}!"), conflict: format!("can't negate {sother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::BAnd { left, right } => { Expression::BAnd { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool && rbool)), (Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool && rbool)),
(lother, rother) => Err(InterpreterError::TypeError { (lother, rother) => Err(InterpreterError::TypeError {
context: "BAnd".to_string(), context: "BAnd".to_string(),
conflict: format!("can't and together {lother:?} and {rother:?}!"), conflict: format!("can't and together {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::BOr { left, right } => { Expression::BOr { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool || rbool)), (Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool || rbool)),
(lother, rother) => Err(InterpreterError::TypeError { (lother, rother) => Err(InterpreterError::TypeError {
context: "BOr".to_string(), context: "BOr".to_string(),
conflict: format!("can't or together {lother:?} and {rother:?}!"), conflict: format!("can't or together {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::BNot { sub } => { Expression::BNot { sub } => {
let sval = self.evaluate_expression(scope_idx, &sub)?; let sval = self.evaluate_expression(scope_idx, &sub, loc)?;
match sval { match sval {
Value::Bool(sbool) => Ok(Value::Bool(!sbool)), Value::Bool(sbool) => Ok(Value::Bool(!sbool)),
sother => Err(InterpreterError::TypeError { sother => Err(InterpreterError::TypeError {
context: "BNot".to_string(), context: "BNot".to_string(),
conflict: format!("can't invert {sother:?}!"), conflict: format!("can't invert {sother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::Equals { left, right } => { Expression::Equals { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::Bool(lbool), Value::Bool(rbool)) => Ok(Value::Bool(lbool == rbool)), (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 { (lother, rother) => Err(InterpreterError::TypeError {
context: "Equals".to_string(), context: "Equals".to_string(),
conflict: format!("can't test {lother:?} and {rother:?} for equality!"), conflict: format!("can't test {lother:?} and {rother:?} for equality!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::ListAdd { left, right } => { Expression::ListAdd { left, right } => {
let lval = self.evaluate_expression(scope_idx, &left)?; let lval = self.evaluate_expression(scope_idx, &left, loc)?;
let rval = self.evaluate_expression(scope_idx, &right)?; let rval = self.evaluate_expression(scope_idx, &right, loc)?;
match (lval, rval) { match (lval, rval) {
(Value::List(mut llist), Value::List(rlist)) => { (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 { (lother, rother) => Err(InterpreterError::TypeError {
context: "ListAdd".to_string(), context: "ListAdd".to_string(),
conflict: format!("can't list-add {lother:?} and {rother:?}!"), conflict: format!("can't list-add {lother:?} and {rother:?}!"),
location: "".to_string(), location: loc.clone(),
}), }),
} }
} }
Expression::List { elements } => { Expression::List { elements } => {
let mut result = Vec::with_capacity(elements.len()); let mut result = Vec::with_capacity(elements.len());
for expr in elements { 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)) Ok(Value::List(result))
} }
@ -411,12 +413,12 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Expression::StringExpr(sexpr) => { Expression::StringExpr(sexpr) => {
let mut output = String::new(); let mut output = String::new();
for piece in &sexpr.pieces { 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))) Ok(Value::Str(Arc::new(output)))
} }
Expression::FieldLookup { obj, ident, loc } => { 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 { match obj_val {
Value::Reflective(reflective) => match reflective.reflect_ref() { Value::Reflective(reflective) => match reflective.reflect_ref() {
ReflectRef::Struct(ss) => { ReflectRef::Struct(ss) => {
@ -426,7 +428,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Err(InterpreterError::TypeError { Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"), context: format!("Field Lookup for '{ident}'"),
conflict: format!("{reflective:?} is a reflective struct that does not have that field!"), 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 { Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"), context: format!("Field Lookup for '{ident}'"),
conflict: format!("{reflective:?} is a reflective tuple struct that does not have that field!"), conflict: format!("{reflective:?} is a reflective tuple struct that does not have that field!"),
location: "".to_string(), location: loc.clone(),
}) })
} }
} else { } else {
Err(InterpreterError::TypeError { Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"), context: format!("Field Lookup for '{ident}'"),
conflict: format!("{reflective:?} is a reflective tuple struct that does not have names for field!"), 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!( conflict: format!(
"{reflective:?} is of a reflective type that has no fields!" "{reflective:?} is of a reflective type that has no fields!"
), ),
location: "".to_string(), location: loc.clone(),
}), }),
}, },
other => Err(InterpreterError::TypeError { other => Err(InterpreterError::TypeError {
context: format!("Field Lookup for '{ident}'"), context: format!("Field Lookup for '{ident}'"),
conflict: format!("{other:?} is of a type that has no fields!"), 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!( conflict: format!(
"No local by that name; the only locals are: {locals_list}." "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, &self,
val: Value, val: Value,
output: &mut String, output: &mut String,
loc: &Locator,
) -> Result<(), InterpreterError<LS::Error, O::Error>> { ) -> Result<(), InterpreterError<LS::Error, O::Error>> {
match val { match val {
Value::Str(s) => { Value::Str(s) => {
@ -516,7 +519,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
conflict: format!( conflict: format!(
"Don't know how to write {reflective:?} as a sensible string output." "Don't know how to write {reflective:?} as a sensible string output."
), ),
location: "".to_string(), location: loc.clone(),
}) })
} }
Value::List(_) => { Value::List(_) => {
@ -526,7 +529,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
conflict: format!( conflict: format!(
"Don't know how to write List[...] as a sensible string output." "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, scope_idx: usize,
piece: &StringPiece, piece: &StringPiece,
output: &mut String, output: &mut String,
loc: &Locator,
) -> Result<(), InterpreterError<LS::Error, O::Error>> { ) -> Result<(), InterpreterError<LS::Error, O::Error>> {
match piece { match piece {
StringPiece::Literal(lit) => { StringPiece::Literal(lit) => {
@ -544,8 +548,8 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
Ok(()) Ok(())
} }
StringPiece::Interpolation(expr) => { StringPiece::Interpolation(expr) => {
let val = self.evaluate_expression(scope_idx, expr)?; let val = self.evaluate_expression(scope_idx, expr, loc)?;
self.write_out_value_as_display_string(val, output)?; self.write_out_value_as_display_string(val, output, loc)?;
Ok(()) Ok(())
} }
StringPiece::Localise { StringPiece::Localise {
@ -555,7 +559,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
let mut parameters_evaluated = BTreeMap::new(); let mut parameters_evaluated = BTreeMap::new();
for (key, expr) in parameters { for (key, expr) in parameters {
parameters_evaluated 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 let localised_text = self
@ -564,7 +568,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
.map_err(|underlying| InterpreterError::Localisation { .map_err(|underlying| InterpreterError::Localisation {
underlying, underlying,
trans_key: (trans_key as &str).to_owned(), trans_key: (trans_key as &str).to_owned(),
location: "".to_string(), location: loc.clone(),
})?; })?;
output.push_str(&localised_text); output.push_str(&localised_text);
@ -581,7 +585,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
return Err(InterpreterError::TypeError { return Err(InterpreterError::TypeError {
context: format!("No entrypoint called {:?}", self.entrypoint), context: format!("No entrypoint called {:?}", self.entrypoint),
conflict: "".to_string(), conflict: "".to_string(),
location: "".to_string(), location: Locator::empty(),
}); });
}; };
self.run_steps(0, main).await?; self.run_steps(0, main).await?;

View File

@ -37,10 +37,10 @@ pub trait OutputSystem {
pub use crate::engine::Value; pub use crate::engine::Value;
use crate::InterpreterError; use crate::InterpreterError;
pub struct LoadedTemplates<L, LS> { pub struct LoadedTemplates<LS> {
// todo might be tempted to use e.g. ouroboros here, to keep the file source adjacent? // todo might be tempted to use e.g. ouroboros here, to keep the file source adjacent?
// or do we just staticify? // or do we just staticify?
template_functions: BTreeMap<String, Arc<Vec<Step<L>>>>, template_functions: BTreeMap<String, Arc<Vec<Step>>>,
localisation: Arc<LS>, localisation: Arc<LS>,
} }
@ -56,7 +56,7 @@ impl Params {
} }
} }
impl<'a, LS> LoadedTemplates<(), LS> { impl<'a, LS> LoadedTemplates<LS> {
pub fn new(localisation_system: LS) -> Self { pub fn new(localisation_system: LS) -> Self {
LoadedTemplates { LoadedTemplates {
template_functions: Default::default(), template_functions: Default::default(),
@ -103,7 +103,7 @@ impl<'a, LS> LoadedTemplates<(), LS> {
template_name: &str, template_name: &str,
fragment_name: Option<&str>, fragment_name: Option<&str>,
params: Params, params: Params,
) -> PreparedTemplate<'a, (), LS> { ) -> PreparedTemplate<'a, LS> {
PreparedTemplate { PreparedTemplate {
all_instructions: Arc::new(self.template_functions.clone()), all_instructions: Arc::new(self.template_functions.clone()),
entrypoint: if let Some(frag) = fragment_name { entrypoint: if let Some(frag) = fragment_name {
@ -120,14 +120,14 @@ impl<'a, LS> LoadedTemplates<(), LS> {
} }
} }
pub struct PreparedTemplate<'a, L, LS> { pub struct PreparedTemplate<'a, LS> {
pub(crate) all_instructions: Arc<BTreeMap<String, Arc<Vec<Step<L>>>>>, pub(crate) all_instructions: Arc<BTreeMap<String, Arc<Vec<Step>>>>,
pub(crate) entrypoint: String, pub(crate) entrypoint: String,
pub(crate) scope: Scope<'a, L>, pub(crate) scope: Scope<'a>,
pub localisation: Arc<LS>, pub localisation: Arc<LS>,
} }
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<O: OutputSystem + Send>( pub async fn render_to_output<O: OutputSystem + Send>(
self, self,
output: O, output: O,

View File

@ -5,7 +5,7 @@ pub(crate) mod interface;
pub mod localisation; pub mod localisation;
use hornbeam_grammar::ParseError; use hornbeam_grammar::{Locator, ParseError};
use hornbeam_ir::AstToIrError; use hornbeam_ir::AstToIrError;
use thiserror::Error; use thiserror::Error;
@ -15,13 +15,13 @@ pub enum InterpreterError<LE: Debug + Clone, OE: Debug> {
TypeError { TypeError {
context: String, context: String,
conflict: String, conflict: String,
location: String, location: Locator,
}, },
#[error("localisation lookup of {trans_key:?} at {location} failed: {underlying:?}")] #[error("localisation lookup of {trans_key:?} at {location} failed: {underlying:?}")]
Localisation { Localisation {
underlying: LE, underlying: LE,
trans_key: String, trans_key: String,
location: String, location: Locator,
}, },
#[error("failed to write to output: {underlying:?}")] #[error("failed to write to output: {underlying:?}")]
OutputError { underlying: OE }, OutputError { underlying: OE },

View File

@ -1,6 +1,6 @@
use crate::ir::{Step, StepDef}; use crate::ir::{Step, StepDef};
use hornbeam_grammar::ast::{Block, Expression, StringExpr, StringPiece, Template}; use hornbeam_grammar::ast::{Block, Expression, StringExpr, StringPiece, Template};
use hornbeam_grammar::intern; use hornbeam_grammar::{intern, Locator};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::btree_map::Entry; use std::collections::btree_map::Entry;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -115,7 +115,7 @@ fn pull_out_entrypoints_from_block<'a>(
/// Step 2. Compile the AST to IR steps. /// Step 2. Compile the AST to IR steps.
pub(crate) fn compile_functions<'a>( pub(crate) fn compile_functions<'a>(
functions: &BTreeMap<String, Vec<Block>>, functions: &BTreeMap<String, Vec<Block>>,
) -> Result<BTreeMap<String, Vec<Step<()>>>, AstToIrError> { ) -> Result<BTreeMap<String, Vec<Step>>, AstToIrError> {
let mut result = BTreeMap::new(); let mut result = BTreeMap::new();
for (func_name, func_blocks) in functions { for (func_name, func_blocks) in functions {
let mut steps = Vec::new(); let mut steps = Vec::new();
@ -129,7 +129,7 @@ pub(crate) fn compile_functions<'a>(
fn compile_ast_block_to_steps<'a>( fn compile_ast_block_to_steps<'a>(
block: &Block, block: &Block,
instructions: &mut Vec<Step<()>>, instructions: &mut Vec<Step>,
) -> Result<(), AstToIrError> { ) -> Result<(), AstToIrError> {
match block { match block {
Block::HtmlElement(he) => { Block::HtmlElement(he) => {
@ -147,7 +147,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false, escape: false,
text: intern(text), text: intern(text),
}, },
locator: (), locator: he.loc.clone(),
}); });
// Write attributes // Write attributes
@ -157,7 +157,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false, escape: false,
text: intern(format!(" {attr_name}=\"")), text: intern(format!(" {attr_name}=\"")),
}, },
locator: (), locator: he.loc.clone(),
}); });
instructions.push(Step { instructions.push(Step {
@ -165,7 +165,7 @@ fn compile_ast_block_to_steps<'a>(
escape: true, escape: true,
expr: attr_expr.clone(), expr: attr_expr.clone(),
}, },
locator: (), locator: he.loc.clone(),
}); });
instructions.push(Step { instructions.push(Step {
@ -173,7 +173,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false, escape: false,
text: intern("\""), text: intern("\""),
}, },
locator: (), locator: he.loc.clone(),
}); });
} }
@ -183,7 +183,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false, escape: false,
text: intern(">"), text: intern(">"),
}, },
locator: (), locator: he.loc.clone(),
}); });
for child in &he.children { for child in &he.children {
@ -195,7 +195,7 @@ fn compile_ast_block_to_steps<'a>(
escape: false, escape: false,
text: intern(format!("</{}>", he.name)), text: intern(format!("</{}>", he.name)),
}, },
locator: (), locator: he.loc.clone(),
}); });
} }
Block::ComponentElement(ce) => { Block::ComponentElement(ce) => {
@ -214,7 +214,7 @@ fn compile_ast_block_to_steps<'a>(
args: ce.attributes.clone(), args: ce.attributes.clone(),
slots: all_slots_steps, slots: all_slots_steps,
}, },
locator: (), locator: ce.loc.clone(),
}) })
} }
Block::IfBlock(ifb) => { Block::IfBlock(ifb) => {
@ -235,7 +235,7 @@ fn compile_ast_block_to_steps<'a>(
true_steps: true_instrs, true_steps: true_instrs,
false_steps: false_instrs, false_steps: false_instrs,
}, },
locator: (), locator: ifb.loc.clone(),
}); });
} }
Block::Text(text) => { Block::Text(text) => {
@ -247,7 +247,7 @@ fn compile_ast_block_to_steps<'a>(
text: lit.clone(), text: lit.clone(),
escape: true, escape: true,
}, },
locator: (), locator: Locator::empty(),
}); });
} }
StringPiece::Interpolation(expr) => { StringPiece::Interpolation(expr) => {
@ -256,7 +256,7 @@ fn compile_ast_block_to_steps<'a>(
expr: expr.clone(), expr: expr.clone(),
escape: true, escape: true,
}, },
locator: (), locator: Locator::empty(),
}); });
} }
piece @ StringPiece::Localise { .. } => { piece @ StringPiece::Localise { .. } => {
@ -267,7 +267,7 @@ fn compile_ast_block_to_steps<'a>(
}), }),
escape: true, escape: true,
}, },
locator: (), locator: Locator::empty(),
}); });
} }
} }
@ -279,7 +279,7 @@ fn compile_ast_block_to_steps<'a>(
name: slot.name.clone(), name: slot.name.clone(),
optional: slot.optional, optional: slot.optional,
}, },
locator: (), locator: slot.loc.clone(),
}); });
} }
Block::DefineFragment(frag) => { Block::DefineFragment(frag) => {
@ -290,7 +290,7 @@ fn compile_ast_block_to_steps<'a>(
args: Default::default(), args: Default::default(),
slots: BTreeMap::new(), slots: BTreeMap::new(),
}, },
locator: (), locator: frag.loc.clone(),
}); });
} }
} }

View File

@ -1,24 +1,24 @@
pub use hornbeam_grammar::ast::{Expression, StringPiece}; pub use hornbeam_grammar::ast::{Expression, StringPiece};
use hornbeam_grammar::IStr; use hornbeam_grammar::{IStr, Locator};
use serde::Serialize; use serde::Serialize;
use std::collections::BTreeMap; use std::collections::BTreeMap;
#[derive(Clone, Debug, Eq, PartialEq, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct Function<L> { pub struct Function {
pub name: IStr, pub name: IStr,
pub locator: L, pub locator: Option<Locator>,
pub steps: Vec<Step<L>>, pub steps: Vec<Step>,
} }
#[derive(Clone, Debug, Eq, PartialEq, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct Step<L> { pub struct Step {
#[serde(flatten)] #[serde(flatten)]
pub def: StepDef<L>, pub def: StepDef,
pub locator: L, pub locator: Locator,
} }
#[derive(Clone, Debug, Eq, PartialEq, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum StepDef<L> { pub enum StepDef {
WriteLiteral { WriteLiteral {
escape: bool, escape: bool,
text: IStr, text: IStr,
@ -29,20 +29,20 @@ pub enum StepDef<L> {
}, },
If { If {
condition: Expression, condition: Expression,
true_steps: Vec<Step<L>>, true_steps: Vec<Step>,
false_steps: Vec<Step<L>>, false_steps: Vec<Step>,
}, },
For { For {
iterable: Expression, iterable: Expression,
// TODO! // TODO!
binding: IStr, binding: IStr,
body_steps: Vec<Step<L>>, body_steps: Vec<Step>,
empty_steps: Vec<Step<L>>, empty_steps: Vec<Step>,
}, },
Call { Call {
name: IStr, name: IStr,
args: BTreeMap<IStr, Expression>, args: BTreeMap<IStr, Expression>,
slots: BTreeMap<IStr, Vec<Step<L>>>, slots: BTreeMap<IStr, Vec<Step>>,
}, },
CallSlotWithParentScope { CallSlotWithParentScope {
name: IStr, name: IStr,

View File

@ -22,7 +22,7 @@ pub use ast_to_ir::AstToIrError;
pub fn ast_to_optimised_ir( pub fn ast_to_optimised_ir(
template_name: &str, template_name: &str,
template: Template, template: Template,
) -> Result<BTreeMap<String, Vec<Step<()>>>, AstToIrError> { ) -> Result<BTreeMap<String, Vec<Step>>, AstToIrError> {
let entrypoints = pull_out_entrypoints(template, template_name)?; let entrypoints = pull_out_entrypoints(template, template_name)?;
let mut compiled_funcs = ast_to_ir::compile_functions(&entrypoints)?; let mut compiled_funcs = ast_to_ir::compile_functions(&entrypoints)?;
for steps in compiled_funcs.values_mut() { for steps in compiled_funcs.values_mut() {

View File

@ -48,7 +48,7 @@ fn peephole_opt<T, F: FnMut(&mut [Option<T>]) -> ()>(
*steps = result; *steps = result;
} }
fn apply_peephole_pass<'a, L, F: Fn(&mut Vec<Step<L>>)>(steps: &mut Vec<Step<L>>, pass: &F) { fn apply_peephole_pass<F: Fn(&mut Vec<Step>)>(steps: &mut Vec<Step>, pass: &F) {
pass(steps); pass(steps);
for step in steps { for step in steps {
match &mut step.def { match &mut step.def {
@ -83,7 +83,7 @@ fn apply_peephole_pass<'a, L, F: Fn(&mut Vec<Step<L>>)>(steps: &mut Vec<Step<L>>
//// Peephole Passes //// Peephole Passes
/// Given a WriteEval step that just writes literals, convert it to a WriteLiteral step. /// Given a WriteEval step that just writes literals, convert it to a WriteLiteral step.
fn pass_write_eval_literal_to_write_literal<L>(steps: &mut Vec<Step<L>>) { fn pass_write_eval_literal_to_write_literal(steps: &mut Vec<Step>) {
'next_step: for step in steps { 'next_step: for step in steps {
if let StepDef::WriteEval { if let StepDef::WriteEval {
escape, escape,
@ -110,7 +110,7 @@ fn pass_write_eval_literal_to_write_literal<L>(steps: &mut Vec<Step<L>>) {
} }
/// Given a WriteLiteral step that escapes the HTML as it is written, precompute the escaped HTML. /// Given a WriteLiteral step that escapes the HTML as it is written, precompute the escaped HTML.
fn pass_write_literal_preescape<L>(steps: &mut Vec<Step<L>>) { fn pass_write_literal_preescape(steps: &mut Vec<Step>) {
for step in steps { for step in steps {
if let StepDef::WriteLiteral { escape, text } = &mut step.def { if let StepDef::WriteLiteral { escape, text } = &mut step.def {
if *escape { if *escape {
@ -125,7 +125,7 @@ fn pass_write_literal_preescape<L>(steps: &mut Vec<Step<L>>) {
} }
/// Given two adjacent WriteLiteral steps, combine them together. /// Given two adjacent WriteLiteral steps, combine them together.
fn pass_combine_write_literals<L>(steps: &mut Vec<Step<L>>) { fn pass_combine_write_literals(steps: &mut Vec<Step>) {
peephole_opt(steps, 2, |steps| { peephole_opt(steps, 2, |steps| {
let (l, r) = steps.split_at_mut(1); let (l, r) = steps.split_at_mut(1);
let left = l[0].as_mut().unwrap(); let left = l[0].as_mut().unwrap();
@ -153,7 +153,7 @@ fn pass_combine_write_literals<L>(steps: &mut Vec<Step<L>>) {
//// Apply all passes in the preferred order //// Apply all passes in the preferred order
pub fn apply_all_peephole_passes<L>(steps: &mut Vec<Step<L>>) { pub fn apply_all_peephole_passes(steps: &mut Vec<Step>) {
apply_peephole_pass(steps, &pass_write_eval_literal_to_write_literal); apply_peephole_pass(steps, &pass_write_eval_literal_to_write_literal);
apply_peephole_pass(steps, &pass_write_literal_preescape); apply_peephole_pass(steps, &pass_write_literal_preescape);
apply_peephole_pass(steps, &pass_combine_write_literals); apply_peephole_pass(steps, &pass_combine_write_literals);
@ -167,7 +167,7 @@ mod tests {
use insta::assert_yaml_snapshot; use insta::assert_yaml_snapshot;
use std::collections::BTreeMap; use std::collections::BTreeMap;
fn parse_ir_and_peephole(text: &str) -> BTreeMap<String, Vec<Step<()>>> { fn parse_ir_and_peephole(text: &str) -> BTreeMap<String, Vec<Step>> {
let template = parse_template(text, "inp").unwrap(); let template = parse_template(text, "inp").unwrap();
let entrypoints = pull_out_entrypoints(template, "TemplateName").unwrap(); let entrypoints = pull_out_entrypoints(template, "TemplateName").unwrap();
let mut compiled = compile_functions(&entrypoints).unwrap(); let mut compiled = compile_functions(&entrypoints).unwrap();

View File

@ -6,67 +6,112 @@ TemplateName:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<div" text: "<div"
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: ">" text: ">"
locator: ~ locator:
filename: inp
line: 2
column: 1
- Call: - Call:
name: TemplateName__Frag1 name: TemplateName__Frag1
args: {} args: {}
slots: {} slots: {}
locator: ~ locator:
filename: inp
line: 3
column: 5
- Call: - Call:
name: TemplateName__Footer name: TemplateName__Footer
args: {} args: {}
slots: {} slots: {}
locator: ~ locator:
filename: inp
line: 9
column: 5
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "</div>" text: "</div>"
locator: ~ locator:
filename: inp
line: 2
column: 1
TemplateName__Footer: TemplateName__Footer:
- WriteLiteral: - WriteLiteral:
escape: true escape: true
text: Or even adjacent ones text: Or even adjacent ones
locator: ~ locator:
filename: ""
line: 0
column: 0
TemplateName__Frag1: TemplateName__Frag1:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<span" text: "<span"
locator: ~ locator:
filename: inp
line: 4
column: 9
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: ">" text: ">"
locator: ~ locator:
filename: inp
line: 4
column: 9
- WriteLiteral: - WriteLiteral:
escape: true escape: true
text: This is a fragment!! text: This is a fragment!!
locator: ~ locator:
filename: ""
line: 0
column: 0
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "</span>" text: "</span>"
locator: ~ locator:
filename: inp
line: 4
column: 9
- Call: - Call:
name: TemplateName__Frag2 name: TemplateName__Frag2
args: {} args: {}
slots: {} slots: {}
locator: ~ locator:
filename: inp
line: 6
column: 9
TemplateName__Frag2: TemplateName__Frag2:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<div" text: "<div"
locator: ~ locator:
filename: inp
line: 7
column: 13
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: ">" text: ">"
locator: ~ locator:
filename: inp
line: 7
column: 13
- WriteLiteral: - WriteLiteral:
escape: true escape: true
text: "There's no problem having nested fragments!" text: "There's no problem having nested fragments!"
locator: ~ locator:
filename: ""
line: 0
column: 0
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "</div>" text: "</div>"
locator: ~ locator:
filename: inp
line: 7
column: 13

View File

@ -6,58 +6,98 @@ TemplateName:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<div" text: "<div"
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: " arb=\"" text: " arb=\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteEval: - WriteEval:
escape: true escape: true
expr: expr:
Variable: Variable:
name: ritrary name: ritrary
locator: ~ loc:
filename: inp
line: 2
column: 47
locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "\"" text: "\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: " size=\"" text: " size=\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteEval: - WriteEval:
escape: true escape: true
expr: expr:
IntLiteral: IntLiteral:
val: 42 val: 42
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "\"" text: "\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: " stringy=\"" text: " stringy=\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteEval: - WriteEval:
escape: true escape: true
expr: expr:
StringExpr: StringExpr:
pieces: pieces:
- Literal: yup - Literal: yup
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "\"" text: "\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: ">" text: ">"
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: true escape: true
text: This is a div with a few extras text: This is a div with a few extras
locator: ~ locator:
filename: ""
line: 0
column: 0
- Call: - Call:
name: OtherComponent name: OtherComponent
args: args:
@ -71,11 +111,21 @@ TemplateName:
param3: param3:
Variable: Variable:
name: three name: three
loc:
filename: inp
line: 4
column: 52
slots: slots:
main: [] main: []
locator: ~ locator:
filename: inp
line: 4
column: 5
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "</div>" text: "</div>"
locator: ~ locator:
filename: inp
line: 2
column: 1

View File

@ -9,12 +9,24 @@ TemplateName:
- DefineFragment: - DefineFragment:
name: TemplateName__Frag1 name: TemplateName__Frag1
blocks: [] blocks: []
loc:
filename: inp
line: 3
column: 5
- DefineFragment: - DefineFragment:
name: TemplateName__Footer name: TemplateName__Footer
blocks: [] blocks: []
loc:
filename: inp
line: 9
column: 5
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 2
column: 1
TemplateName__Footer: TemplateName__Footer:
- Text: - Text:
pieces: pieces:
@ -29,9 +41,17 @@ TemplateName__Frag1:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 4
column: 9
- DefineFragment: - DefineFragment:
name: TemplateName__Frag2 name: TemplateName__Frag2
blocks: [] blocks: []
loc:
filename: inp
line: 6
column: 9
TemplateName__Frag2: TemplateName__Frag2:
- HtmlElement: - HtmlElement:
name: div name: div
@ -42,4 +62,8 @@ TemplateName__Frag2:
classes: [] classes: []
dom_id: ~ dom_id: ~
attributes: {} attributes: {}
loc:
filename: inp
line: 7
column: 13

View File

@ -6,39 +6,63 @@ TemplateName:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<div>" text: "<div>"
locator: ~ locator:
filename: inp
line: 2
column: 1
- Call: - Call:
name: TemplateName__Frag1 name: TemplateName__Frag1
args: {} args: {}
slots: {} slots: {}
locator: ~ locator:
filename: inp
line: 3
column: 5
- Call: - Call:
name: TemplateName__Footer name: TemplateName__Footer
args: {} args: {}
slots: {} slots: {}
locator: ~ locator:
filename: inp
line: 9
column: 5
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "</div>" text: "</div>"
locator: ~ locator:
filename: inp
line: 2
column: 1
TemplateName__Footer: TemplateName__Footer:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: Or even adjacent ones text: Or even adjacent ones
locator: ~ locator:
filename: ""
line: 0
column: 0
TemplateName__Frag1: TemplateName__Frag1:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<span>This is a fragment!!</span>" text: "<span>This is a fragment!!</span>"
locator: ~ locator:
filename: inp
line: 4
column: 9
- Call: - Call:
name: TemplateName__Frag2 name: TemplateName__Frag2
args: {} args: {}
slots: {} slots: {}
locator: ~ locator:
filename: inp
line: 6
column: 9
TemplateName__Frag2: TemplateName__Frag2:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<div>There&#x27;s no problem having &lt;&lt;nested&gt;&gt; fragments!</div>" text: "<div>There&#x27;s no problem having &lt;&lt;nested&gt;&gt; fragments!</div>"
locator: ~ locator:
filename: inp
line: 7
column: 13

View File

@ -6,27 +6,46 @@ TemplateName:
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "<div arb=\"" text: "<div arb=\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteEval: - WriteEval:
escape: true escape: true
expr: expr:
Variable: Variable:
name: ritrary name: ritrary
locator: ~ loc:
filename: inp
line: 2
column: 47
locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "\" size=\"" text: "\" size=\""
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteEval: - WriteEval:
escape: true escape: true
expr: expr:
IntLiteral: IntLiteral:
val: 42 val: 42
locator: ~ locator:
filename: inp
line: 2
column: 1
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "\" stringy=\"yup\">This is a div with a few extras" text: "\" stringy=\"yup\">This is a div with a few extras"
locator: ~ locator:
filename: inp
line: 2
column: 1
- Call: - Call:
name: OtherComponent name: OtherComponent
args: args:
@ -40,11 +59,21 @@ TemplateName:
param3: param3:
Variable: Variable:
name: three name: three
loc:
filename: inp
line: 4
column: 52
slots: slots:
main: [] main: []
locator: ~ locator:
filename: inp
line: 4
column: 5
- WriteLiteral: - WriteLiteral:
escape: false escape: false
text: "</div>" text: "</div>"
locator: ~ locator:
filename: inp
line: 2
column: 1