Replace cows with arc-interned strings

This commit is contained in:
Olivier 'reivilibre' 2023-02-28 20:07:05 +00:00
parent b5d424eb1c
commit 67545f9e8e
11 changed files with 245 additions and 150 deletions

92
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }

View File

@ -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<Block<'a>>,
pub struct Template {
pub blocks: Vec<Block>,
}
#[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<Block<'a>>,
pub classes: Vec<Cow<'a, str>>,
pub dom_id: Option<Cow<'a, str>>,
pub attributes: BTreeMap<Cow<'a, str>, Expression<'a>>,
pub struct HtmlElement {
pub name: IStr,
pub children: Vec<Block>,
pub classes: Vec<IStr>,
pub dom_id: Option<IStr>,
pub attributes: BTreeMap<IStr, Expression>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct ComponentElement<'a> {
pub name: Cow<'a, str>,
pub slots: BTreeMap<Cow<'a, str>, Vec<Block<'a>>>,
pub attributes: BTreeMap<Cow<'a, str>, Expression<'a>>,
pub struct ComponentElement {
pub name: IStr,
pub slots: BTreeMap<IStr, Vec<Block>>,
pub attributes: BTreeMap<IStr, Expression>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct IfBlock<'a> {
pub condition: Expression<'a>,
pub blocks: Vec<Block<'a>>,
pub else_blocks: Vec<Block<'a>>,
pub struct IfBlock {
pub condition: Expression,
pub blocks: Vec<Block>,
pub else_blocks: Vec<Block>,
}
#[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<Block<'a>>,
pub struct DefineFragment {
pub name: IStr,
pub blocks: Vec<Block>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct StringExpr<'a> {
pub pieces: Vec<StringPiece<'a>>,
pub struct StringExpr {
pub pieces: Vec<StringPiece>,
}
#[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<Cow<'a, str>, Expression<'a>>,
trans_key: IStr,
parameters: BTreeMap<IStr, Expression>,
},
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum Expression<'a> {
pub enum Expression {
// Arithmetic Operators
Add {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
Sub {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
Mul {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
Div {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
Negate {
sub: Box<Expression<'a>>,
sub: Box<Expression>,
},
// Boolean Operators
BAnd {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
BOr {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
BNot {
sub: Box<Expression<'a>>,
sub: Box<Expression>,
},
// Comparators
Equals {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
// Other Operators
ListAdd {
left: Box<Expression<'a>>,
right: Box<Expression<'a>>,
left: Box<Expression>,
right: Box<Expression>,
},
// Literals
List {
elements: Vec<Expression<'a>>,
elements: Vec<Expression>,
},
IntLiteral {
val: i64,
},
StringExpr(StringExpr<'a>),
StringExpr(StringExpr),
// Relatives
FieldLookup {
obj: Box<Expression<'a>>,
ident: Cow<'a, str>,
obj: Box<Expression>,
ident: IStr,
},
MethodCall {
obj: Box<Expression<'a>>,
ident: Cow<'a, str>,
args: Vec<Expression<'a>>,
obj: Box<Expression>,
ident: IStr,
args: Vec<Expression>,
},
// Other Primaries
Variable {
name: Cow<'a, str>,
name: IStr,
},
FunctionCall {
name: Cow<'a, str>,
args: Vec<Expression<'a>>,
name: IStr,
args: Vec<Expression>,
},
}

View File

@ -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<Rule>;
pub type IStr = ArcIntern<String>;
pub fn intern(s: impl Into<String>) -> ArcIntern<String> {
ArcIntern::new(s.into())
}

View File

@ -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<Cow<str>> {
Ok(Cow::from(input.as_str()))
fn ElementName(input: Node) -> PCResult<IStr> {
Ok(intern(input.as_str()))
}
fn SupplySlot(input: Node) -> PCResult<(Cow<str>, Vec<Block>, Span)> {
fn SupplySlot(input: Node) -> PCResult<(IStr, Vec<Block>, 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<Cow<str>> {
Ok(Cow::from(input.as_str()))
fn Identifier(input: Node) -> PCResult<IStr> {
Ok(intern(input.as_str()))
}
fn LocalisationIdentifier(input: Node) -> PCResult<Cow<str>> {
Ok(Cow::from(input.as_str()))
fn LocalisationIdentifier(input: Node) -> PCResult<IStr> {
Ok(intern(input.as_str()))
}
fn CssClass(input: Node) -> PCResult<Cow<str>> {
Ok(Cow::from(input.as_str()))
fn CssClass(input: Node) -> PCResult<IStr> {
Ok(intern(input.as_str()))
}
fn DomId(input: Node) -> PCResult<Cow<str>> {
Ok(Cow::from(input.as_str()))
fn DomId(input: Node) -> PCResult<IStr> {
Ok(intern(input.as_str()))
}
fn Text(input: Node) -> PCResult<Block> {
@ -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<str>, 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<BTreeMap<Cow<str>, Expression>> {
fn MapLiteral(input: Node) -> PCResult<BTreeMap<IStr, Expression>> {
Ok(match_nodes!(input.into_children();
[KVPair(kv_pair)..] => kv_pair.collect()
))
}
fn SEscape(input: Node) -> PCResult<Cow<str>> {
fn SEscape(input: Node) -> PCResult<IStr> {
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<Expression> {
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<Item = Node<'a>>) -> PCResult<Vec<Block<'a>>> {
fn helper_blocks<'a>(input: impl Iterator<Item = Node<'a>>) -> PCResult<Vec<Block>> {
let mut result = Vec::with_capacity(input.size_hint().0);
for child in input {
if let Some(block) = HornbeamParser::helper_block(child)? {

View File

@ -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<L>],
}
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<String, Arc<Vec<Step<'a, L>>>>,
pub(crate) program: &'a BTreeMap<String, Arc<Vec<Step<L>>>>,
pub(crate) output: O,
pub(crate) localisation: Arc<LS>,
pub(crate) scopes: Vec<Scope<'a, L>>,
@ -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<L>],
) -> Result<(), InterpreterError<LS::Error, O::Error>> {
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<L>,
) -> Result<(), InterpreterError<LS::Error, O::Error>> {
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<Value, InterpreterError<LS::Error, O::Error>> {
match expr {
Expression::Add { left, right } => {

View File

@ -37,10 +37,10 @@ pub trait OutputSystem {
pub use crate::engine::Value;
use crate::InterpreterError;
pub struct LoadedTemplates<'a, L, LS> {
pub struct LoadedTemplates<L, 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<'a, L>>>>,
template_functions: BTreeMap<String, Arc<Vec<Step<L>>>>,
localisation: Arc<LS>,
}
@ -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<String> = 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<BTreeMap<String, Arc<Vec<Step<'a, L>>>>>,
pub(crate) all_instructions: Arc<BTreeMap<String, Arc<Vec<Step<L>>>>>,
pub(crate) entrypoint: String,
pub(crate) scope: Scope<'a, L>,
pub localisation: Arc<LS>,

View File

@ -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<BTreeMap<String, Vec<Block<'a>>>, AstToIrError> {
) -> Result<BTreeMap<String, Vec<Block>>, 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<String, Vec<Block<'a>>>,
target: &mut BTreeMap<String, Vec<Block>>,
) -> 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<String, Vec<Block<'a>>>,
) -> Result<BTreeMap<String, Vec<Step<'a, ()>>>, AstToIrError> {
functions: &BTreeMap<String, Vec<Block>>,
) -> Result<BTreeMap<String, Vec<Step<()>>>, 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<Step<'a, ()>>,
block: &Block,
instructions: &mut Vec<Step<()>>,
) -> Result<(), AstToIrError> {
match block {
Block::HtmlElement(he) => {
let text = if he.name == "html" {
let text = if he.name.deref() == "html" {
Cow::from("<!DOCTYPE html><html")
} else {
Cow::from(format!("<{}", he.name))
@ -143,7 +145,7 @@ fn compile_ast_block_to_steps<'a>(
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: (),
});

View File

@ -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<L> {
pub name: IStr,
pub locator: L,
pub steps: Vec<Step<'a, L>>,
pub steps: Vec<Step<L>>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct Step<'a, L> {
pub struct Step<L> {
#[serde(flatten)]
pub def: StepDef<'a, L>,
pub def: StepDef<L>,
pub locator: L,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum StepDef<'a, L> {
pub enum StepDef<L> {
WriteLiteral {
escape: bool,
text: Cow<'a, str>,
text: IStr,
},
WriteEval {
escape: bool,
expr: Expression<'a>,
expr: Expression,
},
If {
condition: Expression<'a>,
true_steps: Vec<Step<'a, L>>,
false_steps: Vec<Step<'a, L>>,
condition: Expression,
true_steps: Vec<Step<L>>,
false_steps: Vec<Step<L>>,
},
For {
iterable: Expression<'a>,
iterable: Expression,
// TODO!
binding: Cow<'a, str>,
body_steps: Vec<Step<'a, L>>,
empty_steps: Vec<Step<'a, L>>,
binding: IStr,
body_steps: Vec<Step<L>>,
empty_steps: Vec<Step<L>>,
},
Call {
name: Cow<'a, str>,
args: BTreeMap<Cow<'a, str>, Expression<'a>>,
slots: BTreeMap<Cow<'a, str>, Vec<Step<'a, L>>>,
name: IStr,
args: BTreeMap<IStr, Expression>,
slots: BTreeMap<IStr, Vec<Step<L>>>,
},
CallSlotWithParentScope {
name: Cow<'a, str>,
name: IStr,
optional: bool,
},
}

View File

@ -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<BTreeMap<String, Vec<Step<'a, ()>>>, AstToIrError> {
template: Template,
) -> 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() {

View File

@ -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<T, F: FnMut(&mut [Option<T>]) -> ()>(
*steps = result;
}
fn apply_peephole_pass<'a, L, F: Fn(&mut Vec<Step<'a, L>>)>(
steps: &mut Vec<Step<'a, L>>,
pass: &F,
) {
fn apply_peephole_pass<'a, L, F: Fn(&mut Vec<Step<L>>)>(steps: &mut Vec<Step<L>>, pass: &F) {
pass(steps);
for step in steps {
match &mut step.def {
@ -106,7 +103,7 @@ fn pass_write_eval_literal_to_write_literal<L>(steps: &mut Vec<Step<L>>) {
}
step.def = StepDef::WriteLiteral {
escape: *escape,
text: Cow::from(buf),
text: intern(buf),
};
}
}
@ -118,9 +115,9 @@ fn pass_write_literal_preescape<L>(steps: &mut Vec<Step<L>>) {
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<L>(steps: &mut Vec<Step<L>>) {
) = (&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;
}