Skip to content

Commit

Permalink
Implement macro host generate_code, find proc_macro attributes
Browse files Browse the repository at this point in the history
commit-id:e59370d2
  • Loading branch information
maciektr committed Feb 6, 2024
1 parent 4c1e194 commit 727a0d9
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 17 deletions.
36 changes: 36 additions & 0 deletions scarb/src/compiler/plugin/proc_macro/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::core::Package;
use cairo_lang_defs::patcher::PatchBuilder;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{ast, TypedSyntaxNode};
use scarb_macro_interface::plugin::{ProcMacroResult, TokenStream};
use std::fmt::Debug;

pub trait FromItemAst {
fn from_item_ast(db: &dyn SyntaxGroup, item_ast: ast::ModuleItem) -> Self;
}

impl FromItemAst for TokenStream {
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::from(cairo)
}
}

#[derive(Debug, Clone)]
pub struct ProcMacroInstance {}

impl ProcMacroInstance {
pub fn new(_package: Package) -> Self {
// Load shared library
// TODO(maciektr): Implement
Self {}
}

pub(crate) fn generate_code(&self, _token_stream: TokenStream) -> ProcMacroResult {
// Apply expansion to token stream.
// TODO(maciektr): Implement
ProcMacroResult::Leave
}
}
136 changes: 119 additions & 17 deletions scarb/src/compiler/plugin/proc_macro/host.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,144 @@
use crate::compiler::plugin::proc_macro::{FromItemAst, ProcMacroInstance};
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::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::ast;
use cairo_lang_syntax::node::db::SyntaxGroup;
use itertools::Itertools;
use scarb_macro_interface::plugin::{ProcMacroResult, TokenStream};
use smol_str::SmolStr;
use std::collections::HashMap;
use std::sync::Arc;
use typed_builder::TypedBuilder;

#[derive(Debug, Clone)]
pub struct ProcMacroInstance {}

impl ProcMacroInstance {
pub fn new(_package: Package) -> Self {
// Load shared library
// TODO(maciektr): Implement
Self {}
}
}

#[derive(Debug, TypedBuilder)]
pub struct ProcMacroHost {
macros: HashMap<SmolStr, Box<ProcMacroInstance>>,
}

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<ProcMacroInput> {
// Todo(maciektr): Implement.
Vec::new()
}

/// Handle `#[proc_macro_name]` attribute.
fn handle_attribute(
&self,
db: &dyn SyntaxGroup,
item_ast: ast::ModuleItem,
) -> Vec<ProcMacroInput> {
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<ProcMacroInput> {
// 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.to_string(),
code_mappings: Default::default(),
aux_data: Default::default(),
}),
diagnostics: Vec::new(),
remove_original_item: true,
}
} else {
PluginResult::default()
}
}

fn declared_attributes(&self) -> Vec<String> {
Expand Down
2 changes: 2 additions & 0 deletions scarb/src/compiler/plugin/proc_macro/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod ffi;
mod host;

pub use ffi::*;
pub use host::*;

0 comments on commit 727a0d9

Please sign in to comment.