From 772e4b6d3de18ad94be11b88f8c25c4000a9a489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20Galv=C3=A1n=20=28Dub=29?= Date: Wed, 13 Dec 2023 18:05:21 -0300 Subject: [PATCH] [core] Inline macro - `delete!` (#1271) * adding delete_macro (wip) * fix test delete!() * fix delete.rs --- crates/dojo-core/src/world_test.cairo | 8 +- crates/dojo-lang/src/inline_macros/delete.rs | 164 +++++++++++++++++++ crates/dojo-lang/src/inline_macros/mod.rs | 1 + crates/dojo-lang/src/plugin.rs | 2 + 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 crates/dojo-lang/src/inline_macros/delete.rs diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 22cb3c372d..07bb4e1e9b 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -33,6 +33,7 @@ struct Fizz { trait Ibar { fn set_foo(self: @TContractState, a: felt252, b: u128); fn delete_foo(self: @TContractState); + fn delete_foo_macro(self: @TContractState, foo: Foo); fn set_char(self: @TContractState, a: felt252, b: u32); } @@ -67,6 +68,10 @@ mod bar { .delete_entity('Foo', array![get_caller_address().into()].span(), layout.span()); } + fn delete_foo_macro(self: @ContractState, foo: Foo) { + delete!(self.world.read(), Foo { caller: foo.caller, a: foo.a, b: foo.b }); + } + fn set_char(self: @ContractState, a: felt252, b: u32) { set!( self.world.read(), @@ -151,7 +156,8 @@ fn test_delete() { assert(stored.b == 1337, 'data not stored'); // delete model - bar_contract.delete_foo(); + bar_contract.delete_foo_macro(stored); + let deleted: Foo = get!(world, get_caller_address(), Foo); assert(deleted.a == 0, 'data not deleted'); assert(deleted.b == 0, 'data not deleted'); diff --git a/crates/dojo-lang/src/inline_macros/delete.rs b/crates/dojo-lang/src/inline_macros/delete.rs new file mode 100644 index 0000000000..728a7ee10f --- /dev/null +++ b/crates/dojo-lang/src/inline_macros/delete.rs @@ -0,0 +1,164 @@ +use std::collections::HashMap; + +use cairo_lang_defs::patcher::PatchBuilder; +use cairo_lang_defs::plugin::{ + InlineMacroExprPlugin, InlinePluginResult, NamedPlugin, PluginDiagnostic, PluginGeneratedFile, +}; +use cairo_lang_semantic::inline_macros::unsupported_bracket_diagnostic; +use cairo_lang_syntax::node::ast::{ExprPath, ExprStructCtorCall, FunctionWithBody, ItemModule}; +use cairo_lang_syntax::node::kind::SyntaxKind; +use cairo_lang_syntax::node::{ast, TypedSyntaxNode}; + +use super::unsupported_arg_diagnostic; +use super::utils::{parent_of_kind, SystemRWOpRecord, SYSTEM_WRITES}; + +#[derive(Debug, Default)] +pub struct DeleteMacro; + +impl NamedPlugin for DeleteMacro { + const NAME: &'static str = "delete"; +} + +impl InlineMacroExprPlugin for DeleteMacro { + fn generate_code( + &self, + db: &dyn cairo_lang_syntax::node::db::SyntaxGroup, + syntax: &ast::ExprInlineMacro, + ) -> InlinePluginResult { + let ast::WrappedArgList::ParenthesizedArgList(arg_list) = syntax.arguments(db) else { + return unsupported_bracket_diagnostic(db, syntax); + }; + let mut builder = PatchBuilder::new(db); + builder.add_str("{"); + + let args = arg_list.arguments(db).elements(db); + + if args.len() != 2 { + return InlinePluginResult { + code: None, + diagnostics: vec![PluginDiagnostic { + stable_ptr: arg_list.arguments(db).stable_ptr().untyped(), + message: "Invalid arguments. Expected \"(world, (models,))\"".to_string(), + }], + }; + } + + let world = &args[0]; + + let ast::ArgClause::Unnamed(models) = args[1].arg_clause(db) else { + return unsupported_arg_diagnostic(db, syntax); + }; + + let mut bundle = vec![]; + + match models.value(db) { + ast::Expr::Parenthesized(parens) => { + let syntax_node = parens.expr(db).as_syntax_node(); + bundle.push((syntax_node.get_text(db), syntax_node)); + } + ast::Expr::Tuple(list) => { + list.expressions(db).elements(db).into_iter().for_each(|expr| { + let syntax_node = expr.as_syntax_node(); + bundle.push((syntax_node.get_text(db), syntax_node)); + }) + } + ast::Expr::StructCtorCall(ctor) => { + let syntax_node = ctor.as_syntax_node(); + bundle.push((syntax_node.get_text(db), syntax_node)); + } + _ => { + return InlinePluginResult { + code: None, + diagnostics: vec![PluginDiagnostic { + message: "Invalid arguments. Expected \"(world, (models,))\"".to_string(), + stable_ptr: arg_list.arguments(db).stable_ptr().untyped(), + }], + }; + } + } + + if bundle.is_empty() { + return InlinePluginResult { + code: None, + diagnostics: vec![PluginDiagnostic { + message: "Invalid arguments: No models provided.".to_string(), + stable_ptr: arg_list.arguments(db).stable_ptr().untyped(), + }], + }; + } + + let module_syntax_node = + parent_of_kind(db, &syntax.as_syntax_node(), SyntaxKind::ItemModule); + let module_name = if let Some(module_syntax_node) = &module_syntax_node { + let mod_ast = ItemModule::from_syntax_node(db, module_syntax_node.clone()); + mod_ast.name(db).as_syntax_node().get_text_without_trivia(db) + } else { + "".into() + }; + + let fn_syntax_node = + parent_of_kind(db, &syntax.as_syntax_node(), SyntaxKind::FunctionWithBody); + let fn_name = if let Some(fn_syntax_node) = &fn_syntax_node { + let fn_ast = FunctionWithBody::from_syntax_node(db, fn_syntax_node.clone()); + fn_ast.declaration(db).name(db).as_syntax_node().get_text_without_trivia(db) + } else { + "".into() + }; + + for (entity, syntax_node) in bundle { + // db.lookup_intern_file(key0); + if !module_name.is_empty() && !fn_name.is_empty() { + let mut system_writes = SYSTEM_WRITES.lock().unwrap(); + // fn_syntax_node + if system_writes.get(&module_name).is_none() { + system_writes.insert(module_name.clone(), HashMap::new()); + } + let fns = system_writes.get_mut(&module_name).unwrap(); + if fns.get(&fn_name).is_none() { + fns.insert(fn_name.clone(), vec![]); + } + + match syntax_node.kind(db) { + SyntaxKind::ExprPath => { + fns.get_mut(&fn_name).unwrap().push(SystemRWOpRecord::Path( + ExprPath::from_syntax_node(db, syntax_node), + )); + } + // SyntaxKind::StatementExpr => { + // todo!() + // } + SyntaxKind::ExprStructCtorCall => { + fns.get_mut(&fn_name).unwrap().push(SystemRWOpRecord::StructCtor( + ExprStructCtorCall::from_syntax_node(db, syntax_node.clone()), + )); + } + _ => eprintln!( + "Unsupport component value type {} for semantic writer analysis", + syntax_node.kind(db) + ), + } + } + + builder.add_str(&format!( + " + let __delete_macro_value__ = {}; + {}.delete_entity(dojo::model::Model::name(@__delete_macro_value__), + dojo::model::Model::keys(@__delete_macro_value__), + dojo::model::Model::layout(@__delete_macro_value__));", + entity, + world.as_syntax_node().get_text(db), + )); + } + builder.add_str("}"); + + InlinePluginResult { + code: Some(PluginGeneratedFile { + name: "delete_inline_macro".into(), + content: builder.code, + diagnostics_mappings: builder.diagnostics_mappings, + aux_data: None, + }), + diagnostics: vec![], + } + } +} diff --git a/crates/dojo-lang/src/inline_macros/mod.rs b/crates/dojo-lang/src/inline_macros/mod.rs index 2cc237256d..08953fc9c5 100644 --- a/crates/dojo-lang/src/inline_macros/mod.rs +++ b/crates/dojo-lang/src/inline_macros/mod.rs @@ -3,6 +3,7 @@ use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{ast, Terminal, TypedSyntaxNode}; use smol_str::SmolStr; +pub mod delete; pub mod emit; pub mod get; pub mod set; diff --git a/crates/dojo-lang/src/plugin.rs b/crates/dojo-lang/src/plugin.rs index 725a0e1b93..e97f5763f5 100644 --- a/crates/dojo-lang/src/plugin.rs +++ b/crates/dojo-lang/src/plugin.rs @@ -21,6 +21,7 @@ use smol_str::SmolStr; use url::Url; use crate::contract::DojoContract; +use crate::inline_macros::delete::DeleteMacro; use crate::inline_macros::emit::EmitMacro; use crate::inline_macros::get::GetMacro; use crate::inline_macros::set::SetMacro; @@ -216,6 +217,7 @@ pub fn dojo_plugin_suite() -> PluginSuite { suite .add_plugin::() + .add_inline_macro_plugin::() .add_inline_macro_plugin::() .add_inline_macro_plugin::() .add_inline_macro_plugin::();