diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 07c492c..313bab4 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -24,8 +24,8 @@ pub struct TemplateAst<'input> { root: Vec>, } -impl<'input> TemplateAst<'input> { - pub fn root(&self) -> &[TemplateAstExpr<'input>] { +impl TemplateAst<'_> { + pub fn root(&self) -> &[TemplateAstExpr<'_>] { &self.root } } @@ -73,22 +73,22 @@ impl ModalError for AstError { } } -impl<'input> FromRecoverableError, AstError> for AstError { +impl FromRecoverableError, AstError> for AstError { fn from_recoverable_error( - token_start: & as winnow::stream::Stream>::Checkpoint, - _err_start: & as winnow::stream::Stream>::Checkpoint, - input: &Input<'input>, + token_start: &::Checkpoint, + _err_start: &::Checkpoint, + input: &Input, mut e: AstError, ) -> Self { e } } -impl<'input> AddContext, AstError> for AstError { +impl AddContext, AstError> for AstError { fn add_context( mut self, - _input: &Input<'input>, - _token_start: & as Stream>::Checkpoint, + _input: &Input, + _token_start: &::Checkpoint, context: AstError, ) -> Self { self.message = context.message.or(self.message); @@ -97,10 +97,10 @@ impl<'input> AddContext, AstError> for AstError { } } -impl<'input> ParserError> for AstError { +impl ParserError> for AstError { type Inner = AstError; - fn from_input(_input: &Input<'input>) -> Self { + fn from_input(_input: &Input<'_>) -> Self { AstError::ctx() } @@ -117,24 +117,22 @@ impl<'input> ParserError> for AstError { pub struct AstFailure {} impl AstFailure { - fn from_errors(_errors: Vec, _input: &[TemplateToken<'_>]) -> AstFailure { + fn from_errors(_errors: Vec, _input: &[TemplateToken]) -> AstFailure { AstFailure {} } } -type Input<'input> = Recoverable>, AstError>; +type Input<'input> = Recoverable, AstError>; -impl<'i> Parser, TemplateToken<'i>, AstError> for TokenKind { - fn parse_next(&mut self, input: &mut Input<'i>) -> winnow::Result, AstError> { +impl<'input> Parser, TemplateToken, AstError> for TokenKind { + fn parse_next(&mut self, input: &mut Input<'input>) -> winnow::Result { winnow::token::literal(*self) .parse_next(input) .map(|t| t[0].clone()) } } -pub fn parse<'input>( - input: &'input [TemplateToken<'input>], -) -> Result, AstFailure> { +pub fn parse(input: &[TemplateToken]) -> Result, AstFailure> { let (_remaining, val, errors) = parse_ast.recoverable_parse(TokenSlice::new(input)); if errors.is_empty() @@ -148,15 +146,15 @@ pub fn parse<'input>( #[derive(Debug, Clone)] pub enum TemplateAstExpr<'input> { - StaticContent(TemplateToken<'input>), + StaticContent(TemplateToken), Interpolation { - prev_whitespace: Option>, - wants_output: Option>, + prev_whitespace: Option, + wants_output: Option, expression: Box>, - post_whitespace: Option>, + post_whitespace: Option, }, - VariableAccess(TemplateToken<'input>), - Invalid(&'input [TemplateToken<'input>]), + VariableAccess(TemplateToken), + Invalid(&'input [TemplateToken]), } fn parse_ast<'input>(input: &mut Input<'input>) -> Result>, AstError> { diff --git a/src/emit/mod.rs b/src/emit/mod.rs index 32da751..6c5dd9e 100644 --- a/src/emit/mod.rs +++ b/src/emit/mod.rs @@ -1,4 +1,5 @@ use crate::ast::TemplateAstExpr; +use crate::input::TempleInput; pub struct EmitMachine { current_index: usize, @@ -23,10 +24,19 @@ pub struct VariableSlot { #[derive(Debug)] pub enum Instruction { - AppendContent { content: String }, - LoadFromContextToSlot { name: String, slot: VariableSlot }, - EmitFromSlot { slot: VariableSlot }, - PushScope { inherit_parent: bool }, + AppendContent { + content: TempleInput, + }, + LoadFromContextToSlot { + name: TempleInput, + slot: VariableSlot, + }, + EmitFromSlot { + slot: VariableSlot, + }, + PushScope { + inherit_parent: bool, + }, Abort, } @@ -42,11 +52,15 @@ pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> Vec { eval } -fn emit_ast_expr(machine: &mut EmitMachine, eval: &mut Vec, ast: &TemplateAstExpr<'_>) { +fn emit_ast_expr( + machine: &mut EmitMachine, + eval: &mut Vec, + ast: &TemplateAstExpr<'_>, +) { match ast { TemplateAstExpr::StaticContent(template_token) => { eval.push(Instruction::AppendContent { - content: template_token.source().to_string(), + content: template_token.source().clone(), }); } TemplateAstExpr::Interpolation { @@ -57,7 +71,7 @@ fn emit_ast_expr(machine: &mut EmitMachine, eval: &mut Vec, ast: &T } => { if let Some(ws) = prev_whitespace { eval.push(Instruction::AppendContent { - content: ws.source().to_string(), + content: ws.source().clone(), }); } @@ -70,7 +84,7 @@ fn emit_ast_expr(machine: &mut EmitMachine, eval: &mut Vec, ast: &T if let Some(ws) = post_whitespace { eval.push(Instruction::AppendContent { - content: ws.source().to_string(), + content: ws.source().clone(), }); } } @@ -89,7 +103,7 @@ fn emit_expr( match expression { TemplateAstExpr::VariableAccess(template_token) => { eval.push(Instruction::LoadFromContextToSlot { - name: template_token.source().to_string(), + name: template_token.source().clone(), slot: emit_slot, }); } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index c7a598c..a52f148 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -5,11 +5,12 @@ use thiserror::Error; use crate::Context; use crate::emit::Instruction; +use crate::input::TempleInput; #[derive(Debug, Error, Display)] enum EvalError { /// An unknown variable was encountered: .0 - UnknownVariable(String), + UnknownVariable(TempleInput), /// An explicit abort was requested ExplicitAbort, } @@ -25,7 +26,7 @@ fn execute(instructions: &[Instruction], global_context: &Context) -> Result { let value = global_context .values - .get(name) + .get(name.as_str()) .ok_or(EvalError::UnknownVariable(name.clone()))?; scopes.insert(*slot, value.clone()); diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 0000000..ff8fa36 --- /dev/null +++ b/src/input.rs @@ -0,0 +1,194 @@ +use std::ops::Deref; +use std::ops::Range; +use std::sync::Arc; + +use winnow::stream::Compare; +use winnow::stream::FindSlice; +use winnow::stream::Offset; +use winnow::stream::Stream; +use winnow::stream::StreamIsPartial; + +#[derive(Clone, PartialEq, Eq)] +pub struct TempleInput { + backing: Arc, + range: Range, +} + +impl std::fmt::Debug for TempleInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\"{}\"", self.as_str()) + } +} + +impl From<&str> for TempleInput { + fn from(value: &str) -> Self { + let backing = Arc::from(value.to_string()); + let range = 0..value.len(); + + TempleInput { backing, range } + } +} + +impl FindSlice<&str> for TempleInput { + fn find_slice(&self, substr: &str) -> Option> { + self.as_str().find_slice(substr) + } +} + +impl Compare<&str> for TempleInput { + fn compare(&self, t: &str) -> winnow::stream::CompareResult { + self.as_str().compare(t) + } +} + +impl Deref for TempleInput { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.backing[self.range.clone()] + } +} + +impl TempleInput { + pub fn as_str(&self) -> &str { + self.deref() + } +} + +impl Offset for TempleInput { + fn offset_from(&self, start: &Self) -> usize { + self.as_str().offset_from(&start.as_str()) + } +} + +pub struct TempleInputIter { + idx: usize, + input: TempleInput, +} + +impl Iterator for TempleInputIter { + type Item = (usize, char); + + fn next(&mut self) -> Option { + let val = self.input.as_str().char_indices().nth(self.idx); + self.idx += 1; + val + } +} + +impl StreamIsPartial for TempleInput { + type PartialState = (); + + fn complete(&mut self) -> Self::PartialState { + // Always complete + } + + fn restore_partial(&mut self, _state: Self::PartialState) {} + + fn is_partial_supported() -> bool { + false + } +} + +impl Stream for TempleInput { + type Token = char; + + type Slice = TempleInput; + + type IterOffsets = TempleInputIter; + + type Checkpoint = TempleInput; + + fn iter_offsets(&self) -> Self::IterOffsets { + TempleInputIter { + idx: 0, + input: self.clone(), + } + } + + fn eof_offset(&self) -> usize { + self.len() + } + + fn next_token(&mut self) -> Option { + let c = self.chars().next()?; + + self.range.start += c.len_utf8(); + Some(c) + } + + fn peek_token(&self) -> Option { + self.chars().next() + } + + fn offset_for

(&self, predicate: P) -> Option + where + P: Fn(Self::Token) -> bool, + { + self.as_str().offset_for(predicate) + } + + fn offset_at(&self, tokens: usize) -> Result { + self.as_str().offset_at(tokens) + } + + fn next_slice(&mut self, offset: usize) -> Self::Slice { + let mut next = self.clone(); + + self.range.start += offset; + next.range.end = self.range.start; + + next + } + + fn peek_slice(&self, offset: usize) -> Self::Slice { + let mut next = self.clone(); + next.range.end = self.range.start + offset; + next + } + + fn checkpoint(&self) -> Self::Checkpoint { + self.clone() + } + + fn reset(&mut self, checkpoint: &Self::Checkpoint) { + self.range = checkpoint.range.clone(); + } + + fn raw(&self) -> &dyn core::fmt::Debug { + self + } + + fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests { + use winnow::stream::Stream; + + use crate::input::TempleInput; + + #[test] + fn check_stream_impl() { + let mut stream = TempleInput::from("checking"); + + let checkpoint = stream.checkpoint(); + + assert_eq!(stream.peek_token(), Some('c')); + assert_eq!(stream.next_token(), Some('c')); + let next_slice = stream.next_slice(4); + assert_eq!(next_slice.as_str(), "heck"); + assert_eq!(stream.peek_token(), Some('i')); + + stream.reset(&checkpoint); + assert_eq!(stream.peek_token(), Some('c')); + + let peek = stream.peek_slice(4); + assert_eq!(peek.as_str(), "chec"); + + let eof_offset = stream.eof_offset(); + assert_eq!(eof_offset, 8); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9d1df4d..9601158 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub mod ast; pub mod emit; pub mod eval; pub mod parser; +mod input; #[derive(Debug, Error, Display)] pub enum TempleError { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 367b85e..ecf86d2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -31,9 +31,10 @@ use winnow::token::take_until; use winnow::token::take_while; use crate::SourceSpan; +use crate::input::TempleInput; use crate::resume_after_cut; -type Input<'input> = Recoverable, ParseError>; +type Input<'input> = Recoverable, ParseError>; type PResult<'input, T> = Result; #[derive(Debug)] @@ -177,12 +178,12 @@ impl<'input> ParserError> for ParseError { } #[derive(Debug)] -pub struct ParsedTemplate<'input> { - tokens: Vec>, +pub struct ParsedTemplate { + tokens: Vec, } -impl<'input> ParsedTemplate<'input> { - pub fn tokens(&self) -> &[TemplateToken<'input>] { +impl ParsedTemplate { + pub fn tokens(&self) -> &[TemplateToken] { &self.tokens } } @@ -198,88 +199,86 @@ pub enum TokenKind { Invalid, } -impl PartialEq for TemplateToken<'_> { +impl PartialEq for TemplateToken { fn eq(&self, other: &TokenKind) -> bool { self.kind == *other } } -impl winnow::stream::ContainsToken<&'_ TemplateToken<'_>> for TokenKind { - fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool { +impl winnow::stream::ContainsToken<&'_ TemplateToken> for TokenKind { + fn contains_token(&self, token: &'_ TemplateToken) -> bool { *self == token.kind } } -impl winnow::stream::ContainsToken<&'_ TemplateToken<'_>> for &'_ [TokenKind] { - fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool { +impl winnow::stream::ContainsToken<&'_ TemplateToken> for &'_ [TokenKind] { + fn contains_token(&self, token: &'_ TemplateToken) -> bool { self.contains(&token.kind) } } -impl winnow::stream::ContainsToken<&'_ TemplateToken<'_>> - for &'_ [TokenKind; LEN] -{ - fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool { +impl winnow::stream::ContainsToken<&'_ TemplateToken> for &'_ [TokenKind; LEN] { + fn contains_token(&self, token: &'_ TemplateToken) -> bool { self.contains(&token.kind) } } -impl winnow::stream::ContainsToken<&'_ TemplateToken<'_>> for [TokenKind; LEN] { - fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool { +impl winnow::stream::ContainsToken<&'_ TemplateToken> for [TokenKind; LEN] { + fn contains_token(&self, token: &'_ TemplateToken) -> bool { self.contains(&token.kind) } } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TemplateToken<'input> { +pub struct TemplateToken { kind: TokenKind, - source: &'input str, + source: TempleInput, } -impl<'input> TemplateToken<'input> { - fn content(source: &'input str) -> Self { +impl TemplateToken { + fn content(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::Content, source, } } - fn left_delim(source: &'input str) -> Self { + fn left_delim(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::LeftDelim, source, } } - fn right_delim(source: &'input str) -> Self { + fn right_delim(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::RightDelim, source, } } - fn wants_output(source: &'input str) -> Self { + fn wants_output(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::WantsOutput, source, } } - fn ident(source: &'input str) -> Self { + fn ident(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::Ident, source, } } - fn whitespace(source: &'input str) -> Self { + fn whitespace(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::Whitespace, source, } } - fn invalid(source: &'input str) -> Self { + fn invalid(source: TempleInput) -> Self { TemplateToken { kind: TokenKind::Invalid, source, @@ -290,13 +289,14 @@ impl<'input> TemplateToken<'input> { self.kind } - pub fn source(&self) -> &'input str { - self.source + pub fn source(&self) -> TempleInput { + self.source.clone() } } -pub fn parse(input: &str) -> Result, ParseFailure> { - let (_remaining, val, errors) = parse_tokens.recoverable_parse(LocatingSlice::new(input)); +pub fn parse(input: &str) -> Result { + let (_remaining, val, errors) = + parse_tokens.recoverable_parse(LocatingSlice::new(TempleInput::from(input))); if errors.is_empty() && let Some(val) = val @@ -307,13 +307,13 @@ pub fn parse(input: &str) -> Result, ParseFailure> { } } -fn parse_tokens<'input>(input: &mut Input<'input>) -> PResult<'input, Vec>> { +fn parse_tokens<'input>(input: &mut Input<'input>) -> PResult<'input, Vec> { repeat_till(0.., alt((parse_interpolate, parse_content)), eof) .map(|(v, _): (Vec<_>, _)| v.into_iter().flatten().collect()) .parse_next(input) } -fn parse_content<'input>(input: &mut Input<'input>) -> PResult<'input, Vec>> { +fn parse_content<'input>(input: &mut Input<'input>) -> PResult<'input, Vec> { alt(( repeat_till(1.., any, peek((multispace0, "{{"))).map(|((), _)| ()), rest.void(), @@ -324,9 +324,7 @@ fn parse_content<'input>(input: &mut Input<'input>) -> PResult<'input, Vec( - input: &mut Input<'input>, -) -> PResult<'input, Vec>> { +fn parse_interpolate<'input>(input: &mut Input<'input>) -> PResult<'input, Vec> { let prev_whitespace = opt(parse_whitespace).parse_next(input)?; let left_delim = "{{".map(TemplateToken::left_delim).parse_next(input)?; let wants_output = opt("=".map(TemplateToken::wants_output)).parse_next(input)?; @@ -337,7 +335,9 @@ fn parse_interpolate<'input>( let (inside_tokens, _): (Vec<_>, _) = get_tokens .resume_after(recover) .with_taken() - .map(|(val, taken)| val.unwrap_or_else(|| (vec![TemplateToken::invalid(taken)], ""))) + .map(|(val, taken)| { + val.unwrap_or_else(|| (vec![TemplateToken::invalid(taken)], TempleInput::from(""))) + }) .parse_next(input)?; let right_delim = "}}".map(TemplateToken::right_delim).parse_next(input)?; @@ -354,9 +354,7 @@ fn parse_interpolate<'input>( Ok(tokens) } -fn parse_interpolate_token<'input>( - input: &mut Input<'input>, -) -> PResult<'input, TemplateToken<'input>> { +fn parse_interpolate_token<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { trace( "parse_interpolate_token", alt((parse_ident, parse_whitespace)), @@ -364,7 +362,7 @@ fn parse_interpolate_token<'input>( .parse_next(input) } -fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken<'input>> { +fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { trace( "parse_whitespace", multispace1.map(TemplateToken::whitespace), @@ -372,7 +370,7 @@ fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, Templa .parse_next(input) } -fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken<'input>> { +fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { resume_after_cut( terminated( ident.map(TemplateToken::ident), @@ -390,7 +388,7 @@ fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateTok .parse_next(input) } -fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, &'input str> { +fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, TempleInput> { take_while(1.., char::is_alphanumeric).parse_next(input) }