@@ -344,6 +344,48 @@ pub fn entity(input: TokenStream) -> TokenStream {
344344 }
345345}
346346
347+ /// Parsed input for the [`delta`] macro.
348+ ///
349+ /// The invocation takes the form `crate_path, namespace_path, prev, curr, [..]`.
350+ /// `prev` and `curr` are expressions evaluating to [`TribleSet`]s. The pattern
351+ /// syntax matches that of [`pattern!`].
352+ struct DeltaInput {
353+ crate_path : Path ,
354+ ns : Path ,
355+ prev : Expr ,
356+ curr : Expr ,
357+ pattern : Vec < Entity > ,
358+ }
359+
360+ impl Parse for DeltaInput {
361+ fn parse ( input : ParseStream < ' _ > ) -> syn:: Result < Self > {
362+ let crate_path: Path = input. parse ( ) ?;
363+ input. parse :: < Token ! [ , ] > ( ) ?;
364+ let ns: Path = input. parse ( ) ?;
365+ input. parse :: < Token ! [ , ] > ( ) ?;
366+ let prev: Expr = input. parse ( ) ?;
367+ input. parse :: < Token ! [ , ] > ( ) ?;
368+ let curr: Expr = input. parse ( ) ?;
369+ input. parse :: < Token ! [ , ] > ( ) ?;
370+ let content;
371+ bracketed ! ( content in input) ;
372+ let mut pattern = Vec :: new ( ) ;
373+ while !content. is_empty ( ) {
374+ pattern. push ( content. parse ( ) ?) ;
375+ if content. peek ( Token ! [ , ] ) {
376+ content. parse :: < Token ! [ , ] > ( ) ?;
377+ }
378+ }
379+ Ok ( DeltaInput {
380+ crate_path,
381+ ns,
382+ prev,
383+ curr,
384+ pattern,
385+ } )
386+ }
387+ }
388+
347389fn entity_impl ( input : TokenStream ) -> syn:: Result < TokenStream > {
348390 let EntityInput {
349391 crate_path,
@@ -406,3 +448,182 @@ fn entity_impl(input: TokenStream) -> syn::Result<TokenStream> {
406448
407449 Ok ( output. into ( ) )
408450}
451+
452+ /// Procedural implementation of the `delta!` macro.
453+ #[ proc_macro]
454+ pub fn delta ( input : TokenStream ) -> TokenStream {
455+ match delta_impl ( input) {
456+ Ok ( ts) => ts,
457+ Err ( e) => e. to_compile_error ( ) . into ( ) ,
458+ }
459+ }
460+
461+ fn delta_impl ( input : TokenStream ) -> syn:: Result < TokenStream > {
462+ use std:: collections:: HashMap ;
463+
464+ let DeltaInput {
465+ crate_path,
466+ ns,
467+ prev,
468+ curr,
469+ pattern,
470+ } = syn:: parse ( input) ?;
471+
472+ // Identifiers used throughout the expansion
473+ let ctx_ident = format_ident ! ( "__ctx" , span = Span :: call_site( ) ) ;
474+ let prev_ident = format_ident ! ( "__prev" , span = Span :: call_site( ) ) ;
475+ let curr_ident = format_ident ! ( "__curr" , span = Span :: call_site( ) ) ;
476+ let delta_ident = format_ident ! ( "__delta" , span = Span :: call_site( ) ) ;
477+
478+ // Prepare declarations shared by all union branches
479+ let mut attr_decl_tokens = TokenStream2 :: new ( ) ;
480+ let mut attr_const_tokens = TokenStream2 :: new ( ) ;
481+
482+ let mut entity_decl_tokens = TokenStream2 :: new ( ) ;
483+ let mut entity_const_tokens = TokenStream2 :: new ( ) ;
484+
485+ let mut value_decl_tokens = TokenStream2 :: new ( ) ;
486+ let mut value_const_tokens = TokenStream2 :: new ( ) ;
487+
488+ struct TripleInfo {
489+ e_ident : Ident ,
490+ a_ident : Ident ,
491+ v_ident : Ident ,
492+ }
493+ let mut triples: Vec < TripleInfo > = Vec :: new ( ) ;
494+
495+ let mut attr_map: HashMap < String , Ident > = HashMap :: new ( ) ;
496+ let mut attr_idx = 0usize ;
497+ let mut entity_idx = 0usize ;
498+ let mut value_idx = 0usize ;
499+
500+ for entity in pattern {
501+ let e_ident = format_ident ! ( "__e{}" , entity_idx, span = Span :: call_site( ) ) ;
502+ entity_idx += 1 ;
503+ match entity. id {
504+ Some ( EntityId :: Var ( id) ) => {
505+ entity_decl_tokens. extend ( quote ! { let #e_ident = #id; } ) ;
506+ }
507+ Some ( EntityId :: Lit ( expr) ) => {
508+ entity_decl_tokens. extend ( quote ! {
509+ let #e_ident: #crate_path:: query:: Variable <#crate_path:: value:: schemas:: genid:: GenId > = #ctx_ident. next_variable( ) ;
510+ } ) ;
511+ entity_const_tokens. extend ( quote ! {
512+ constraints. push( { let e: #crate_path:: id:: Id = #expr; Box :: new( #e_ident. is( #crate_path:: value:: ToValue :: to_value( e) ) ) } ) ;
513+ } ) ;
514+ }
515+ None => {
516+ entity_decl_tokens. extend ( quote ! {
517+ let #e_ident: #crate_path:: query:: Variable <#crate_path:: value:: schemas:: genid:: GenId > = #ctx_ident. next_variable( ) ;
518+ } ) ;
519+ }
520+ }
521+
522+ for Field { name, value } in entity. fields {
523+ let field_ident = name;
524+ let a_ident = attr_map
525+ . entry ( field_ident. to_string ( ) )
526+ . or_insert_with ( || {
527+ let ident = format_ident ! ( "__a{}" , attr_idx, span = Span :: call_site( ) ) ;
528+ attr_idx += 1 ;
529+ attr_decl_tokens. extend ( quote ! {
530+ let #ident: #crate_path:: query:: Variable <#crate_path:: value:: schemas:: genid:: GenId > = #ctx_ident. next_variable( ) ;
531+ } ) ;
532+ attr_const_tokens. extend ( quote ! {
533+ constraints. push( Box :: new( #ident. is( #crate_path:: value:: ToValue :: to_value( ns:: ids:: #field_ident) ) ) ) ;
534+ } ) ;
535+ ident
536+ } )
537+ . clone ( ) ;
538+
539+ let v_ident = format_ident ! ( "__v{}" , value_idx, span = Span :: call_site( ) ) ;
540+ value_idx += 1 ;
541+
542+ match value {
543+ FieldValue :: Lit ( expr) => {
544+ let val_ident = format_ident ! ( "__c{}" , value_idx, span = Span :: call_site( ) ) ;
545+ value_idx += 1 ;
546+ value_decl_tokens. extend ( quote ! {
547+ let #v_ident: #crate_path:: query:: Variable <ns:: schemas:: #field_ident> = #ctx_ident. next_variable( ) ;
548+ let #val_ident: #crate_path:: value:: Value <ns:: schemas:: #field_ident> = #crate_path:: value:: ToValue :: to_value( #expr) ;
549+ } ) ;
550+ value_const_tokens. extend ( quote ! {
551+ constraints. push( Box :: new( #v_ident. is( #val_ident) ) ) ;
552+ } ) ;
553+ }
554+ FieldValue :: Var ( expr) => {
555+ value_decl_tokens. extend ( quote ! {
556+ let #v_ident: #crate_path:: query:: Variable <ns:: schemas:: #field_ident> = #expr;
557+ } ) ;
558+ }
559+ }
560+
561+ triples. push ( TripleInfo {
562+ e_ident : e_ident. clone ( ) ,
563+ a_ident : a_ident. clone ( ) ,
564+ v_ident,
565+ } ) ;
566+ }
567+ }
568+
569+ let mut case_exprs: Vec < TokenStream2 > = Vec :: new ( ) ;
570+ for delta_idx in 0 ..triples. len ( ) {
571+ let mut triple_tokens = TokenStream2 :: new ( ) ;
572+ for (
573+ idx,
574+ TripleInfo {
575+ e_ident,
576+ a_ident,
577+ v_ident,
578+ } ,
579+ ) in triples. iter ( ) . enumerate ( )
580+ {
581+ let dataset = if idx == delta_idx {
582+ & delta_ident
583+ } else {
584+ & curr_ident
585+ } ;
586+ triple_tokens. extend ( quote ! {
587+ constraints. push( Box :: new( #dataset. pattern( #e_ident, #a_ident, #v_ident) ) ) ;
588+ } ) ;
589+ }
590+
591+ let case = quote ! {
592+ {
593+ let mut constraints: Vec <Box <dyn #crate_path:: query:: Constraint >> = vec![ ] ;
594+ use #crate_path:: query:: TriblePattern ;
595+ #triple_tokens
596+ #crate_path:: query:: intersectionconstraint:: IntersectionConstraint :: new( constraints)
597+ }
598+ } ;
599+ case_exprs. push ( case) ;
600+ }
601+
602+ let union_expr = quote ! {
603+ #crate_path:: query:: unionconstraint:: UnionConstraint :: new( vec![
604+ #( Box :: new( #case_exprs) as Box <dyn #crate_path:: query:: Constraint >) , *
605+ ] )
606+ } ;
607+
608+ let output = quote ! {
609+ {
610+ let #ctx_ident = __local_find_context!( ) ;
611+ let #prev_ident = #prev;
612+ let #curr_ident = #curr;
613+ let #delta_ident = #curr_ident. difference( & #prev_ident) ;
614+ use #ns as ns;
615+ #attr_decl_tokens
616+ #entity_decl_tokens
617+ #value_decl_tokens
618+ let mut constraints: Vec <Box <dyn #crate_path:: query:: Constraint >> = vec![ ] ;
619+ use #crate_path:: query:: TriblePattern ;
620+ #attr_const_tokens
621+ #entity_const_tokens
622+ #value_const_tokens
623+ constraints. push( Box :: new( #union_expr) ) ;
624+ #crate_path:: query:: intersectionconstraint:: IntersectionConstraint :: new( constraints)
625+ }
626+ } ;
627+
628+ Ok ( output. into ( ) )
629+ }
0 commit comments