use core::marker::PhantomData;
use crate as rune;
use crate::alloc::prelude::*;
use crate::alloc::{Vec, VecDeque};
use crate::ast;
use crate::ast::{LitStr, Spanned};
use crate::compile::{self, ErrorKind};
use crate::parse::{self, Parse, Resolve, ResolveContext};
pub(crate) struct Parser {
unused: VecDeque<usize>,
missed: Vec<usize>,
}
impl Parser {
pub(crate) fn new(attributes: &[ast::Attribute]) -> compile::Result<Self> {
Ok(Self {
unused: attributes
.iter()
.enumerate()
.map(|(i, _)| i)
.try_collect()?,
missed: Vec::new(),
})
}
pub(crate) fn parse_all<'this, 'a, T>(
&'this mut self,
cx: ResolveContext<'this>,
attributes: &'a [ast::Attribute],
) -> compile::Result<ParseAll<'this, 'a, T>>
where
T: Attribute + Parse,
{
for index in self.missed.drain(..) {
self.unused.try_push_back(index)?;
}
Ok(ParseAll {
outer: self,
attributes,
cx,
_marker: PhantomData,
})
}
pub(crate) fn try_parse<'a, T>(
&mut self,
cx: ResolveContext<'_>,
attributes: &'a [ast::Attribute],
) -> compile::Result<Option<(&'a ast::Attribute, T)>>
where
T: Attribute + Parse,
{
let mut vec = self.parse_all::<T>(cx, attributes)?;
let first = vec.next();
let second = vec.next();
match (first, second) {
(None, _) => Ok(None),
(Some(first), None) => Ok(Some(first?)),
(Some(first), _) => Err(compile::Error::new(
first?.0,
ErrorKind::MultipleMatchingAttributes { name: T::PATH },
)),
}
}
pub(crate) fn remaining<'a>(
&'a self,
attributes: &'a [ast::Attribute],
) -> impl Iterator<Item = &'a ast::Attribute> + 'a {
self.unused
.iter()
.chain(self.missed.iter())
.flat_map(|&n| attributes.get(n))
}
}
pub(crate) struct ParseAll<'this, 'a, T> {
outer: &'this mut Parser,
attributes: &'a [ast::Attribute],
cx: ResolveContext<'this>,
_marker: PhantomData<T>,
}
impl<'a, T> Iterator for ParseAll<'_, 'a, T>
where
T: Attribute + Parse,
{
type Item = compile::Result<(&'a ast::Attribute, T)>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let index = self.outer.unused.pop_front()?;
let Some(a) = self.attributes.get(index) else {
if let Err(error) = self.outer.missed.try_push(index) {
return Some(Err(error.into()));
}
continue;
};
let Some(ident) = a.path.try_as_ident() else {
if let Err(error) = self.outer.missed.try_push(index) {
return Some(Err(error.into()));
}
continue;
};
let ident = match ident.resolve(self.cx) {
Ok(ident) => ident,
Err(e) => {
return Some(Err(e));
}
};
if ident != T::PATH {
if let Err(error) = self.outer.missed.try_push(index) {
return Some(Err(error.into()));
}
continue;
}
let mut parser = parse::Parser::from_token_stream(&a.input, a.span());
let item = match parser.parse::<T>() {
Ok(item) => item,
Err(e) => {
return Some(Err(e));
}
};
if let Err(e) = parser.eof() {
return Some(Err(e));
}
return Some(Ok((a, item)));
}
}
}
pub(crate) trait Attribute {
const PATH: &'static str;
}
#[derive(Default)]
pub(crate) struct BuiltInArgs {
pub(crate) literal: bool,
}
#[derive(Parse)]
pub(crate) struct BuiltIn {
pub args: Option<ast::Parenthesized<ast::Ident, T![,]>>,
}
impl BuiltIn {
pub(crate) fn args(&self, cx: ResolveContext<'_>) -> compile::Result<BuiltInArgs> {
let mut out = BuiltInArgs::default();
if let Some(args) = &self.args {
for (ident, _) in args {
match ident.resolve(cx)? {
"literal" => {
out.literal = true;
}
_ => {
return Err(compile::Error::msg(ident, "unsupported attribute"));
}
}
}
}
Ok(out)
}
}
impl Attribute for BuiltIn {
const PATH: &'static str = "builtin";
}
#[derive(Parse)]
pub(crate) struct Test {}
impl Attribute for Test {
const PATH: &'static str = "test";
}
#[derive(Parse)]
pub(crate) struct Bench {}
impl Attribute for Bench {
const PATH: &'static str = "bench";
}
#[derive(Parse)]
pub(crate) struct Doc {
#[allow(dead_code)]
pub eq_token: T![=],
pub doc_string: LitStr,
}
impl Attribute for Doc {
const PATH: &'static str = "doc";
}