Skip to content

Commit

Permalink
Merge pull request #1510 from 0xPolygonMiden/bitwalker/annotations
Browse files Browse the repository at this point in the history
feat: implement procedure annotation syntax
  • Loading branch information
bitwalker authored Sep 27, 2024
2 parents 6f60f74 + 8762dae commit f2b9161
Show file tree
Hide file tree
Showing 16 changed files with 1,499 additions and 26 deletions.
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

0 comments on commit f2b9161

Please sign in to comment.