diff --git a/Cargo.lock b/Cargo.lock index 60252e4..b91c825 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +dependencies = [ + "const-random", +] + [[package]] name = "ahash" version = "0.7.6" @@ -22,6 +31,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "arc-interner" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655ae79d4a7661f2f9a8f6daf95af32897eafdea938df06aeb127cea02b5f4ac" +dependencies = [ + "ahash 0.3.8", + "dashmap", + "once_cell", + "serde", +] + [[package]] name = "arc-swap" version = "1.6.0" @@ -109,7 +130,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e9aa1866c1cf7ee000f281ce9e90d02d701f5c7380a107252017e58e2f5246" dependencies = [ - "ahash", + "ahash 0.7.6", "getrandom", "hashbrown", "instant", @@ -166,6 +187,28 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "const-random" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom", + "once_cell", + "proc-macro-hack", + "tiny-keccak", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -175,6 +218,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -185,6 +234,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + [[package]] name = "digest" version = "0.10.6" @@ -379,7 +438,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", "serde", ] @@ -389,6 +448,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hornbeam" version = "0.1.0" @@ -400,6 +468,7 @@ dependencies = [ name = "hornbeam_grammar" version = "0.1.0" dependencies = [ + "arc-interner", "insta", "lazy_static", "pest", @@ -578,6 +647,16 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -894,6 +973,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.7.1" diff --git a/hornbeam_grammar/Cargo.toml b/hornbeam_grammar/Cargo.toml index 9a3886a..14bf9dc 100644 --- a/hornbeam_grammar/Cargo.toml +++ b/hornbeam_grammar/Cargo.toml @@ -11,6 +11,7 @@ pest_derive = "2.5.5" pest_consume = "1.1.3" lazy_static = "1.4.0" serde = { version = "1.0.152", features = ["derive"] } +arc-interner = "0.7.0" [dev-dependencies] insta = { version = "1.28.0", features = ["yaml"] } diff --git a/hornbeam_grammar/src/ast.rs b/hornbeam_grammar/src/ast.rs index d0b8ac9..beae484 100644 --- a/hornbeam_grammar/src/ast.rs +++ b/hornbeam_grammar/src/ast.rs @@ -1,146 +1,146 @@ +use crate::IStr; use serde::Serialize; -use std::borrow::Cow; use std::collections::BTreeMap; #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Template<'a> { - pub blocks: Vec>, +pub struct Template { + pub blocks: Vec, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Block<'a> { - HtmlElement(HtmlElement<'a>), - ComponentElement(ComponentElement<'a>), - IfBlock(IfBlock<'a>), - Text(StringExpr<'a>), - DefineExpandSlot(DefineExpandSlot<'a>), - DefineFragment(DefineFragment<'a>), +pub enum Block { + HtmlElement(HtmlElement), + ComponentElement(ComponentElement), + IfBlock(IfBlock), + Text(StringExpr), + DefineExpandSlot(DefineExpandSlot), + DefineFragment(DefineFragment), } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct HtmlElement<'a> { - pub name: Cow<'a, str>, - pub children: Vec>, - pub classes: Vec>, - pub dom_id: Option>, - pub attributes: BTreeMap, Expression<'a>>, +pub struct HtmlElement { + pub name: IStr, + pub children: Vec, + pub classes: Vec, + pub dom_id: Option, + pub attributes: BTreeMap, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ComponentElement<'a> { - pub name: Cow<'a, str>, - pub slots: BTreeMap, Vec>>, - pub attributes: BTreeMap, Expression<'a>>, +pub struct ComponentElement { + pub name: IStr, + pub slots: BTreeMap>, + pub attributes: BTreeMap, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct IfBlock<'a> { - pub condition: Expression<'a>, - pub blocks: Vec>, - pub else_blocks: Vec>, +pub struct IfBlock { + pub condition: Expression, + pub blocks: Vec, + pub else_blocks: Vec, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct DefineExpandSlot<'a> { - pub name: Cow<'a, str>, +pub struct DefineExpandSlot { + pub name: IStr, pub optional: bool, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct DefineFragment<'a> { - pub name: Cow<'a, str>, - pub blocks: Vec>, +pub struct DefineFragment { + pub name: IStr, + pub blocks: Vec, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct StringExpr<'a> { - pub pieces: Vec>, +pub struct StringExpr { + pub pieces: Vec, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum StringPiece<'a> { - Literal(Cow<'a, str>), - Interpolation(Expression<'a>), +pub enum StringPiece { + Literal(IStr), + Interpolation(Expression), Localise { - trans_key: Cow<'a, str>, - parameters: BTreeMap, Expression<'a>>, + trans_key: IStr, + parameters: BTreeMap, }, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Expression<'a> { +pub enum Expression { // Arithmetic Operators Add { - left: Box>, - right: Box>, + left: Box, + right: Box, }, Sub { - left: Box>, - right: Box>, + left: Box, + right: Box, }, Mul { - left: Box>, - right: Box>, + left: Box, + right: Box, }, Div { - left: Box>, - right: Box>, + left: Box, + right: Box, }, Negate { - sub: Box>, + sub: Box, }, // Boolean Operators BAnd { - left: Box>, - right: Box>, + left: Box, + right: Box, }, BOr { - left: Box>, - right: Box>, + left: Box, + right: Box, }, BNot { - sub: Box>, + sub: Box, }, // Comparators Equals { - left: Box>, - right: Box>, + left: Box, + right: Box, }, // Other Operators ListAdd { - left: Box>, - right: Box>, + left: Box, + right: Box, }, // Literals List { - elements: Vec>, + elements: Vec, }, IntLiteral { val: i64, }, - StringExpr(StringExpr<'a>), + StringExpr(StringExpr), // Relatives FieldLookup { - obj: Box>, - ident: Cow<'a, str>, + obj: Box, + ident: IStr, }, MethodCall { - obj: Box>, - ident: Cow<'a, str>, - args: Vec>, + obj: Box, + ident: IStr, + args: Vec, }, // Other Primaries Variable { - name: Cow<'a, str>, + name: IStr, }, FunctionCall { - name: Cow<'a, str>, - args: Vec>, + name: IStr, + args: Vec, }, } diff --git a/hornbeam_grammar/src/lib.rs b/hornbeam_grammar/src/lib.rs index b188cf0..c35397f 100644 --- a/hornbeam_grammar/src/lib.rs +++ b/hornbeam_grammar/src/lib.rs @@ -1,7 +1,14 @@ pub mod ast; mod parser; +use arc_interner::ArcIntern; pub use parser::parse_template; use parser::Rule; pub type ParseError = pest::error::Error; + +pub type IStr = ArcIntern; + +pub fn intern(s: impl Into) -> ArcIntern { + ArcIntern::new(s.into()) +} diff --git a/hornbeam_grammar/src/parser.rs b/hornbeam_grammar/src/parser.rs index 2231502..290316c 100644 --- a/hornbeam_grammar/src/parser.rs +++ b/hornbeam_grammar/src/parser.rs @@ -4,12 +4,12 @@ use crate::ast::{ Block, ComponentElement, DefineExpandSlot, DefineFragment, Expression, HtmlElement, IfBlock, StringExpr, StringPiece, Template, }; +use crate::{intern, IStr}; use lazy_static::lazy_static; use pest::error::ErrorVariant; use pest::pratt_parser::{Assoc, Op, PrattParser}; use pest::Span; use pest_consume::{match_nodes, Error as PCError, Parser}; -use std::borrow::Cow; use std::collections::BTreeMap; use std::fmt::Debug; use std::hash::Hash; @@ -100,7 +100,7 @@ impl HornbeamParser { if !supply_slots.is_empty() { let mut slots = BTreeMap::new(); for (slot_name, slot_content_blocks, _slot_span) in supply_slots { - slots.insert(Cow::from(slot_name), slot_content_blocks); + slots.insert(slot_name, slot_content_blocks); } Block::ComponentElement(ComponentElement { name, @@ -109,7 +109,7 @@ impl HornbeamParser { }) } else { let mut slots = BTreeMap::new(); - slots.insert(Cow::from("main"), blocks); + slots.insert(intern("main"), blocks); Block::ComponentElement(ComponentElement { name, slots, @@ -145,11 +145,11 @@ impl HornbeamParser { Ok(()) } - fn ElementName(input: Node) -> PCResult> { - Ok(Cow::from(input.as_str())) + fn ElementName(input: Node) -> PCResult { + Ok(intern(input.as_str())) } - fn SupplySlot(input: Node) -> PCResult<(Cow, Vec, Span)> { + fn SupplySlot(input: Node) -> PCResult<(IStr, Vec, Span)> { let in_span = input.as_span(); match_nodes!(input.into_children(); [Identifier(ident), blocks..] => { @@ -159,17 +159,17 @@ impl HornbeamParser { ) } - fn Identifier(input: Node) -> PCResult> { - Ok(Cow::from(input.as_str())) + fn Identifier(input: Node) -> PCResult { + Ok(intern(input.as_str())) } - fn LocalisationIdentifier(input: Node) -> PCResult> { - Ok(Cow::from(input.as_str())) + fn LocalisationIdentifier(input: Node) -> PCResult { + Ok(intern(input.as_str())) } - fn CssClass(input: Node) -> PCResult> { - Ok(Cow::from(input.as_str())) + fn CssClass(input: Node) -> PCResult { + Ok(intern(input.as_str())) } - fn DomId(input: Node) -> PCResult> { - Ok(Cow::from(input.as_str())) + fn DomId(input: Node) -> PCResult { + Ok(intern(input.as_str())) } fn Text(input: Node) -> PCResult { @@ -189,10 +189,10 @@ impl HornbeamParser { Rule::SingleStringContent | Rule::DoubleStringContent | Rule::BlockStringContent => { - pieces.push(StringPiece::Literal(Cow::from(node.as_str()))); + pieces.push(StringPiece::Literal(intern(node.as_str()))); } Rule::BlockStringNewline => { - pieces.push(StringPiece::Literal(Cow::from("\n"))); + pieces.push(StringPiece::Literal(intern("\n"))); } Rule::ParameterisedLocalisation => { let (trans_key, parameters) = match_nodes!(node.into_children(); @@ -210,28 +210,28 @@ impl HornbeamParser { Ok(StringExpr { pieces }) } - fn KVPair(input: Node) -> PCResult<(Cow, Expression)> { + fn KVPair(input: Node) -> PCResult<(IStr, Expression)> { Ok(match_nodes!(input.into_children(); [Identifier(key), Expr(value)] => (key, value), )) } - fn MapLiteral(input: Node) -> PCResult, Expression>> { + fn MapLiteral(input: Node) -> PCResult> { Ok(match_nodes!(input.into_children(); [KVPair(kv_pair)..] => kv_pair.collect() )) } - fn SEscape(input: Node) -> PCResult> { + fn SEscape(input: Node) -> PCResult { let esc = input.as_str(); Ok(match esc { - "\\\\" | "\\'" | "\\\"" | "\\$" => Cow::from(&esc[1..2]), + "\\\\" | "\\'" | "\\\"" | "\\$" => intern(&esc[1..2]), other => unimplemented!("Unimplemented escape sequence {other:?}! This is a bug."), }) } fn Variable(input: Node) -> PCResult { - let name = Cow::from(input.into_children().single()?.as_str()); + let name = intern(input.into_children().single()?.as_str()); Ok(Expression::Variable { name }) } @@ -261,7 +261,7 @@ impl HornbeamParser { .map_postfix(|lhs, op| Ok(match op.as_rule() { Rule::unwrap => unimplemented!("unimp unwrap"), Rule::FieldLookup => { - let ident = Cow::from(Node::new(op).into_children().single()?.as_str()); + let ident = intern(Node::new(op).into_children().single()?.as_str()); Expression::FieldLookup { obj: Box::new(lhs?), ident } }, Rule::MethodCall => { @@ -316,7 +316,7 @@ impl HornbeamParser { } impl HornbeamParser { - fn helper_blocks<'a>(input: impl Iterator>) -> PCResult>> { + fn helper_blocks<'a>(input: impl Iterator>) -> PCResult> { let mut result = Vec::with_capacity(input.size_hint().0); for child in input { if let Some(block) = HornbeamParser::helper_block(child)? { diff --git a/hornbeam_interpreter/src/engine.rs b/hornbeam_interpreter/src/engine.rs index da6f622..1a45b0b 100644 --- a/hornbeam_interpreter/src/engine.rs +++ b/hornbeam_interpreter/src/engine.rs @@ -14,7 +14,7 @@ use std::sync::Arc; pub(crate) struct FilledSlot<'a, L> { pub scope_idx: usize, - pub steps: &'a [Step<'a, L>], + pub steps: &'a [Step], } pub(crate) struct Scope<'a, L> { @@ -24,7 +24,7 @@ pub(crate) struct Scope<'a, L> { pub(crate) struct Interpreter<'a, L, O, LS> { pub(crate) entrypoint: String, - pub(crate) program: &'a BTreeMap>>>, + pub(crate) program: &'a BTreeMap>>>, pub(crate) output: O, pub(crate) localisation: Arc, pub(crate) scopes: Vec>, @@ -97,7 +97,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync + pub async fn run_steps( &mut self, scope_idx: usize, - steps: &'a [Step<'a, L>], + steps: &'a [Step], ) -> Result<(), InterpreterError> { for step in steps { self.run_step(scope_idx, step).await?; @@ -108,14 +108,14 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync + pub async fn run_step( &mut self, scope_idx: usize, - step: &'a Step<'a, L>, + step: &'a Step, ) -> Result<(), InterpreterError> { match &step.def { StepDef::WriteLiteral { escape, text } => { if *escape { // Unlikely to be hit: peephole optimisation should escape literals wherever // possible. - let escaped = html_escape::encode_safe(text); + let escaped = html_escape::encode_safe(text as &str); self.output .write(&escaped) .await @@ -265,7 +265,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync + pub fn evaluate_expression( &self, scope_idx: usize, - expr: &'a Expression<'a>, + expr: &'a Expression, ) -> Result> { match expr { Expression::Add { left, right } => { diff --git a/hornbeam_interpreter/src/interface.rs b/hornbeam_interpreter/src/interface.rs index 739308c..f2e88df 100644 --- a/hornbeam_interpreter/src/interface.rs +++ b/hornbeam_interpreter/src/interface.rs @@ -37,10 +37,10 @@ pub trait OutputSystem { pub use crate::engine::Value; use crate::InterpreterError; -pub struct LoadedTemplates<'a, L, LS> { +pub struct LoadedTemplates { // todo might be tempted to use e.g. ouroboros here, to keep the file source adjacent? // or do we just staticify? - template_functions: BTreeMap>>>, + template_functions: BTreeMap>>>, localisation: Arc, } @@ -56,7 +56,7 @@ impl Params { } } -impl<'a, LS> LoadedTemplates<'a, (), LS> { +impl<'a, LS> LoadedTemplates<(), LS> { pub fn new(localisation_system: LS) -> Self { LoadedTemplates { template_functions: Default::default(), @@ -72,7 +72,7 @@ impl<'a, LS> LoadedTemplates<'a, (), LS> { let keys_to_remove: Vec = self .template_functions .range(prefix.to_owned()..) - .map(|(k, v)| k) + .map(|(k, _v)| k) .take_while(|k| k.starts_with(&prefix)) .cloned() .collect(); @@ -120,7 +120,7 @@ impl<'a, LS> LoadedTemplates<'a, (), LS> { } pub struct PreparedTemplate<'a, L, LS> { - pub(crate) all_instructions: Arc>>>>, + pub(crate) all_instructions: Arc>>>>, pub(crate) entrypoint: String, pub(crate) scope: Scope<'a, L>, pub localisation: Arc, diff --git a/hornbeam_ir/src/ast_to_ir.rs b/hornbeam_ir/src/ast_to_ir.rs index e1573d8..e64bef0 100644 --- a/hornbeam_ir/src/ast_to_ir.rs +++ b/hornbeam_ir/src/ast_to_ir.rs @@ -1,8 +1,10 @@ use crate::ir::{Step, StepDef}; use hornbeam_grammar::ast::{Block, Expression, StringExpr, StringPiece, Template}; +use hornbeam_grammar::intern; use std::borrow::Cow; use std::collections::btree_map::Entry; use std::collections::BTreeMap; +use std::ops::Deref; use thiserror::Error; #[derive(Error, Debug, Clone)] @@ -23,9 +25,9 @@ pub enum AstToIrError { /// Fragments are extracted to `{template_name}__{fragment_name}`. /// The top-level template is extracted to `{template_name}`. pub(crate) fn pull_out_entrypoints<'a>( - mut template: Template<'a>, + mut template: Template, template_name: &str, -) -> Result>>, AstToIrError> { +) -> Result>, AstToIrError> { let mut functions = BTreeMap::new(); for child in &mut template.blocks { @@ -51,9 +53,9 @@ pub(crate) fn pull_out_entrypoints<'a>( } fn pull_out_entrypoints_from_block<'a>( - block: &mut Block<'a>, + block: &mut Block, template_name: &str, - target: &mut BTreeMap>>, + target: &mut BTreeMap>, ) -> Result<(), AstToIrError> { match block { Block::HtmlElement(he) => { @@ -86,7 +88,7 @@ fn pull_out_entrypoints_from_block<'a>( let new_func_name = format!("{template_name}__{}", frag.name); // Canonicalise the fragment name that is left in the AST nodes. - frag.name = Cow::from(new_func_name.clone()); + frag.name = intern(new_func_name.clone()); match target.entry(new_func_name) { Entry::Vacant(ve) => { @@ -112,8 +114,8 @@ fn pull_out_entrypoints_from_block<'a>( /// Step 2. Compile the AST to IR steps. pub(crate) fn compile_functions<'a>( - functions: &BTreeMap>>, -) -> Result>>, AstToIrError> { + functions: &BTreeMap>, +) -> Result>>, AstToIrError> { let mut result = BTreeMap::new(); for (func_name, func_blocks) in functions { let mut steps = Vec::new(); @@ -126,12 +128,12 @@ pub(crate) fn compile_functions<'a>( } fn compile_ast_block_to_steps<'a>( - block: &Block<'a>, - instructions: &mut Vec>, + block: &Block, + instructions: &mut Vec>, ) -> Result<(), AstToIrError> { match block { Block::HtmlElement(he) => { - let text = if he.name == "html" { + let text = if he.name.deref() == "html" { Cow::from("( instructions.push(Step { def: StepDef::WriteLiteral { escape: false, - text, + text: intern(text), }, locator: (), }); @@ -153,7 +155,7 @@ fn compile_ast_block_to_steps<'a>( instructions.push(Step { def: StepDef::WriteLiteral { escape: false, - text: Cow::from(format!(" {attr_name}=\"")), + text: intern(format!(" {attr_name}=\"")), }, locator: (), }); @@ -169,7 +171,7 @@ fn compile_ast_block_to_steps<'a>( instructions.push(Step { def: StepDef::WriteLiteral { escape: false, - text: Cow::from("\""), + text: intern("\""), }, locator: (), }); @@ -179,7 +181,7 @@ fn compile_ast_block_to_steps<'a>( instructions.push(Step { def: StepDef::WriteLiteral { escape: false, - text: Cow::from(">"), + text: intern(">"), }, locator: (), }); @@ -191,7 +193,7 @@ fn compile_ast_block_to_steps<'a>( instructions.push(Step { def: StepDef::WriteLiteral { escape: false, - text: Cow::from(format!("", he.name)), + text: intern(format!("", he.name)), }, locator: (), }); diff --git a/hornbeam_ir/src/ir.rs b/hornbeam_ir/src/ir.rs index 51de888..8aa144a 100644 --- a/hornbeam_ir/src/ir.rs +++ b/hornbeam_ir/src/ir.rs @@ -1,51 +1,51 @@ pub use hornbeam_grammar::ast::{Expression, StringPiece}; +use hornbeam_grammar::IStr; use serde::Serialize; -use std::borrow::Cow; use std::collections::BTreeMap; #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Function<'a, L> { - pub name: Cow<'a, str>, +pub struct Function { + pub name: IStr, pub locator: L, - pub steps: Vec>, + pub steps: Vec>, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Step<'a, L> { +pub struct Step { #[serde(flatten)] - pub def: StepDef<'a, L>, + pub def: StepDef, pub locator: L, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum StepDef<'a, L> { +pub enum StepDef { WriteLiteral { escape: bool, - text: Cow<'a, str>, + text: IStr, }, WriteEval { escape: bool, - expr: Expression<'a>, + expr: Expression, }, If { - condition: Expression<'a>, - true_steps: Vec>, - false_steps: Vec>, + condition: Expression, + true_steps: Vec>, + false_steps: Vec>, }, For { - iterable: Expression<'a>, + iterable: Expression, // TODO! - binding: Cow<'a, str>, - body_steps: Vec>, - empty_steps: Vec>, + binding: IStr, + body_steps: Vec>, + empty_steps: Vec>, }, Call { - name: Cow<'a, str>, - args: BTreeMap, Expression<'a>>, - slots: BTreeMap, Vec>>, + name: IStr, + args: BTreeMap, + slots: BTreeMap>>, }, CallSlotWithParentScope { - name: Cow<'a, str>, + name: IStr, optional: bool, }, } diff --git a/hornbeam_ir/src/lib.rs b/hornbeam_ir/src/lib.rs index 342fe32..7f28e52 100644 --- a/hornbeam_ir/src/lib.rs +++ b/hornbeam_ir/src/lib.rs @@ -19,10 +19,10 @@ mod peephole; pub use ast_to_ir::AstToIrError; -pub fn ast_to_optimised_ir<'a>( +pub fn ast_to_optimised_ir( template_name: &str, - template: Template<'a>, -) -> Result>>, AstToIrError> { + template: Template, +) -> Result>>, AstToIrError> { let entrypoints = pull_out_entrypoints(template, template_name)?; let mut compiled_funcs = ast_to_ir::compile_functions(&entrypoints)?; for steps in compiled_funcs.values_mut() { diff --git a/hornbeam_ir/src/peephole.rs b/hornbeam_ir/src/peephole.rs index b56af07..56dc979 100644 --- a/hornbeam_ir/src/peephole.rs +++ b/hornbeam_ir/src/peephole.rs @@ -3,8 +3,8 @@ // - WriteLiteral, WriteLiteral -> combine use crate::ir::{Step, StepDef}; -use hornbeam_grammar::ast::{Expression, StringExpr, StringPiece}; -use std::borrow::Cow; +use hornbeam_grammar::ast::{Expression, StringPiece}; +use hornbeam_grammar::intern; //// Peephole Machinery @@ -48,10 +48,7 @@ fn peephole_opt]) -> ()>( *steps = result; } -fn apply_peephole_pass<'a, L, F: Fn(&mut Vec>)>( - steps: &mut Vec>, - pass: &F, -) { +fn apply_peephole_pass<'a, L, F: Fn(&mut Vec>)>(steps: &mut Vec>, pass: &F) { pass(steps); for step in steps { match &mut step.def { @@ -106,7 +103,7 @@ fn pass_write_eval_literal_to_write_literal(steps: &mut Vec>) { } step.def = StepDef::WriteLiteral { escape: *escape, - text: Cow::from(buf), + text: intern(buf), }; } } @@ -118,9 +115,9 @@ fn pass_write_literal_preescape(steps: &mut Vec>) { if let StepDef::WriteLiteral { escape, text } = &mut step.def { if *escape { *escape = false; - let safe = html_escape::encode_safe(text); - if *text != safe { - *text = Cow::from(String::from(safe)); + let safe = html_escape::encode_safe(text as &str); + if text as &str != &safe as &str { + *text = intern(safe); } } } @@ -146,7 +143,7 @@ fn pass_combine_write_literals(steps: &mut Vec>) { ) = (&mut left.def, &mut right.def) { if *escape_left == *escape_right { - let combined_text = Cow::from(String::from(text_left as &str) + text_right); + let combined_text = intern(String::from(text_left as &str) + text_right); *text_left = combined_text; r[0] = None; }