ast: Add metadata attributes on Items
TODO: decide what other places attributes belong
This commit is contained in:
		@@ -8,9 +8,13 @@ Visibility  = "pub"? ;
 | 
			
		||||
File        = Item* EOI ;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Item        = Visibility ItemKind ;
 | 
			
		||||
Attrs       = ('#' '[' (Meta ',') Meta? ']')* ;
 | 
			
		||||
Meta        = Identifier ('=' Literal | '(' (Literal ',')* Literal? ')')? ;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Item        = Attrs* Visibility ItemKind ;
 | 
			
		||||
ItemKind    = Const    | Static | Module 
 | 
			
		||||
                | Function | Struct | Enum 
 | 
			
		||||
            | Function | Struct | Enum 
 | 
			
		||||
            | Alias    | Impl ;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,11 +32,31 @@ pub struct File {
 | 
			
		||||
    pub items: Vec<Item>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Metadata decorators
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct Attrs {
 | 
			
		||||
    pub meta: Vec<Meta>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct Meta {
 | 
			
		||||
    pub name: Identifier,
 | 
			
		||||
    pub kind: MetaKind,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum MetaKind {
 | 
			
		||||
    Plain,
 | 
			
		||||
    Equals(Literal),
 | 
			
		||||
    Func(Vec<Literal>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Items
 | 
			
		||||
/// Stores an [ItemKind] and associated metadata
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct Item {
 | 
			
		||||
    pub extents: Span,
 | 
			
		||||
    pub attrs: Attrs,
 | 
			
		||||
    pub vis: Visibility,
 | 
			
		||||
    pub kind: ItemKind,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -77,10 +77,39 @@ mod display {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Display for Attrs {
 | 
			
		||||
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
            let Self { meta } = self;
 | 
			
		||||
            if meta.is_empty() {
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            }
 | 
			
		||||
            "#".fmt(f)?;
 | 
			
		||||
            delimit(separate(meta, ", "), INLINE_SQUARE)(f)?;
 | 
			
		||||
            "\n".fmt(f)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    impl Display for Meta {
 | 
			
		||||
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
            let Self { name, kind } = self;
 | 
			
		||||
            write!(f, "{name}{kind}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    impl Display for MetaKind {
 | 
			
		||||
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
            match self {
 | 
			
		||||
                MetaKind::Plain => Ok(()),
 | 
			
		||||
                MetaKind::Equals(v) => write!(f, " = {v}"),
 | 
			
		||||
                MetaKind::Func(args) => delimit(separate(args, ", "), INLINE_PARENS)(f),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Display for Item {
 | 
			
		||||
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
            self.vis.fmt(f)?;
 | 
			
		||||
            match &self.kind {
 | 
			
		||||
            let Self { extents: _, attrs, vis, kind } = self;
 | 
			
		||||
            attrs.fmt(f)?;
 | 
			
		||||
            vis.fmt(f)?;
 | 
			
		||||
            match kind {
 | 
			
		||||
                ItemKind::Alias(v) => v.fmt(f),
 | 
			
		||||
                ItemKind::Const(v) => v.fmt(f),
 | 
			
		||||
                ItemKind::Static(v) => v.fmt(f),
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,9 @@ pub mod error {
 | 
			
		||||
    pub enum Parsing {
 | 
			
		||||
        File,
 | 
			
		||||
 | 
			
		||||
        Attrs,
 | 
			
		||||
        Meta,
 | 
			
		||||
 | 
			
		||||
        Item,
 | 
			
		||||
        Visibility,
 | 
			
		||||
        Mutability,
 | 
			
		||||
@@ -161,6 +164,10 @@ pub mod error {
 | 
			
		||||
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
            match self {
 | 
			
		||||
                Parsing::File => "a file",
 | 
			
		||||
 | 
			
		||||
                Parsing::Attrs => "an attribute-set",
 | 
			
		||||
                Parsing::Meta => "an attribute",
 | 
			
		||||
 | 
			
		||||
                Parsing::Item => "an item",
 | 
			
		||||
                Parsing::Visibility => "a visibility qualifier",
 | 
			
		||||
                Parsing::Mutability => "a mutability qualifier",
 | 
			
		||||
@@ -371,7 +378,8 @@ fn rep<'t, T>(
 | 
			
		||||
 | 
			
		||||
/// Expands to a pattern which matches item-like [Token] [Type]s
 | 
			
		||||
macro item_like() {
 | 
			
		||||
    Type::Keyword(
 | 
			
		||||
    Type::Hash
 | 
			
		||||
        | Type::Keyword(
 | 
			
		||||
        Keyword::Pub
 | 
			
		||||
            | Keyword::Type
 | 
			
		||||
            | Keyword::Const
 | 
			
		||||
@@ -406,6 +414,7 @@ impl<'t> Parser<'t> {
 | 
			
		||||
        let start = self.loc();
 | 
			
		||||
        Ok(Item {
 | 
			
		||||
            vis: self.visibility()?,
 | 
			
		||||
            attrs: self.attributes()?,
 | 
			
		||||
            kind: self.itemkind()?,
 | 
			
		||||
            extents: Span(start, self.loc()),
 | 
			
		||||
        })
 | 
			
		||||
@@ -464,6 +473,41 @@ impl<'t> Parser<'t> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Attribute parsing
 | 
			
		||||
impl<'t> Parser<'t> {
 | 
			
		||||
    /// Parses an [attribute set](Attrs)
 | 
			
		||||
    pub fn attributes(&mut self) -> PResult<Attrs> {
 | 
			
		||||
        if self.match_type(Type::Hash, Parsing::Attrs).is_err() {
 | 
			
		||||
            return Ok(Attrs { meta: vec![] });
 | 
			
		||||
        }
 | 
			
		||||
        let meta = delim(
 | 
			
		||||
            sep(Self::meta, Type::Comma, BRACKETS.1, Parsing::Attrs),
 | 
			
		||||
            BRACKETS,
 | 
			
		||||
            Parsing::Attrs,
 | 
			
		||||
        );
 | 
			
		||||
        Ok(Attrs { meta: meta(self)? })
 | 
			
		||||
    }
 | 
			
		||||
    pub fn meta(&mut self) -> PResult<Meta> {
 | 
			
		||||
        Ok(Meta { name: self.identifier()?, kind: self.meta_kind()? })
 | 
			
		||||
    }
 | 
			
		||||
    pub fn meta_kind(&mut self) -> PResult<MetaKind> {
 | 
			
		||||
        const PARSING: Parsing = Parsing::Meta;
 | 
			
		||||
        let lit_tuple = delim(
 | 
			
		||||
            sep(Self::literal, Type::Comma, PARENS.1, PARSING),
 | 
			
		||||
            PARENS,
 | 
			
		||||
            PARSING,
 | 
			
		||||
        );
 | 
			
		||||
        Ok(match self.peek_type(PARSING) {
 | 
			
		||||
            Ok(Type::Eq) => {
 | 
			
		||||
                self.consume_peeked();
 | 
			
		||||
                MetaKind::Equals(self.literal()?)
 | 
			
		||||
            }
 | 
			
		||||
            Ok(Type::LParen) => MetaKind::Func(lit_tuple(self)?),
 | 
			
		||||
            _ => MetaKind::Plain,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Item parsing
 | 
			
		||||
impl<'t> Parser<'t> {
 | 
			
		||||
    /// Parses an [ItemKind]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user