-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement procedure annotation syntax
This commit introduces the concept of attributes/annotations to Miden Assembly procedures. These can be used to represent interesting/useful bits of metadata associated with specific procedures in three different forms: * Marker attributes, e.g. `@inline`, a name with no associated data * List attributes, e.g. `@inline(always)`, i.e. a parameterized marker; multiple values can be provided as comma-delimited metadata expressions. * Key-value attributes, e.g. `@props(<key> = <value>, ...)`, where `<key>` must be a valid identifier, and `<value>` must be a valid metadata expression. Multiple key-value attributes can be set at once, or you can specify the same key-value attribute multiple times, so long as each instance does not have any keys that conflict with previous instances. Metadata expressions come in three possible types: * bare identifier, e.g. `foo` * quoted string, e.g. `"some text"` * integer value, either decimal or hexadecimal format, e.g. `1` or `0x01` Attributes will provide the foundation for upcoming changes that will rely on being able to attach metadata to procedures. For now, attributes may _only_ be attached to procedure definitions, not re-exports or any other syntactic construct. NOTE: This does not yet act on any attributes, nor store them anywhere when assembling to MAST. For now, they are simply parsed, made available in the AST, and ignored. Future PRs will introduce these as needed. Closes #1434
- Loading branch information
Showing
16 changed files
with
1,499 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
mod expr; | ||
mod kv; | ||
mod list; | ||
|
||
use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec}; | ||
use core::fmt; | ||
|
||
pub use self::{expr::MetaExpr, kv::MetaKeyValue, list::MetaList}; | ||
use crate::{ast::Ident, parser::HexEncodedValue, Felt, SourceSpan, Span}; | ||
|
||
/// Represents the metadata provided as arguments to an attribute. | ||
#[derive(Clone, PartialEq, Eq)] | ||
pub enum Meta { | ||
/// Represents empty metadata, e.g. `@foo` | ||
Unit, | ||
/// A list of metadata expressions, e.g. `@foo(a, "some text", 0x01)` | ||
/// | ||
/// The list should always have at least one element, and this is guaranteed by the parser. | ||
List(Vec<MetaExpr>), | ||
/// A set of uniquely-named metadata expressions, e.g. `@foo(letter = a, text = "some text")` | ||
/// | ||
/// The set should always have at least one key-value pair, and this is guaranteed by the | ||
/// parser. | ||
KeyValue(BTreeMap<Ident, MetaExpr>), | ||
} | ||
impl Meta { | ||
/// Borrow the metadata without unwrapping the specific type | ||
/// | ||
/// Returns `None` if there is no meaningful metadata | ||
#[inline] | ||
pub fn borrow(&self) -> Option<BorrowedMeta<'_>> { | ||
match self { | ||
Self::Unit => None, | ||
Self::List(ref list) => Some(BorrowedMeta::List(list)), | ||
Self::KeyValue(ref kv) => Some(BorrowedMeta::KeyValue(kv)), | ||
} | ||
} | ||
} | ||
impl FromIterator<MetaItem> for Meta { | ||
#[inline] | ||
fn from_iter<T: IntoIterator<Item = MetaItem>>(iter: T) -> Self { | ||
let mut iter = iter.into_iter(); | ||
match iter.next() { | ||
None => Self::Unit, | ||
Some(MetaItem::Expr(expr)) => Self::List( | ||
core::iter::once(expr) | ||
.chain(iter.map(|item| match item { | ||
MetaItem::Expr(expr) => expr, | ||
MetaItem::KeyValue(..) => unsafe { core::hint::unreachable_unchecked() }, | ||
})) | ||
.collect(), | ||
), | ||
Some(MetaItem::KeyValue(k, v)) => Self::KeyValue( | ||
core::iter::once((k, v)) | ||
.chain(iter.map(|item| match item { | ||
MetaItem::KeyValue(k, v) => (k, v), | ||
MetaItem::Expr(_) => unsafe { core::hint::unreachable_unchecked() }, | ||
})) | ||
.collect(), | ||
), | ||
} | ||
} | ||
} | ||
|
||
impl FromIterator<MetaExpr> for Meta { | ||
#[inline] | ||
fn from_iter<T: IntoIterator<Item = MetaExpr>>(iter: T) -> Self { | ||
Self::List(iter.into_iter().collect()) | ||
} | ||
} | ||
|
||
impl FromIterator<(Ident, MetaExpr)> for Meta { | ||
#[inline] | ||
fn from_iter<T: IntoIterator<Item = (Ident, MetaExpr)>>(iter: T) -> Self { | ||
Self::KeyValue(iter.into_iter().collect()) | ||
} | ||
} | ||
|
||
impl<'a> FromIterator<(&'a str, MetaExpr)> for Meta { | ||
#[inline] | ||
fn from_iter<T>(iter: T) -> Self | ||
where | ||
T: IntoIterator<Item = (&'a str, MetaExpr)>, | ||
{ | ||
Self::KeyValue( | ||
iter.into_iter() | ||
.map(|(k, v)| { | ||
let k = Ident::new_unchecked(Span::new(SourceSpan::UNKNOWN, Arc::from(k))); | ||
(k, v) | ||
}) | ||
.collect(), | ||
) | ||
} | ||
} | ||
|
||
impl<I, V> From<I> for Meta | ||
where | ||
Meta: FromIterator<V>, | ||
I: IntoIterator<Item = V>, | ||
{ | ||
#[inline] | ||
fn from(iter: I) -> Self { | ||
Self::from_iter(iter) | ||
} | ||
} | ||
|
||
/// Represents a reference to the metadata for an [super::Attribute] | ||
/// | ||
/// See [Meta] for what metadata is represented, and its syntax. | ||
#[derive(Copy, Clone, PartialEq, Eq)] | ||
pub enum BorrowedMeta<'a> { | ||
/// A list of metadata expressions | ||
List(&'a [MetaExpr]), | ||
/// A list of uniquely-named metadata expressions | ||
KeyValue(&'a BTreeMap<Ident, MetaExpr>), | ||
} | ||
impl fmt::Debug for BorrowedMeta<'_> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::List(items) => write!(f, "{items:#?}"), | ||
Self::KeyValue(items) => write!(f, "{items:#?}"), | ||
} | ||
} | ||
} | ||
|
||
/// Represents a single metadata item provided as an argument to an attribute. | ||
/// | ||
/// For example, the `foo` attribute in `@foo(bar, baz)` has two metadata items, both of `Expr` | ||
/// type, which compose a ` | ||
#[derive(Clone, PartialEq, Eq)] | ||
pub enum MetaItem { | ||
/// A metadata expression, e.g. `"some text"` in `@foo("some text")` | ||
/// | ||
/// This represents the element type for `Meta::List`-based attributes. | ||
Expr(MetaExpr), | ||
/// A named metadata expression, e.g. `letter = a` in `@foo(letter = a)` | ||
/// | ||
/// This represents the element type for `Meta::KeyValue`-based attributes. | ||
KeyValue(Ident, MetaExpr), | ||
} | ||
|
||
impl MetaItem { | ||
/// Unwrap this item to extract the contained [MetaExpr]. | ||
/// | ||
/// Panics if this item is not the `Expr` variant. | ||
#[inline] | ||
#[track_caller] | ||
pub fn unwrap_expr(self) -> MetaExpr { | ||
match self { | ||
Self::Expr(expr) => expr, | ||
Self::KeyValue(..) => unreachable!("tried to unwrap key-value as expression"), | ||
} | ||
} | ||
|
||
/// Unwrap this item to extract the contained key-value pair. | ||
/// | ||
/// Panics if this item is not the `KeyValue` variant. | ||
#[inline] | ||
#[track_caller] | ||
pub fn unwrap_key_value(self) -> (Ident, MetaExpr) { | ||
match self { | ||
Self::KeyValue(k, v) => (k, v), | ||
Self::Expr(_) => unreachable!("tried to unwrap expression as key-value"), | ||
} | ||
} | ||
} | ||
|
||
impl From<Ident> for MetaItem { | ||
fn from(value: Ident) -> Self { | ||
Self::Expr(MetaExpr::Ident(value)) | ||
} | ||
} | ||
|
||
impl From<&str> for MetaItem { | ||
fn from(value: &str) -> Self { | ||
Self::Expr(MetaExpr::String(Ident::new_unchecked(Span::new( | ||
SourceSpan::UNKNOWN, | ||
Arc::from(value), | ||
)))) | ||
} | ||
} | ||
|
||
impl From<String> for MetaItem { | ||
fn from(value: String) -> Self { | ||
Self::Expr(MetaExpr::String(Ident::new_unchecked(Span::new( | ||
SourceSpan::UNKNOWN, | ||
Arc::from(value.into_boxed_str()), | ||
)))) | ||
} | ||
} | ||
|
||
impl From<u8> for MetaItem { | ||
fn from(value: u8) -> Self { | ||
Self::Expr(MetaExpr::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::U8(value)))) | ||
} | ||
} | ||
|
||
impl From<u16> for MetaItem { | ||
fn from(value: u16) -> Self { | ||
Self::Expr(MetaExpr::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::U16(value)))) | ||
} | ||
} | ||
|
||
impl From<u32> for MetaItem { | ||
fn from(value: u32) -> Self { | ||
Self::Expr(MetaExpr::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::U32(value)))) | ||
} | ||
} | ||
|
||
impl From<Felt> for MetaItem { | ||
fn from(value: Felt) -> Self { | ||
Self::Expr(MetaExpr::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::Felt(value)))) | ||
} | ||
} | ||
|
||
impl From<[Felt; 4]> for MetaItem { | ||
fn from(value: [Felt; 4]) -> Self { | ||
Self::Expr(MetaExpr::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::Word(value)))) | ||
} | ||
} | ||
|
||
impl<V> From<(Ident, V)> for MetaItem | ||
where | ||
V: Into<MetaExpr>, | ||
{ | ||
fn from(entry: (Ident, V)) -> Self { | ||
let (key, value) = entry; | ||
Self::KeyValue(key, value.into()) | ||
} | ||
} | ||
|
||
impl<V> From<(&str, V)> for MetaItem | ||
where | ||
V: Into<MetaExpr>, | ||
{ | ||
fn from(entry: (&str, V)) -> Self { | ||
let (key, value) = entry; | ||
let key = Ident::new_unchecked(Span::new(SourceSpan::UNKNOWN, Arc::from(key))); | ||
Self::KeyValue(key, value.into()) | ||
} | ||
} | ||
|
||
impl<V> From<(String, V)> for MetaItem | ||
where | ||
V: Into<MetaExpr>, | ||
{ | ||
fn from(entry: (String, V)) -> Self { | ||
let (key, value) = entry; | ||
let key = | ||
Ident::new_unchecked(Span::new(SourceSpan::UNKNOWN, Arc::from(key.into_boxed_str()))); | ||
Self::KeyValue(key, value.into()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
use alloc::{string::String, sync::Arc}; | ||
|
||
use crate::{ast::Ident, parser::HexEncodedValue, prettier, Felt, SourceSpan, Span, Spanned}; | ||
|
||
/// Represents a metadata expression of an [crate::ast::Attribute] | ||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub enum MetaExpr { | ||
/// An identifier/keyword, e.g. `inline` | ||
Ident(Ident), | ||
/// A decimal or hexadecimal integer value | ||
Int(Span<HexEncodedValue>), | ||
/// A quoted string or identifier | ||
String(Ident), | ||
} | ||
|
||
impl prettier::PrettyPrint for MetaExpr { | ||
fn render(&self) -> prettier::Document { | ||
use prettier::*; | ||
|
||
match self { | ||
Self::Ident(id) => text(id), | ||
Self::Int(value) => text(value), | ||
Self::String(id) => text(format!("\"{}\"", id.as_str().escape_default())), | ||
} | ||
} | ||
} | ||
|
||
impl From<Ident> for MetaExpr { | ||
fn from(value: Ident) -> Self { | ||
Self::Ident(value) | ||
} | ||
} | ||
|
||
impl From<&str> for MetaExpr { | ||
fn from(value: &str) -> Self { | ||
Self::String(Ident::new_unchecked(Span::new(SourceSpan::UNKNOWN, Arc::from(value)))) | ||
} | ||
} | ||
|
||
impl From<String> for MetaExpr { | ||
fn from(value: String) -> Self { | ||
Self::String(Ident::new_unchecked(Span::new( | ||
SourceSpan::UNKNOWN, | ||
Arc::from(value.into_boxed_str()), | ||
))) | ||
} | ||
} | ||
|
||
impl From<u8> for MetaExpr { | ||
fn from(value: u8) -> Self { | ||
Self::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::U8(value))) | ||
} | ||
} | ||
|
||
impl From<u16> for MetaExpr { | ||
fn from(value: u16) -> Self { | ||
Self::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::U16(value))) | ||
} | ||
} | ||
|
||
impl From<u32> for MetaExpr { | ||
fn from(value: u32) -> Self { | ||
Self::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::U32(value))) | ||
} | ||
} | ||
|
||
impl From<Felt> for MetaExpr { | ||
fn from(value: Felt) -> Self { | ||
Self::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::Felt(value))) | ||
} | ||
} | ||
|
||
impl From<[Felt; 4]> for MetaExpr { | ||
fn from(value: [Felt; 4]) -> Self { | ||
Self::Int(Span::new(SourceSpan::UNKNOWN, HexEncodedValue::Word(value))) | ||
} | ||
} | ||
|
||
impl Spanned for MetaExpr { | ||
fn span(&self) -> SourceSpan { | ||
match self { | ||
Self::Ident(spanned) | Self::String(spanned) => spanned.span(), | ||
Self::Int(spanned) => spanned.span(), | ||
} | ||
} | ||
} |
Oops, something went wrong.