From 9b4163450c643ebcdfe7861e207f25f935fa64ce Mon Sep 17 00:00:00 2001 From: maciektr Date: Fri, 19 Jan 2024 16:23:08 +0100 Subject: [PATCH] Implement macro host generate_code, find proc_macro attributes commit-id:e59370d2 --- scarb/src/compiler/plugin/proc_macro_host.rs | 157 ++++++++++++++++++- 1 file changed, 151 insertions(+), 6 deletions(-) diff --git a/scarb/src/compiler/plugin/proc_macro_host.rs b/scarb/src/compiler/plugin/proc_macro_host.rs index e473317b0..50bb9cd2b 100644 --- a/scarb/src/compiler/plugin/proc_macro_host.rs +++ b/scarb/src/compiler/plugin/proc_macro_host.rs @@ -2,15 +2,47 @@ use crate::compiler::plugin::{CairoPlugin, CairoPluginInstance}; use crate::core::{Package, PackageId, PackageName, SourceId}; use crate::internal::to_version::ToVersion; use anyhow::Result; -use cairo_lang_defs::plugin::{MacroPlugin, MacroPluginMetadata, PluginResult}; +use cairo_lang_defs::patcher::PatchBuilder; +use cairo_lang_defs::plugin::{ + MacroPlugin, MacroPluginMetadata, PluginGeneratedFile, PluginResult, +}; use cairo_lang_semantic::plugin::PluginSuite; -use cairo_lang_syntax::node::ast::ModuleItem; +use cairo_lang_syntax::attribute::structured::AttributeListStructurize; use cairo_lang_syntax::node::db::SyntaxGroup; +use cairo_lang_syntax::node::{ast, TypedSyntaxNode}; +use itertools::Itertools; use smol_str::SmolStr; use std::collections::HashMap; use std::sync::Arc; use typed_builder::TypedBuilder; +#[derive(Debug, Default, Clone)] +pub struct TokenStream(String); + +impl TokenStream { + pub fn from_item_ast(db: &dyn SyntaxGroup, item_ast: ast::ModuleItem) -> Self { + let mut builder = PatchBuilder::new(db); + builder.add_node(item_ast.as_syntax_node()); + let cairo = builder.code.clone(); + Self(cairo) + } + + pub fn collect(self) -> String { + self.0 + } +} + +#[derive(Debug)] +#[allow(dead_code)] +pub enum ProcMacroResult { + /// Plugin has not taken any action. + Leave, + /// Plugin generated TokenStream replacement. + Replace(TokenStream), + /// Plugin ordered item removal. + Remove, +} + #[derive(Debug, Clone)] pub struct ProcMacroInstance {} @@ -20,6 +52,12 @@ impl ProcMacroInstance { // TODO(maciektr): Implement Self {} } + + fn generate_code(&self, _token_stream: TokenStream) -> ProcMacroResult { + // Apply expansion to token stream. + // TODO(maciektr): Implement + ProcMacroResult::Leave + } } #[derive(Debug, TypedBuilder)] @@ -27,16 +65,123 @@ pub struct ProcMacroHost { macros: HashMap>, } +pub type ProcMacroId = SmolStr; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum ProcMacroKind { + /// `proc_macro_name!(...)` + MacroCall, + /// `#[proc_macro_name]` + Attribute, + /// `#[derive(...)]` + Derive, +} + +#[derive(Debug)] +pub struct ProcMacroInput { + pub id: ProcMacroId, + pub kind: ProcMacroKind, +} + +impl ProcMacroHost { + /// Handle `proc_macro_name!` expression. + fn handle_macro( + &self, + _db: &dyn SyntaxGroup, + _item_ast: ast::ModuleItem, + ) -> Vec { + // Todo(maciektr): Implement. + Vec::new() + } + + /// Handle `#[proc_macro_name]` attribute. + fn handle_attribute( + &self, + db: &dyn SyntaxGroup, + item_ast: ast::ModuleItem, + ) -> Vec { + let attrs = match item_ast { + ast::ModuleItem::Struct(struct_ast) => Some(struct_ast.attributes(db)), + ast::ModuleItem::Enum(enum_ast) => Some(enum_ast.attributes(db)), + ast::ModuleItem::ExternType(extern_type_ast) => Some(extern_type_ast.attributes(db)), + ast::ModuleItem::ExternFunction(extern_func_ast) => { + Some(extern_func_ast.attributes(db)) + } + ast::ModuleItem::FreeFunction(free_func_ast) => Some(free_func_ast.attributes(db)), + _ => None, + }; + + attrs + .map(|attrs| attrs.structurize(db)) + .unwrap_or_default() + .iter() + .filter(|attr| self.macros.contains_key(&attr.id)) + .map(|attr| ProcMacroInput { + id: attr.id.clone(), + kind: ProcMacroKind::Attribute, + }) + .collect_vec() + } + + /// Handle `#[derive(...)]` attribute. + fn handle_derive( + &self, + _db: &dyn SyntaxGroup, + _item_ast: ast::ModuleItem, + ) -> Vec { + // Todo(maciektr): Implement. + Vec::new() + } +} + impl MacroPlugin for ProcMacroHost { fn generate_code( &self, - _db: &dyn SyntaxGroup, - _item_ast: ModuleItem, + db: &dyn SyntaxGroup, + item_ast: ast::ModuleItem, _metadata: &MacroPluginMetadata<'_>, ) -> PluginResult { // Apply expansion to `item_ast` where needed. - // TODO(maciektr): Implement - PluginResult::default() + let expansions = self + .handle_macro(db, item_ast.clone()) + .into_iter() + .chain(self.handle_attribute(db, item_ast.clone())) + .chain(self.handle_derive(db, item_ast.clone())); + + let mut token_stream = TokenStream::from_item_ast(db, item_ast); + let mut modified = false; + for input in expansions { + let instance = self.macros.get(&input.id).unwrap(); + match instance.generate_code(token_stream.clone()) { + ProcMacroResult::Replace(new_token_stream) => { + token_stream = new_token_stream; + modified = true; + } + ProcMacroResult::Remove => { + return PluginResult { + code: None, + diagnostics: Vec::new(), + remove_original_item: true, + } + } + ProcMacroResult::Leave => {} + }; + } + if modified { + PluginResult { + code: Some(PluginGeneratedFile { + name: "proc_macro".into(), + content: token_stream.collect(), + code_mappings: Default::default(), + aux_data: Default::default(), + }), + diagnostics: Vec::new(), + remove_original_item: true, + } + } else { + PluginResult::default() + } } fn declared_attributes(&self) -> Vec {