blob: 4f8cc67f5e0197857d388f56a872eb24afcd9a22 [file] [log] [blame]
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt, quote};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::str::pattern::Pattern;
use std::{fmt, str::FromStr};
use crate::context::LocalContext;
use crate::fn_suffix::make_neon_suffix;
use crate::typekinds::{ToRepr, TypeRepr};
use crate::wildcards::Wildcard;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WildStringPart {
String(String),
Wildcard(Wildcard),
}
/// Wildcard-able string
#[derive(Debug, Clone, PartialEq, Eq, Default, SerializeDisplay, DeserializeFromStr)]
pub struct WildString(pub Vec<WildStringPart>);
impl WildString {
pub fn has_wildcards(&self) -> bool {
for part in self.0.iter() {
if let WildStringPart::Wildcard(..) = part {
return true;
}
}
false
}
pub fn wildcards(&self) -> impl Iterator<Item = &Wildcard> + '_ {
self.0.iter().filter_map(|part| match part {
WildStringPart::Wildcard(w) => Some(w),
_ => None,
})
}
pub fn iter(&self) -> impl Iterator<Item = &WildStringPart> + '_ {
self.0.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut WildStringPart> + '_ {
self.0.iter_mut()
}
pub fn starts_with(&self, s2: &str) -> bool {
self.to_string().starts_with(s2)
}
pub fn prepend_str(&mut self, s: impl Into<String>) {
self.0.insert(0, WildStringPart::String(s.into()))
}
pub fn push_str(&mut self, s: impl Into<String>) {
self.0.push(WildStringPart::String(s.into()))
}
pub fn push_wildcard(&mut self, w: Wildcard) {
self.0.push(WildStringPart::Wildcard(w))
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn replace<P>(&self, from: P, to: &str) -> WildString
where
P: Pattern + Copy,
{
WildString(
self.0
.iter()
.map(|part| match part {
WildStringPart::String(s) => WildStringPart::String(s.replace(from, to)),
part => part.clone(),
})
.collect_vec(),
)
}
pub fn build_acle(&mut self, ctx: &LocalContext) -> Result<(), String> {
self.build(ctx, TypeRepr::ACLENotation)
}
pub fn build_neon_intrinsic_signature(&mut self, ctx: &LocalContext) -> Result<(), String> {
let repr = TypeRepr::ACLENotation;
self.iter_mut().try_for_each(|wp| -> Result<(), String> {
if let WildStringPart::Wildcard(w) = wp {
match w {
&mut Wildcard::NEONType(_, _, ref maybe_suffix_kind) => {
if let Some(suffix_kind) = maybe_suffix_kind {
let x = ctx.provide_type_wildcard(w).unwrap();
*wp = WildStringPart::String(make_neon_suffix(x, *suffix_kind))
} else {
*wp = WildString::make_default_build(ctx, repr, w)
}
}
_ => *wp = WildString::make_default_build(ctx, repr, w),
}
}
Ok(())
})
}
pub fn build(&mut self, ctx: &LocalContext, repr: TypeRepr) -> Result<(), String> {
match repr {
TypeRepr::ACLENotation | TypeRepr::LLVMMachine => {
self.iter_mut().try_for_each(|wp| -> Result<(), String> {
if let WildStringPart::Wildcard(w) = wp {
match w {
&mut Wildcard::NEONType(_, _, ref maybe_suffix_kind) => {
if let Some(suffix_kind) = maybe_suffix_kind {
let x = ctx.provide_type_wildcard(w).unwrap();
*wp = WildStringPart::String(make_neon_suffix(x, *suffix_kind))
} else {
*wp = WildString::make_default_build(ctx, repr, w)
}
}
_ => *wp = WildString::make_default_build(ctx, repr, w),
}
}
Ok(())
})
}
_ => self.iter_mut().try_for_each(|wp| -> Result<(), String> {
if let WildStringPart::Wildcard(w) = wp {
*wp = WildString::make_default_build(ctx, repr, w);
}
Ok(())
}),
}
}
fn make_default_build(ctx: &LocalContext, repr: TypeRepr, w: &mut Wildcard) -> WildStringPart {
WildStringPart::String(
ctx.provide_substitution_wildcard(w)
.or_else(|_| ctx.provide_type_wildcard(w).map(|ty| ty.repr(repr)))
.unwrap(),
)
}
}
impl From<String> for WildString {
fn from(s: String) -> Self {
WildString(vec![WildStringPart::String(s)])
}
}
impl FromStr for WildString {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
enum State {
Normal { start: usize },
Wildcard { start: usize, count: usize },
EscapeTokenOpen { start: usize, at: usize },
EscapeTokenClose { start: usize, at: usize },
}
let mut ws = WildString::default();
match s
.char_indices()
.try_fold(State::Normal { start: 0 }, |state, (idx, ch)| {
match (state, ch) {
(State::Normal { start }, '{') => Ok(State::EscapeTokenOpen { start, at: idx }),
(State::Normal { start }, '}') => {
Ok(State::EscapeTokenClose { start, at: idx })
}
(State::EscapeTokenOpen { start, at }, '{')
| (State::EscapeTokenClose { start, at }, '}') => {
if start < at {
ws.push_str(&s[start..at])
}
Ok(State::Normal { start: idx })
}
(State::EscapeTokenOpen { at, .. }, '}') => Err(format!(
"empty wildcard given in string {s:?} at position {at}"
)),
(State::EscapeTokenOpen { start, at }, _) => {
if start < at {
ws.push_str(&s[start..at])
}
Ok(State::Wildcard {
start: idx,
count: 0,
})
}
(State::EscapeTokenClose { at, .. }, _) => Err(format!(
"closing a non-wildcard/bad escape in string {s:?} at position {at}"
)),
// Nesting wildcards is only supported for `{foo as {bar}}`, wildcards cannot be
// nested at the start of a WildString.
(State::Wildcard { start, count }, '{') => Ok(State::Wildcard {
start,
count: count + 1,
}),
(State::Wildcard { start, count: 0 }, '}') => {
ws.push_wildcard(s[start..idx].parse()?);
Ok(State::Normal { start: idx + 1 })
}
(State::Wildcard { start, count }, '}') => Ok(State::Wildcard {
start,
count: count - 1,
}),
(state @ State::Normal { .. }, _) | (state @ State::Wildcard { .. }, _) => {
Ok(state)
}
}
})? {
State::Normal { start } => {
if start < s.len() {
ws.push_str(&s[start..]);
}
Ok(ws)
}
State::EscapeTokenOpen { at, .. } | State::Wildcard { start: at, .. } => Err(format!(
"unclosed wildcard in string {s:?} at position {at}"
)),
State::EscapeTokenClose { at, .. } => Err(format!(
"closing a non-wildcard/bad escape in string {s:?} at position {at}"
)),
}
}
}
impl fmt::Display for WildString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
self.0
.iter()
.map(|part| match part {
WildStringPart::String(s) => s.to_owned(),
WildStringPart::Wildcard(w) => format!("{{{w}}}"),
})
.join("")
)
}
}
impl ToTokens for WildString {
fn to_tokens(&self, tokens: &mut TokenStream) {
assert!(
!self.has_wildcards(),
"cannot convert string with wildcards {self:?} to TokenStream"
);
let str = self.to_string();
tokens.append_all(quote! { #str })
}
}
#[cfg(test)]
mod tests {
use crate::typekinds::*;
use crate::wildstring::*;
#[test]
fn test_empty_string() {
let ws: WildString = "".parse().unwrap();
assert_eq!(ws.0.len(), 0);
}
#[test]
fn test_plain_string() {
let ws: WildString = "plain string".parse().unwrap();
assert_eq!(ws.0.len(), 1);
assert_eq!(
ws,
WildString(vec![WildStringPart::String("plain string".to_string())])
)
}
#[test]
fn test_escaped_curly_brackets() {
let ws: WildString = "VALUE = {{value}}".parse().unwrap();
assert_eq!(ws.to_string(), "VALUE = {value}");
assert!(!ws.has_wildcards());
}
#[test]
fn test_escaped_curly_brackets_wildcard() {
let ws: WildString = "TYPE = {{{type}}}".parse().unwrap();
assert_eq!(ws.to_string(), "TYPE = {{type}}");
assert_eq!(ws.0.len(), 4);
assert!(ws.has_wildcards());
}
#[test]
fn test_wildcard_right_boundary() {
let s = "string test {type}";
let ws: WildString = s.parse().unwrap();
assert_eq!(&ws.to_string(), s);
assert!(ws.has_wildcards());
}
#[test]
fn test_wildcard_left_boundary() {
let s = "{type} string test";
let ws: WildString = s.parse().unwrap();
assert_eq!(&ws.to_string(), s);
assert!(ws.has_wildcards());
}
#[test]
fn test_recursive_wildcard() {
let s = "string test {type[0] as {type[1]}}";
let ws: WildString = s.parse().unwrap();
assert_eq!(ws.0.len(), 2);
assert_eq!(
ws,
WildString(vec![
WildStringPart::String("string test ".to_string()),
WildStringPart::Wildcard(Wildcard::Scale(
Box::new(Wildcard::Type(Some(0))),
Box::new(TypeKind::Wildcard(Wildcard::Type(Some(1)))),
))
])
);
}
#[test]
fn test_scale_wildcard() {
let s = "string {type[0] as i8} test";
let ws: WildString = s.parse().unwrap();
assert_eq!(ws.0.len(), 3);
assert_eq!(
ws,
WildString(vec![
WildStringPart::String("string ".to_string()),
WildStringPart::Wildcard(Wildcard::Scale(
Box::new(Wildcard::Type(Some(0))),
Box::new(TypeKind::Base(BaseType::Sized(BaseTypeKind::Int, 8))),
)),
WildStringPart::String(" test".to_string())
])
);
}
#[test]
fn test_solitaire_wildcard() {
let ws: WildString = "{type}".parse().unwrap();
assert_eq!(ws.0.len(), 1);
assert_eq!(
ws,
WildString(vec![WildStringPart::Wildcard(Wildcard::Type(None))])
)
}
#[test]
fn test_empty_wildcard() {
"string {}"
.parse::<WildString>()
.expect_err("expected parse error");
}
#[test]
fn test_invalid_open_wildcard_right() {
"string {"
.parse::<WildString>()
.expect_err("expected parse error");
}
#[test]
fn test_invalid_close_wildcard_right() {
"string }"
.parse::<WildString>()
.expect_err("expected parse error");
}
#[test]
fn test_invalid_open_wildcard_left() {
"{string"
.parse::<WildString>()
.expect_err("expected parse error");
}
#[test]
fn test_invalid_close_wildcard_left() {
"}string"
.parse::<WildString>()
.expect_err("expected parse error");
}
#[test]
fn test_consecutive_wildcards() {
let s = "svprf{size_literal[1]}_gather_{type[0]}{index_or_offset}";
let ws: WildString = s.parse().unwrap();
assert_eq!(ws.to_string(), s)
}
}