Use custom Arc backed input
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
1ea15f0e49
commit
1ee7611981
6 changed files with 283 additions and 77 deletions
|
|
@ -24,8 +24,8 @@ pub struct TemplateAst<'input> {
|
||||||
root: Vec<TemplateAstExpr<'input>>,
|
root: Vec<TemplateAstExpr<'input>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> TemplateAst<'input> {
|
impl TemplateAst<'_> {
|
||||||
pub fn root(&self) -> &[TemplateAstExpr<'input>] {
|
pub fn root(&self) -> &[TemplateAstExpr<'_>] {
|
||||||
&self.root
|
&self.root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,22 +73,22 @@ impl ModalError for AstError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> FromRecoverableError<Input<'input>, AstError> for AstError {
|
impl FromRecoverableError<Input<'_>, AstError> for AstError {
|
||||||
fn from_recoverable_error(
|
fn from_recoverable_error(
|
||||||
token_start: &<Input<'input> as winnow::stream::Stream>::Checkpoint,
|
token_start: &<Input as winnow::stream::Stream>::Checkpoint,
|
||||||
_err_start: &<Input<'input> as winnow::stream::Stream>::Checkpoint,
|
_err_start: &<Input as winnow::stream::Stream>::Checkpoint,
|
||||||
input: &Input<'input>,
|
input: &Input,
|
||||||
mut e: AstError,
|
mut e: AstError,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> AddContext<Input<'input>, AstError> for AstError {
|
impl AddContext<Input<'_>, AstError> for AstError {
|
||||||
fn add_context(
|
fn add_context(
|
||||||
mut self,
|
mut self,
|
||||||
_input: &Input<'input>,
|
_input: &Input,
|
||||||
_token_start: &<Input<'input> as Stream>::Checkpoint,
|
_token_start: &<Input as Stream>::Checkpoint,
|
||||||
context: AstError,
|
context: AstError,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.message = context.message.or(self.message);
|
self.message = context.message.or(self.message);
|
||||||
|
|
@ -97,10 +97,10 @@ impl<'input> AddContext<Input<'input>, AstError> for AstError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> ParserError<Input<'input>> for AstError {
|
impl ParserError<Input<'_>> for AstError {
|
||||||
type Inner = AstError;
|
type Inner = AstError;
|
||||||
|
|
||||||
fn from_input(_input: &Input<'input>) -> Self {
|
fn from_input(_input: &Input<'_>) -> Self {
|
||||||
AstError::ctx()
|
AstError::ctx()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,24 +117,22 @@ impl<'input> ParserError<Input<'input>> for AstError {
|
||||||
pub struct AstFailure {}
|
pub struct AstFailure {}
|
||||||
|
|
||||||
impl AstFailure {
|
impl AstFailure {
|
||||||
fn from_errors(_errors: Vec<AstError>, _input: &[TemplateToken<'_>]) -> AstFailure {
|
fn from_errors(_errors: Vec<AstError>, _input: &[TemplateToken]) -> AstFailure {
|
||||||
AstFailure {}
|
AstFailure {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Input<'input> = Recoverable<TokenSlice<'input, TemplateToken<'input>>, AstError>;
|
type Input<'input> = Recoverable<TokenSlice<'input, TemplateToken>, AstError>;
|
||||||
|
|
||||||
impl<'i> Parser<Input<'i>, TemplateToken<'i>, AstError> for TokenKind {
|
impl<'input> Parser<Input<'input>, TemplateToken, AstError> for TokenKind {
|
||||||
fn parse_next(&mut self, input: &mut Input<'i>) -> winnow::Result<TemplateToken<'i>, AstError> {
|
fn parse_next(&mut self, input: &mut Input<'input>) -> winnow::Result<TemplateToken, AstError> {
|
||||||
winnow::token::literal(*self)
|
winnow::token::literal(*self)
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
.map(|t| t[0].clone())
|
.map(|t| t[0].clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<'input>(
|
pub fn parse(input: &[TemplateToken]) -> Result<TemplateAst<'_>, AstFailure> {
|
||||||
input: &'input [TemplateToken<'input>],
|
|
||||||
) -> Result<TemplateAst<'input>, AstFailure> {
|
|
||||||
let (_remaining, val, errors) = parse_ast.recoverable_parse(TokenSlice::new(input));
|
let (_remaining, val, errors) = parse_ast.recoverable_parse(TokenSlice::new(input));
|
||||||
|
|
||||||
if errors.is_empty()
|
if errors.is_empty()
|
||||||
|
|
@ -148,15 +146,15 @@ pub fn parse<'input>(
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TemplateAstExpr<'input> {
|
pub enum TemplateAstExpr<'input> {
|
||||||
StaticContent(TemplateToken<'input>),
|
StaticContent(TemplateToken),
|
||||||
Interpolation {
|
Interpolation {
|
||||||
prev_whitespace: Option<TemplateToken<'input>>,
|
prev_whitespace: Option<TemplateToken>,
|
||||||
wants_output: Option<TemplateToken<'input>>,
|
wants_output: Option<TemplateToken>,
|
||||||
expression: Box<TemplateAstExpr<'input>>,
|
expression: Box<TemplateAstExpr<'input>>,
|
||||||
post_whitespace: Option<TemplateToken<'input>>,
|
post_whitespace: Option<TemplateToken>,
|
||||||
},
|
},
|
||||||
VariableAccess(TemplateToken<'input>),
|
VariableAccess(TemplateToken),
|
||||||
Invalid(&'input [TemplateToken<'input>]),
|
Invalid(&'input [TemplateToken]),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ast<'input>(input: &mut Input<'input>) -> Result<Vec<TemplateAstExpr<'input>>, AstError> {
|
fn parse_ast<'input>(input: &mut Input<'input>) -> Result<Vec<TemplateAstExpr<'input>>, AstError> {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::ast::TemplateAstExpr;
|
use crate::ast::TemplateAstExpr;
|
||||||
|
use crate::input::TempleInput;
|
||||||
|
|
||||||
pub struct EmitMachine {
|
pub struct EmitMachine {
|
||||||
current_index: usize,
|
current_index: usize,
|
||||||
|
|
@ -23,10 +24,19 @@ pub struct VariableSlot {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
AppendContent { content: String },
|
AppendContent {
|
||||||
LoadFromContextToSlot { name: String, slot: VariableSlot },
|
content: TempleInput,
|
||||||
EmitFromSlot { slot: VariableSlot },
|
},
|
||||||
PushScope { inherit_parent: bool },
|
LoadFromContextToSlot {
|
||||||
|
name: TempleInput,
|
||||||
|
slot: VariableSlot,
|
||||||
|
},
|
||||||
|
EmitFromSlot {
|
||||||
|
slot: VariableSlot,
|
||||||
|
},
|
||||||
|
PushScope {
|
||||||
|
inherit_parent: bool,
|
||||||
|
},
|
||||||
Abort,
|
Abort,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,11 +52,15 @@ pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> Vec<Instruction> {
|
||||||
eval
|
eval
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_ast_expr(machine: &mut EmitMachine, eval: &mut Vec<Instruction>, ast: &TemplateAstExpr<'_>) {
|
fn emit_ast_expr(
|
||||||
|
machine: &mut EmitMachine,
|
||||||
|
eval: &mut Vec<Instruction>,
|
||||||
|
ast: &TemplateAstExpr<'_>,
|
||||||
|
) {
|
||||||
match ast {
|
match ast {
|
||||||
TemplateAstExpr::StaticContent(template_token) => {
|
TemplateAstExpr::StaticContent(template_token) => {
|
||||||
eval.push(Instruction::AppendContent {
|
eval.push(Instruction::AppendContent {
|
||||||
content: template_token.source().to_string(),
|
content: template_token.source().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
TemplateAstExpr::Interpolation {
|
TemplateAstExpr::Interpolation {
|
||||||
|
|
@ -57,7 +71,7 @@ fn emit_ast_expr(machine: &mut EmitMachine, eval: &mut Vec<Instruction>, ast: &T
|
||||||
} => {
|
} => {
|
||||||
if let Some(ws) = prev_whitespace {
|
if let Some(ws) = prev_whitespace {
|
||||||
eval.push(Instruction::AppendContent {
|
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<Instruction>, ast: &T
|
||||||
|
|
||||||
if let Some(ws) = post_whitespace {
|
if let Some(ws) = post_whitespace {
|
||||||
eval.push(Instruction::AppendContent {
|
eval.push(Instruction::AppendContent {
|
||||||
content: ws.source().to_string(),
|
content: ws.source().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +103,7 @@ fn emit_expr(
|
||||||
match expression {
|
match expression {
|
||||||
TemplateAstExpr::VariableAccess(template_token) => {
|
TemplateAstExpr::VariableAccess(template_token) => {
|
||||||
eval.push(Instruction::LoadFromContextToSlot {
|
eval.push(Instruction::LoadFromContextToSlot {
|
||||||
name: template_token.source().to_string(),
|
name: template_token.source().clone(),
|
||||||
slot: emit_slot,
|
slot: emit_slot,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
use crate::emit::Instruction;
|
use crate::emit::Instruction;
|
||||||
|
use crate::input::TempleInput;
|
||||||
|
|
||||||
#[derive(Debug, Error, Display)]
|
#[derive(Debug, Error, Display)]
|
||||||
enum EvalError {
|
enum EvalError {
|
||||||
/// An unknown variable was encountered: .0
|
/// An unknown variable was encountered: .0
|
||||||
UnknownVariable(String),
|
UnknownVariable(TempleInput),
|
||||||
/// An explicit abort was requested
|
/// An explicit abort was requested
|
||||||
ExplicitAbort,
|
ExplicitAbort,
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +26,7 @@ fn execute(instructions: &[Instruction], global_context: &Context) -> Result<Str
|
||||||
Instruction::LoadFromContextToSlot { name, slot } => {
|
Instruction::LoadFromContextToSlot { name, slot } => {
|
||||||
let value = global_context
|
let value = global_context
|
||||||
.values
|
.values
|
||||||
.get(name)
|
.get(name.as_str())
|
||||||
.ok_or(EvalError::UnknownVariable(name.clone()))?;
|
.ok_or(EvalError::UnknownVariable(name.clone()))?;
|
||||||
|
|
||||||
scopes.insert(*slot, value.clone());
|
scopes.insert(*slot, value.clone());
|
||||||
|
|
|
||||||
194
src/input.rs
Normal file
194
src/input.rs
Normal file
|
|
@ -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<str>,
|
||||||
|
range: Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<core::ops::Range<usize>> {
|
||||||
|
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<Self::Item> {
|
||||||
|
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<Self::Token> {
|
||||||
|
let c = self.chars().next()?;
|
||||||
|
|
||||||
|
self.range.start += c.len_utf8();
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_token(&self) -> Option<Self::Token> {
|
||||||
|
self.chars().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_for<P>(&self, predicate: P) -> Option<usize>
|
||||||
|
where
|
||||||
|
P: Fn(Self::Token) -> bool,
|
||||||
|
{
|
||||||
|
self.as_str().offset_for(predicate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_at(&self, tokens: usize) -> Result<usize, winnow::error::Needed> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ pub mod ast;
|
||||||
pub mod emit;
|
pub mod emit;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
mod input;
|
||||||
|
|
||||||
#[derive(Debug, Error, Display)]
|
#[derive(Debug, Error, Display)]
|
||||||
pub enum TempleError {
|
pub enum TempleError {
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,10 @@ use winnow::token::take_until;
|
||||||
use winnow::token::take_while;
|
use winnow::token::take_while;
|
||||||
|
|
||||||
use crate::SourceSpan;
|
use crate::SourceSpan;
|
||||||
|
use crate::input::TempleInput;
|
||||||
use crate::resume_after_cut;
|
use crate::resume_after_cut;
|
||||||
|
|
||||||
type Input<'input> = Recoverable<LocatingSlice<&'input str>, ParseError>;
|
type Input<'input> = Recoverable<LocatingSlice<TempleInput>, ParseError>;
|
||||||
type PResult<'input, T> = Result<T, ParseError>;
|
type PResult<'input, T> = Result<T, ParseError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -177,12 +178,12 @@ impl<'input> ParserError<Input<'input>> for ParseError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ParsedTemplate<'input> {
|
pub struct ParsedTemplate {
|
||||||
tokens: Vec<TemplateToken<'input>>,
|
tokens: Vec<TemplateToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> ParsedTemplate<'input> {
|
impl ParsedTemplate {
|
||||||
pub fn tokens(&self) -> &[TemplateToken<'input>] {
|
pub fn tokens(&self) -> &[TemplateToken] {
|
||||||
&self.tokens
|
&self.tokens
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,88 +199,86 @@ pub enum TokenKind {
|
||||||
Invalid,
|
Invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<TokenKind> for TemplateToken<'_> {
|
impl PartialEq<TokenKind> for TemplateToken {
|
||||||
fn eq(&self, other: &TokenKind) -> bool {
|
fn eq(&self, other: &TokenKind) -> bool {
|
||||||
self.kind == *other
|
self.kind == *other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl winnow::stream::ContainsToken<&'_ TemplateToken<'_>> for TokenKind {
|
impl winnow::stream::ContainsToken<&'_ TemplateToken> for TokenKind {
|
||||||
fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool {
|
fn contains_token(&self, token: &'_ TemplateToken) -> bool {
|
||||||
*self == token.kind
|
*self == token.kind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl winnow::stream::ContainsToken<&'_ TemplateToken<'_>> for &'_ [TokenKind] {
|
impl winnow::stream::ContainsToken<&'_ TemplateToken> for &'_ [TokenKind] {
|
||||||
fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool {
|
fn contains_token(&self, token: &'_ TemplateToken) -> bool {
|
||||||
self.contains(&token.kind)
|
self.contains(&token.kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const LEN: usize> winnow::stream::ContainsToken<&'_ TemplateToken<'_>>
|
impl<const LEN: usize> winnow::stream::ContainsToken<&'_ TemplateToken> for &'_ [TokenKind; LEN] {
|
||||||
for &'_ [TokenKind; LEN]
|
fn contains_token(&self, token: &'_ TemplateToken) -> bool {
|
||||||
{
|
|
||||||
fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool {
|
|
||||||
self.contains(&token.kind)
|
self.contains(&token.kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const LEN: usize> winnow::stream::ContainsToken<&'_ TemplateToken<'_>> for [TokenKind; LEN] {
|
impl<const LEN: usize> winnow::stream::ContainsToken<&'_ TemplateToken> for [TokenKind; LEN] {
|
||||||
fn contains_token(&self, token: &'_ TemplateToken<'_>) -> bool {
|
fn contains_token(&self, token: &'_ TemplateToken) -> bool {
|
||||||
self.contains(&token.kind)
|
self.contains(&token.kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TemplateToken<'input> {
|
pub struct TemplateToken {
|
||||||
kind: TokenKind,
|
kind: TokenKind,
|
||||||
source: &'input str,
|
source: TempleInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> TemplateToken<'input> {
|
impl TemplateToken {
|
||||||
fn content(source: &'input str) -> Self {
|
fn content(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::Content,
|
kind: TokenKind::Content,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn left_delim(source: &'input str) -> Self {
|
fn left_delim(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::LeftDelim,
|
kind: TokenKind::LeftDelim,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn right_delim(source: &'input str) -> Self {
|
fn right_delim(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::RightDelim,
|
kind: TokenKind::RightDelim,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wants_output(source: &'input str) -> Self {
|
fn wants_output(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::WantsOutput,
|
kind: TokenKind::WantsOutput,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident(source: &'input str) -> Self {
|
fn ident(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::Ident,
|
kind: TokenKind::Ident,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn whitespace(source: &'input str) -> Self {
|
fn whitespace(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::Whitespace,
|
kind: TokenKind::Whitespace,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalid(source: &'input str) -> Self {
|
fn invalid(source: TempleInput) -> Self {
|
||||||
TemplateToken {
|
TemplateToken {
|
||||||
kind: TokenKind::Invalid,
|
kind: TokenKind::Invalid,
|
||||||
source,
|
source,
|
||||||
|
|
@ -290,13 +289,14 @@ impl<'input> TemplateToken<'input> {
|
||||||
self.kind
|
self.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source(&self) -> &'input str {
|
pub fn source(&self) -> TempleInput {
|
||||||
self.source
|
self.source.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(input: &str) -> Result<ParsedTemplate<'_>, ParseFailure> {
|
pub fn parse(input: &str) -> Result<ParsedTemplate, ParseFailure> {
|
||||||
let (_remaining, val, errors) = parse_tokens.recoverable_parse(LocatingSlice::new(input));
|
let (_remaining, val, errors) =
|
||||||
|
parse_tokens.recoverable_parse(LocatingSlice::new(TempleInput::from(input)));
|
||||||
|
|
||||||
if errors.is_empty()
|
if errors.is_empty()
|
||||||
&& let Some(val) = val
|
&& let Some(val) = val
|
||||||
|
|
@ -307,13 +307,13 @@ pub fn parse(input: &str) -> Result<ParsedTemplate<'_>, ParseFailure> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tokens<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<TemplateToken<'input>>> {
|
fn parse_tokens<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<TemplateToken>> {
|
||||||
repeat_till(0.., alt((parse_interpolate, parse_content)), eof)
|
repeat_till(0.., alt((parse_interpolate, parse_content)), eof)
|
||||||
.map(|(v, _): (Vec<_>, _)| v.into_iter().flatten().collect())
|
.map(|(v, _): (Vec<_>, _)| v.into_iter().flatten().collect())
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_content<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<TemplateToken<'input>>> {
|
fn parse_content<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<TemplateToken>> {
|
||||||
alt((
|
alt((
|
||||||
repeat_till(1.., any, peek((multispace0, "{{"))).map(|((), _)| ()),
|
repeat_till(1.., any, peek((multispace0, "{{"))).map(|((), _)| ()),
|
||||||
rest.void(),
|
rest.void(),
|
||||||
|
|
@ -324,9 +324,7 @@ fn parse_content<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<Templ
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_interpolate<'input>(
|
fn parse_interpolate<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<TemplateToken>> {
|
||||||
input: &mut Input<'input>,
|
|
||||||
) -> PResult<'input, Vec<TemplateToken<'input>>> {
|
|
||||||
let prev_whitespace = opt(parse_whitespace).parse_next(input)?;
|
let prev_whitespace = opt(parse_whitespace).parse_next(input)?;
|
||||||
let left_delim = "{{".map(TemplateToken::left_delim).parse_next(input)?;
|
let left_delim = "{{".map(TemplateToken::left_delim).parse_next(input)?;
|
||||||
let wants_output = opt("=".map(TemplateToken::wants_output)).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
|
let (inside_tokens, _): (Vec<_>, _) = get_tokens
|
||||||
.resume_after(recover)
|
.resume_after(recover)
|
||||||
.with_taken()
|
.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)?;
|
.parse_next(input)?;
|
||||||
|
|
||||||
let right_delim = "}}".map(TemplateToken::right_delim).parse_next(input)?;
|
let right_delim = "}}".map(TemplateToken::right_delim).parse_next(input)?;
|
||||||
|
|
@ -354,9 +354,7 @@ fn parse_interpolate<'input>(
|
||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_interpolate_token<'input>(
|
fn parse_interpolate_token<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
|
||||||
input: &mut Input<'input>,
|
|
||||||
) -> PResult<'input, TemplateToken<'input>> {
|
|
||||||
trace(
|
trace(
|
||||||
"parse_interpolate_token",
|
"parse_interpolate_token",
|
||||||
alt((parse_ident, parse_whitespace)),
|
alt((parse_ident, parse_whitespace)),
|
||||||
|
|
@ -364,7 +362,7 @@ fn parse_interpolate_token<'input>(
|
||||||
.parse_next(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(
|
trace(
|
||||||
"parse_whitespace",
|
"parse_whitespace",
|
||||||
multispace1.map(TemplateToken::whitespace),
|
multispace1.map(TemplateToken::whitespace),
|
||||||
|
|
@ -372,7 +370,7 @@ fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, Templa
|
||||||
.parse_next(input)
|
.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(
|
resume_after_cut(
|
||||||
terminated(
|
terminated(
|
||||||
ident.map(TemplateToken::ident),
|
ident.map(TemplateToken::ident),
|
||||||
|
|
@ -390,7 +388,7 @@ fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateTok
|
||||||
.parse_next(input)
|
.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)
|
take_while(1.., char::is_alphanumeric).parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue