Skip to content

Commit

Permalink
Basic macro support.
Browse files Browse the repository at this point in the history
  • Loading branch information
gilbens-starkware committed Feb 13, 2025
1 parent 6dda66a commit 023c9fe
Show file tree
Hide file tree
Showing 19 changed files with 807 additions and 122 deletions.
1 change: 1 addition & 0 deletions corelib/src/test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod language_features {
mod const_test;
mod for_test;
mod glob_use_test;
mod macro_test;
mod panics_test;
mod trait_test;
mod while_test;
Expand Down
66 changes: 66 additions & 0 deletions corelib/src/test/language_features/macro_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
macro count_idents {
($x:ident) => {
1
};

($x:ident, $y:ident) => {
2
};

($x:ident, $y:ident, $z:ident) => {
3
};

($x:ident $y:ident $z:ident) => {
3
};

($x:ident
| $y:ident
| $z:ident) => {
3
};
}

#[test]
fn test_macro_count_idents() {
assert_eq!(count_idents!(x), 1);
assert_eq!(count_idents!(x, y), 2);
assert_eq!(count_idents!(x, y, z), 3);
assert_eq!(count_idents!(x y z), 3);
assert_eq!(count_idents!(x | y | z), 3);
}

macro add_one {
($x:ident) => {
$x + 1
};
}

#[test]
fn test_macro_add_one() {
let x1 = 1;
let x2 = 2;
let x3 = 3;
assert_eq!(add_one!(x1), 2);
assert_eq!(add_one!(x2), 3);
assert_eq!(add_one!(x3), 4);
}

mod test_assert_eq {
macro assert_eq {
($left:ident, $right:ident) => {
if $left != $right {
panic!("PANIC!");
}
};
}

#[test]
#[should_panic(expected: ("PANIC!",))]
fn test_user_defined_assert_eq() {
let x = 1;
let y = 2;
assert_eq!(x, y);
}
}
55 changes: 54 additions & 1 deletion crates/cairo-lang-defs/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ pub trait DefsGroup:
#[salsa::interned]
fn intern_extern_function(&self, id: ExternFunctionLongId) -> ExternFunctionId;
#[salsa::interned]
fn intern_macro_declaration(&self, id: MacroDeclarationLongId) -> MacroDeclarationId;
#[salsa::interned]
fn intern_param(&self, id: ParamLongId) -> ParamId;
#[salsa::interned]
fn intern_generic_param(&self, id: GenericParamLongId) -> GenericParamId;
Expand Down Expand Up @@ -244,6 +246,21 @@ pub trait DefsGroup:
&self,
extern_function_id: ExternFunctionId,
) -> Maybe<Option<ast::ItemExternFunction>>;
/// Returns the macro declarations in the module.
fn module_macro_declarations(
&self,
module_id: ModuleId,
) -> Maybe<Arc<OrderedHashMap<MacroDeclarationId, ast::ItemMacroDeclaration>>>;
/// Returns the IDs of the macro declarations in the module.
fn module_macro_declarations_ids(
&self,
module_id: ModuleId,
) -> Maybe<Arc<[MacroDeclarationId]>>;
/// Returns the macro declaration by its ID.
fn module_macro_declaration_by_id(
&self,
macro_declaration_id: MacroDeclarationId,
) -> Maybe<Option<ast::ItemMacroDeclaration>>;
fn module_ancestors(&self, module_id: ModuleId) -> OrderedHashSet<ModuleId>;
fn module_generated_file_aux_data(
&self,
Expand Down Expand Up @@ -410,6 +427,7 @@ pub struct ModuleData {
impls: Arc<OrderedHashMap<ImplDefId, ast::ItemImpl>>,
extern_types: Arc<OrderedHashMap<ExternTypeId, ast::ItemExternType>>,
extern_functions: Arc<OrderedHashMap<ExternFunctionId, ast::ItemExternFunction>>,
macro_declarations: Arc<OrderedHashMap<MacroDeclarationId, ast::ItemMacroDeclaration>>,
global_uses: Arc<OrderedHashMap<GlobalUseId, ast::UsePathStar>>,

files: Vec<FileId>,
Expand Down Expand Up @@ -477,6 +495,7 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe<ModuleData
let mut impls = OrderedHashMap::default();
let mut extern_types = OrderedHashMap::default();
let mut extern_functions = OrderedHashMap::default();
let mut macro_declarations = OrderedHashMap::default();
let mut global_uses = OrderedHashMap::default();
let mut aux_data = Vec::new();
let mut files = Vec::new();
Expand Down Expand Up @@ -571,6 +590,13 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe<ModuleData
impl_aliases.insert(item_id, impl_alias);
items.push(ModuleItemId::ImplAlias(item_id));
}
ast::ModuleItem::MacroDeclaration(macro_declaration) => {
let item_id =
MacroDeclarationLongId(module_file_id, macro_declaration.stable_ptr())
.intern(db);
macro_declarations.insert(item_id, macro_declaration);
items.push(ModuleItemId::MacroDeclaration(item_id));
}
ast::ModuleItem::InlineMacro(inline_macro_ast) => plugin_diagnostics.push((
module_file_id,
PluginDiagnostic::error(
Expand All @@ -581,7 +607,6 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe<ModuleData
),
),
)),
ast::ModuleItem::MacroDeclaration(_) => todo!(),
ast::ModuleItem::HeaderDoc(_) => {}
ast::ModuleItem::Missing(_) => {}
}
Expand All @@ -601,6 +626,7 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe<ModuleData
impls: impls.into(),
extern_types: extern_types.into(),
extern_functions: extern_functions.into(),
macro_declarations: macro_declarations.into(),
global_uses: global_uses.into(),
files,
generated_file_aux_data: aux_data,
Expand Down Expand Up @@ -1051,6 +1077,30 @@ pub fn module_extern_type_by_id(
Ok(module_extern_types.get(&extern_type_id).cloned())
}

/// Returns all the macro declarations of the given module.
pub fn module_macro_declarations(
db: &dyn DefsGroup,
module_id: ModuleId,
) -> Maybe<Arc<OrderedHashMap<MacroDeclarationId, ast::ItemMacroDeclaration>>> {
Ok(db.priv_module_data(module_id)?.macro_declarations)
}
/// Returns all the ids of the macro declarations of the given module.
pub fn module_macro_declarations_ids(
db: &dyn DefsGroup,
module_id: ModuleId,
) -> Maybe<Arc<[MacroDeclarationId]>> {
Ok(db.module_macro_declarations(module_id)?.keys().copied().collect_vec().into())
}
/// Returns the macro declaration of the given id.
pub fn module_macro_declaration_by_id(
db: &dyn DefsGroup,
macro_declaration_id: MacroDeclarationId,
) -> Maybe<Option<ast::ItemMacroDeclaration>> {
let module_macro_declarations =
db.module_macro_declarations(macro_declaration_id.module_file_id(db.upcast()).0)?;
Ok(module_macro_declarations.get(&macro_declaration_id).cloned())
}

/// Returns all the extern_functions of the given module.
pub fn module_extern_functions(
db: &dyn DefsGroup,
Expand Down Expand Up @@ -1144,5 +1194,8 @@ fn module_item_name_stable_ptr(
ModuleItemId::ExternFunction(id) => {
data.extern_functions[id].declaration(db).name(db).stable_ptr().untyped()
}
ModuleItemId::MacroDeclaration(id) => {
data.macro_declarations[id].name(db).stable_ptr().untyped()
}
})
}
12 changes: 11 additions & 1 deletion crates/cairo-lang-defs/src/ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ define_language_element_id_as_enum! {
Impl(ImplDefId),
ExternType(ExternTypeId),
ExternFunction(ExternFunctionId),
MacroDeclaration(MacroDeclarationId),
}
}
define_top_level_language_element_id!(
Expand Down Expand Up @@ -381,6 +382,14 @@ define_top_level_language_element_id!(
intern_free_function
);

define_top_level_language_element_id!(
MacroDeclarationId,
MacroDeclarationLongId,
ast::ItemMacroDeclaration,
lookup_intern_macro_declaration,
intern_macro_declaration
);

impl UnstableSalsaId for FreeFunctionId {
fn get_internal_id(&self) -> &salsa::InternId {
&self.0
Expand Down Expand Up @@ -1058,7 +1067,8 @@ impl OptionFrom<ModuleItemId> for GenericTypeId {
| ModuleItemId::FreeFunction(_)
| ModuleItemId::Trait(_)
| ModuleItemId::Impl(_)
| ModuleItemId::ExternFunction(_) => None,
| ModuleItemId::ExternFunction(_)
| ModuleItemId::MacroDeclaration(_) => None,
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/cairo-lang-doc/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ impl ToDocumentableItemId<DocumentableItemId> for ResolvedGenericItem {
ResolvedGenericItem::Impl(id) => Some(DocumentableItemId::LookupItem(
LookupItemId::ModuleItem(ModuleItemId::Impl(id)),
)),
ResolvedGenericItem::Macro(id) => Some(DocumentableItemId::LookupItem(
LookupItemId::ModuleItem(ModuleItemId::MacroDeclaration(id)),
)),
ResolvedGenericItem::GenericType(GenericTypeId::Extern(id)) => {
Some(DocumentableItemId::LookupItem(LookupItemId::ModuleItem(
ModuleItemId::ExternType(id),
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-lang-lowering/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ fn module_lowering_diagnostics(
}
ModuleItemId::ExternType(_) => {}
ModuleItemId::ExternFunction(_) => {}
ModuleItemId::MacroDeclaration(_) => {}
}
}
Ok(diagnostics.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@
test_partial_parser_tree(expect_diagnostics: false)

//! > cairo_code
macro macro_name {
() => {
2 + $placeholder
};
mod a {
mod b {
mod c {
macro macro_name {
($xxxxxxxxxxxxxxxxxxxxxxxx:ident
| $yyyyyyyyyyyyyyyyyyyyyy:ident
| $zzzzzzzzzzzzzzzzzzzzzz:ident) => {
3
};
}
}
}
}

//! > top_level_kind
Expand All @@ -19,34 +27,75 @@ macro macro_name {
//! > expected_tree
└── root (kind: SyntaxFile)
├── items (kind: ModuleItemList)
│ └── child #0 (kind: ItemMacroDeclaration)
│ └── child #0 (kind: ItemModule)
│ ├── attributes (kind: AttributeList) []
│ ├── visibility (kind: VisibilityDefault) []
│ ├── macro_kw (kind: TokenMacro): 'macro'
│ ├── name (kind: TokenIdentifier): 'macro_name'
│ ├── lbrace (kind: TokenLBrace): '{'
│ ├── rules (kind: MacroRulesList)
│ │ └── child #0 (kind: MacroRule)
│ │ ├── lhs (kind: ParenthesizedMacroMatcher)
│ │ │ ├── lparen (kind: TokenLParen): '('
│ │ │ ├── elements (kind: MacroRuleElements) []
│ │ │ └── rparen (kind: TokenRParen): ')'
│ │ ├── fat_arrow (kind: TokenMatchArrow): '=>'
│ │ ├── rhs (kind: ExprBlock)
│ │ │ ├── lbrace (kind: TokenLBrace): '{'
│ │ │ ├── statements (kind: StatementList)
│ │ │ │ └── child #0 (kind: StatementExpr)
│ │ │ │ ├── attributes (kind: AttributeList) []
│ │ │ │ ├── expr (kind: ExprBinary)
│ │ │ │ │ ├── lhs (kind: TokenLiteralNumber): '2'
│ │ │ │ │ ├── op (kind: TokenPlus): '+'
│ │ │ │ │ └── rhs (kind: ExprPlaceholder)
│ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
│ │ │ │ │ └── name (kind: TokenIdentifier): 'placeholder'
│ │ │ │ └── semicolon (kind: OptionTerminalSemicolonEmpty) []
│ │ │ └── rbrace (kind: TokenRBrace): '}'
│ │ └── semicolon (kind: TokenSemicolon): ';'
│ └── rbrace (kind: TokenRBrace): '}'
│ ├── module_kw (kind: TokenModule): 'mod'
│ ├── name (kind: TokenIdentifier): 'a'
│ └── body (kind: ModuleBody)
│ ├── lbrace (kind: TokenLBrace): '{'
│ ├── items (kind: ModuleItemList)
│ │ └── child #0 (kind: ItemModule)
│ │ ├── attributes (kind: AttributeList) []
│ │ ├── visibility (kind: VisibilityDefault) []
│ │ ├── module_kw (kind: TokenModule): 'mod'
│ │ ├── name (kind: TokenIdentifier): 'b'
│ │ └── body (kind: ModuleBody)
│ │ ├── lbrace (kind: TokenLBrace): '{'
│ │ ├── items (kind: ModuleItemList)
│ │ │ └── child #0 (kind: ItemModule)
│ │ │ ├── attributes (kind: AttributeList) []
│ │ │ ├── visibility (kind: VisibilityDefault) []
│ │ │ ├── module_kw (kind: TokenModule): 'mod'
│ │ │ ├── name (kind: TokenIdentifier): 'c'
│ │ │ └── body (kind: ModuleBody)
│ │ │ ├── lbrace (kind: TokenLBrace): '{'
│ │ │ ├── items (kind: ModuleItemList)
│ │ │ │ └── child #0 (kind: ItemMacroDeclaration)
│ │ │ │ ├── attributes (kind: AttributeList) []
│ │ │ │ ├── visibility (kind: VisibilityDefault) []
│ │ │ │ ├── macro_kw (kind: TokenMacro): 'macro'
│ │ │ │ ├── name (kind: TokenIdentifier): 'macro_name'
│ │ │ │ ├── lbrace (kind: TokenLBrace): '{'
│ │ │ │ ├── rules (kind: MacroRulesList)
│ │ │ │ │ └── child #0 (kind: MacroRule)
│ │ │ │ │ ├── lhs (kind: ParenthesizedMacroMatcher)
│ │ │ │ │ │ ├── lparen (kind: TokenLParen): '('
│ │ │ │ │ │ ├── elements (kind: MacroRuleElements)
│ │ │ │ │ │ │ ├── child #0 (kind: MacroRuleParam)
│ │ │ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
│ │ │ │ │ │ │ │ ├── name (kind: TokenIdentifier): 'xxxxxxxxxxxxxxxxxxxxxxxx'
│ │ │ │ │ │ │ │ ├── colon (kind: TokenColon): ':'
│ │ │ │ │ │ │ │ └── kind (kind: TokenIdentifier): 'ident'
│ │ │ │ │ │ │ ├── child #1 (kind: TokenTreeLeaf)
│ │ │ │ │ │ │ │ └── leaf (kind: TokenOr): '|'
│ │ │ │ │ │ │ ├── child #2 (kind: MacroRuleParam)
│ │ │ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
│ │ │ │ │ │ │ │ ├── name (kind: TokenIdentifier): 'yyyyyyyyyyyyyyyyyyyyyy'
│ │ │ │ │ │ │ │ ├── colon (kind: TokenColon): ':'
│ │ │ │ │ │ │ │ └── kind (kind: TokenIdentifier): 'ident'
│ │ │ │ │ │ │ ├── child #3 (kind: TokenTreeLeaf)
│ │ │ │ │ │ │ │ └── leaf (kind: TokenOr): '|'
│ │ │ │ │ │ │ └── child #4 (kind: MacroRuleParam)
│ │ │ │ │ │ │ ├── dollar (kind: TokenDollar): '$'
│ │ │ │ │ │ │ ├── name (kind: TokenIdentifier): 'zzzzzzzzzzzzzzzzzzzzzz'
│ │ │ │ │ │ │ ├── colon (kind: TokenColon): ':'
│ │ │ │ │ │ │ └── kind (kind: TokenIdentifier): 'ident'
│ │ │ │ │ │ └── rparen (kind: TokenRParen): ')'
│ │ │ │ │ ├── fat_arrow (kind: TokenMatchArrow): '=>'
│ │ │ │ │ ├── rhs (kind: ExprBlock)
│ │ │ │ │ │ ├── lbrace (kind: TokenLBrace): '{'
│ │ │ │ │ │ ├── statements (kind: StatementList)
│ │ │ │ │ │ │ └── child #0 (kind: StatementExpr)
│ │ │ │ │ │ │ ├── attributes (kind: AttributeList) []
│ │ │ │ │ │ │ ├── expr (kind: TokenLiteralNumber): '3'
│ │ │ │ │ │ │ └── semicolon (kind: OptionTerminalSemicolonEmpty) []
│ │ │ │ │ │ └── rbrace (kind: TokenRBrace): '}'
│ │ │ │ │ └── semicolon (kind: TokenSemicolon): ';'
│ │ │ │ └── rbrace (kind: TokenRBrace): '}'
│ │ │ └── rbrace (kind: TokenRBrace): '}'
│ │ └── rbrace (kind: TokenRBrace): '}'
│ └── rbrace (kind: TokenRBrace): '}'
└── eof (kind: TokenEndOfFile).

//! > ==========================================================================
Expand Down
Loading

0 comments on commit 023c9fe

Please sign in to comment.