use super::HasSpan;
use super::Lookup;
use crate::parser::{ContextLevel, Parse, ParseErrorKind, ParseRes, Parser, Token};
use serde_derive::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::ops::Range;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Str<'source> {
pub sequence: Vec<StrComponent>,
span: Range<usize>,
phantom: PhantomData<&'source Self>,
}
impl HasSpan for Str<'_> {
fn span(&self) -> &Range<usize> {
&self.span
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub enum StrComponent {
Literal(String),
Lookup(Lookup),
}
impl<'source> Str<'source> {
fn new(first_component: StrComponent, component_span: Range<usize>) -> Self {
Self {
sequence: vec![first_component],
span: component_span,
phantom: PhantomData,
}
}
fn extend(
&mut self,
component: StrComponent,
last_component_span: Range<usize>,
mut component_span: Range<usize>,
full_input: &'source str,
) -> Range<usize> {
match component {
StrComponent::Literal(_) => {
if matches!(self.sequence.last().unwrap(), StrComponent::Literal(_)) {
component_span.start = last_component_span.start;
let last_ref = self.sequence.last_mut().unwrap();
*last_ref =
StrComponent::Literal(full_input[component_span.clone()].to_string());
} else {
component_span.start = last_component_span.end;
self.sequence.push(StrComponent::Literal(
full_input[component_span.clone()].to_string(),
));
}
},
StrComponent::Lookup(_) => {
if matches!(self.sequence.last().unwrap(), StrComponent::Literal(_)) {
let last_component_span = last_component_span.start..component_span.start;
let last_ref = self.sequence.last_mut().unwrap();
*last_ref = StrComponent::Literal(full_input[last_component_span].to_string());
} else {
self.sequence.push(StrComponent::Literal(
full_input[last_component_span.end..component_span.start].to_string(),
));
}
self.sequence.push(component);
},
}
component_span
}
}
impl<'source> Parse<'source> for (StrComponent, Range<usize>) {
fn parse(parser: &mut Parser<'source>) -> ParseRes<Self> {
let res = match parser.current_token()?.unwrap() {
Token::Variable => {
let span = parser.current_token_span().clone();
(
match parser.parse() {
Ok(lookup) => StrComponent::Lookup(lookup),
Err(e) if matches!(e.kind, ParseErrorKind::UnknownVariable) => {
StrComponent::Literal(parser.current_token_source().to_string())
},
Err(e) => return Err(e),
},
span,
)
},
Token::Identifier => (
StrComponent::Literal(parser.current_token_source().to_string()),
parser.current_token_span().clone(),
),
Token::RawIdentifier => {
let mut tok_chars = parser.current_token_source().chars();
tok_chars.next();
tok_chars.next_back();
(
StrComponent::Literal(tok_chars.collect()),
parser.current_token_span().clone(),
)
},
_ => unreachable!("The main str parser should have stopped on these already."),
};
parser.accept_current();
Ok(res)
}
}
impl<'source> Parse<'source> for Str<'source> {
#[allow(clippy::blocks_in_if_conditions)] fn parse(parser: &mut Parser<'source>) -> ParseRes<Self> {
parser.with_context(ContextLevel::InterpSeq, |parser| {
let (mut str, mut last_span) =
{
parser.expect_one_of_tokens(
&[Token::Variable, Token::Identifier, Token::RawIdentifier],
Some("interpolated strings need at least one variable or string without spaces"),
)?;
let (component, span) = parser.parse()?;
(Self::new(component, span.clone()), span)
};
while parser
.current_token()?
.filter(|tok| {
matches!(
tok,
Token::Variable | Token::Identifier | Token::RawIdentifier
)
})
.is_some()
{
let (next_component, next_span) = parser.parse()?;
last_span = str.extend(next_component, last_span, next_span, parser.source());
}
Ok(str)
})
}
}
use crate::interpreter;
impl interpreter::Resolve for Str<'_> {
fn resolve(
&self,
cache: &mut interpreter::Cache,
) -> Result<interpreter::ExprResult, interpreter::ErrorKind> {
let mut str = String::new();
for x in &self.sequence {
match x {
StrComponent::Literal(lit) => str.push_str(lit),
StrComponent::Lookup(lookup) => {
str.push_str(&cache.resolve(lookup)?.cast_to_string())
},
}
}
Ok(str.into())
}
}