Simplify and use locators in the IR and interpreter
This commit is contained in:
parent
f54f2093de
commit
0370de40a1
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 <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:
|
||||
- HtmlElement:
|
||||
@ -9,4 +9,8 @@ blocks:
|
||||
classes: []
|
||||
dom_id: ~
|
||||
attributes: {}
|
||||
loc:
|
||||
filename: inp
|
||||
line: 3
|
||||
column: 1
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<L>],
|
||||
pub steps: &'a [Step],
|
||||
}
|
||||
|
||||
pub(crate) struct Scope<'a, L> {
|
||||
pub(crate) struct Scope<'a> {
|
||||
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) program: &'a BTreeMap<String, Arc<Vec<Step<L>>>>,
|
||||
pub(crate) program: &'a BTreeMap<String, Arc<Vec<Step>>>,
|
||||
pub(crate) output: O,
|
||||
pub(crate) localisation: Arc<LS>,
|
||||
pub(crate) scopes: Vec<Scope<'a, L>>,
|
||||
pub(crate) scopes: Vec<Scope<'a>>,
|
||||
}
|
||||
|
||||
#[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<L>],
|
||||
steps: &'a [Step],
|
||||
) -> Result<(), InterpreterError<LS::Error, O::Error>> {
|
||||
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<L>,
|
||||
step: &'a Step,
|
||||
) -> Result<(), InterpreterError<LS::Error, O::Error>> {
|
||||
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<Value, InterpreterError<LS::Error, O::Error>> {
|
||||
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<LS::Error, O::Error>> {
|
||||
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<LS::Error, O::Error>> {
|
||||
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?;
|
||||
|
@ -37,10 +37,10 @@ pub trait OutputSystem {
|
||||
pub use crate::engine::Value;
|
||||
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?
|
||||
// or do we just staticify?
|
||||
template_functions: BTreeMap<String, Arc<Vec<Step<L>>>>,
|
||||
template_functions: BTreeMap<String, Arc<Vec<Step>>>,
|
||||
|
||||
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 {
|
||||
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<BTreeMap<String, Arc<Vec<Step<L>>>>>,
|
||||
pub struct PreparedTemplate<'a, LS> {
|
||||
pub(crate) all_instructions: Arc<BTreeMap<String, Arc<Vec<Step>>>>,
|
||||
pub(crate) entrypoint: String,
|
||||
pub(crate) scope: Scope<'a, L>,
|
||||
pub(crate) scope: Scope<'a>,
|
||||
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>(
|
||||
self,
|
||||
output: O,
|
||||
|
@ -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<LE: Debug + Clone, OE: Debug> {
|
||||
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 },
|
||||
|
@ -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<String, Vec<Block>>,
|
||||
) -> Result<BTreeMap<String, Vec<Step<()>>>, AstToIrError> {
|
||||
) -> Result<BTreeMap<String, Vec<Step>>, 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<Step<()>>,
|
||||
instructions: &mut Vec<Step>,
|
||||
) -> 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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<L> {
|
||||
pub struct Function {
|
||||
pub name: IStr,
|
||||
pub locator: L,
|
||||
pub steps: Vec<Step<L>>,
|
||||
pub locator: Option<Locator>,
|
||||
pub steps: Vec<Step>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct Step<L> {
|
||||
pub struct Step {
|
||||
#[serde(flatten)]
|
||||
pub def: StepDef<L>,
|
||||
pub locator: L,
|
||||
pub def: StepDef,
|
||||
pub locator: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub enum StepDef<L> {
|
||||
pub enum StepDef {
|
||||
WriteLiteral {
|
||||
escape: bool,
|
||||
text: IStr,
|
||||
@ -29,20 +29,20 @@ pub enum StepDef<L> {
|
||||
},
|
||||
If {
|
||||
condition: Expression,
|
||||
true_steps: Vec<Step<L>>,
|
||||
false_steps: Vec<Step<L>>,
|
||||
true_steps: Vec<Step>,
|
||||
false_steps: Vec<Step>,
|
||||
},
|
||||
For {
|
||||
iterable: Expression,
|
||||
// TODO!
|
||||
binding: IStr,
|
||||
body_steps: Vec<Step<L>>,
|
||||
empty_steps: Vec<Step<L>>,
|
||||
body_steps: Vec<Step>,
|
||||
empty_steps: Vec<Step>,
|
||||
},
|
||||
Call {
|
||||
name: IStr,
|
||||
args: BTreeMap<IStr, Expression>,
|
||||
slots: BTreeMap<IStr, Vec<Step<L>>>,
|
||||
slots: BTreeMap<IStr, Vec<Step>>,
|
||||
},
|
||||
CallSlotWithParentScope {
|
||||
name: IStr,
|
||||
|
@ -22,7 +22,7 @@ pub use ast_to_ir::AstToIrError;
|
||||
pub fn ast_to_optimised_ir(
|
||||
template_name: &str,
|
||||
template: Template,
|
||||
) -> Result<BTreeMap<String, Vec<Step<()>>>, AstToIrError> {
|
||||
) -> Result<BTreeMap<String, Vec<Step>>, 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() {
|
||||
|
@ -48,7 +48,7 @@ fn peephole_opt<T, F: FnMut(&mut [Option<T>]) -> ()>(
|
||||
*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);
|
||||
for step in steps {
|
||||
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
|
||||
|
||||
/// 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 {
|
||||
if let StepDef::WriteEval {
|
||||
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.
|
||||
fn pass_write_literal_preescape<L>(steps: &mut Vec<Step<L>>) {
|
||||
fn pass_write_literal_preescape(steps: &mut Vec<Step>) {
|
||||
for step in steps {
|
||||
if let StepDef::WriteLiteral { escape, text } = &mut step.def {
|
||||
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.
|
||||
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| {
|
||||
let (l, r) = steps.split_at_mut(1);
|
||||
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
|
||||
|
||||
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_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<String, Vec<Step<()>>> {
|
||||
fn parse_ir_and_peephole(text: &str) -> BTreeMap<String, Vec<Step>> {
|
||||
let template = parse_template(text, "inp").unwrap();
|
||||
let entrypoints = pull_out_entrypoints(template, "TemplateName").unwrap();
|
||||
let mut compiled = compile_functions(&entrypoints).unwrap();
|
||||
|
@ -6,67 +6,112 @@ TemplateName:
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "<div"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- 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: "</div>"
|
||||
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: "<span"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 4
|
||||
column: 9
|
||||
- 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: "</span>"
|
||||
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: "<div"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 13
|
||||
- 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: "</div>"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 13
|
||||
|
||||
|
@ -6,58 +6,98 @@ TemplateName:
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "<div"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: " arb=\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteEval:
|
||||
escape: true
|
||||
expr:
|
||||
Variable:
|
||||
name: ritrary
|
||||
locator: ~
|
||||
loc:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 47
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: " size=\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteEval:
|
||||
escape: true
|
||||
expr:
|
||||
IntLiteral:
|
||||
val: 42
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: " stringy=\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteEval:
|
||||
escape: true
|
||||
expr:
|
||||
StringExpr:
|
||||
pieces:
|
||||
- Literal: yup
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- 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: "</div>"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -6,39 +6,63 @@ TemplateName:
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "<div>"
|
||||
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: "</div>"
|
||||
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: "<span>This is a fragment!!</span>"
|
||||
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: "<div>There's no problem having <<nested>> fragments!</div>"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 7
|
||||
column: 13
|
||||
|
||||
|
@ -6,27 +6,46 @@ TemplateName:
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "<div arb=\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteEval:
|
||||
escape: true
|
||||
expr:
|
||||
Variable:
|
||||
name: ritrary
|
||||
locator: ~
|
||||
loc:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 47
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "\" size=\""
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteEval:
|
||||
escape: true
|
||||
expr:
|
||||
IntLiteral:
|
||||
val: 42
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
- WriteLiteral:
|
||||
escape: false
|
||||
text: "\" stringy=\"yup\">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: "</div>"
|
||||
locator: ~
|
||||
locator:
|
||||
filename: inp
|
||||
line: 2
|
||||
column: 1
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user