Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement procedure annotation syntax #1510

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 0.11.0 (TBD)

#### Enhancements

- Added support for procedure annotation (attribute) syntax to Miden Assembly

#### Changes

- [BREAKING] Wrapped `MastForest`s in `Program` and `Library` structs in `Arc` (#1465).
Expand Down
253 changes: 253 additions & 0 deletions assembly/src/ast/attribute/meta.rs
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())
}
}
86 changes: 86 additions & 0 deletions assembly/src/ast/attribute/meta/expr.rs
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(),
}
}
}
Loading
Loading