Skip to content

Commit 5ed768e

Browse files
Merge pull request #123 from triblespace/codex/refactor-pattern-macro-for-unique-attributes
reuse attribute vars in pattern macro
2 parents 67fb084 + 3b13a83 commit 5ed768e

2 files changed

Lines changed: 30 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9696
- Replaced branch allocation code with `Layout::from_size_align_unchecked`.
9797
- Removed unused `FromBlob` and `TryToBlob` traits and updated documentation.
9898
- Simplified constant comparison in query tests.
99+
- `pattern!` now reuses attribute variables for identical field names.
99100
- Clarified that the project's developer experience goal also includes
100101
providing an intuitive API for library users.
101102
- Documented Kani proof guidelines to avoid constants and prefer

tribles-macros/src/lib.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,22 @@ fn pattern_impl(input: TokenStream) -> syn::Result<TokenStream> {
168168

169169
// Accumulate the token stream for each entity pattern.
170170
let mut entity_tokens = TokenStream2::new();
171+
// Token stream that initializes attribute variables once.
172+
let mut attr_tokens = TokenStream2::new();
173+
// Bring the namespace into scope for attribute initialization.
174+
attr_tokens.extend(quote! { use #ns as ns; });
171175
// Counter to create unique identifiers for entity variables.
172-
let mut idx = 0usize;
176+
let mut entity_idx = 0usize;
177+
// Counter and map for unique attribute variables.
178+
let mut attr_idx = 0usize;
179+
use std::collections::HashMap;
180+
let mut attr_map: HashMap<String, Ident> = HashMap::new();
173181

174182
// Expand one block per entity described in the pattern.
175183
for entity in pattern {
176184
// Variable name representing the entity id.
177-
let e_ident = format_ident!("__e{}", idx, span = Span::call_site());
178-
idx += 1;
185+
let e_ident = format_ident!("__e{}", entity_idx, span = Span::call_site());
186+
entity_idx += 1;
179187
// Initialization depends on whether an id was supplied.
180188
let init = match entity.id {
181189
// Existing identifier variable: reuse it directly.
@@ -194,24 +202,35 @@ fn pattern_impl(input: TokenStream) -> syn::Result<TokenStream> {
194202
// Emit triple constraints for each field within the entity.
195203
for Field { name, value } in entity.fields {
196204
let field_ident = name;
205+
206+
// Reuse the same attribute variable for each unique field name.
207+
let a_var_ident = attr_map
208+
.entry(field_ident.to_string())
209+
.or_insert_with(|| {
210+
let ident = format_ident!("__a{}", attr_idx, span = Span::call_site());
211+
attr_idx += 1;
212+
attr_tokens.extend(quote! {
213+
let #ident: #crate_path::query::Variable<#crate_path::value::schemas::genid::GenId> = #ctx_ident.next_variable();
214+
constraints.push(Box::new(#ident.is(#crate_path::value::ToValue::to_value(ns::ids::#field_ident))));
215+
});
216+
ident
217+
})
218+
.clone();
219+
197220
let triple_tokens = match value {
198221
// Literal value: create a variable bound to the literal and match it.
199222
FieldValue::Lit(expr) => {
200223
quote! {
201224
{
202225
use #crate_path::query::TriblePattern;
203226
use #ns as ns;
204-
// fresh vars for attribute and value
205-
let a_var: #crate_path::query::Variable<#crate_path::value::schemas::genid::GenId> = #ctx_ident.next_variable();
206227
let v_var: #crate_path::query::Variable<ns::schemas::#field_ident> = #ctx_ident.next_variable();
207228
// literal value converted to a `Value`
208229
let v: #crate_path::value::Value<ns::schemas::#field_ident> = #crate_path::value::ToValue::to_value(#expr);
209-
// ensure the attribute id matches
210-
constraints.push(Box::new(a_var.is(#crate_path::value::ToValue::to_value(ns::ids::#field_ident))));
211230
// ensure the literal matches the variable
212231
constraints.push(Box::new(v_var.is(v)));
213232
// match the triple from the dataset
214-
constraints.push(Box::new(#set_ident.pattern(#e_ident, a_var, v_var)));
233+
constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, v_var)));
215234
}
216235
}
217236
}
@@ -221,10 +240,8 @@ fn pattern_impl(input: TokenStream) -> syn::Result<TokenStream> {
221240
{
222241
use #crate_path::query::TriblePattern;
223242
use #ns as ns;
224-
let a_var: #crate_path::query::Variable<#crate_path::value::schemas::genid::GenId> = #ctx_ident.next_variable();
225243
let v_var: #crate_path::query::Variable<ns::schemas::#field_ident> = #expr;
226-
constraints.push(Box::new(a_var.is(#crate_path::value::ToValue::to_value(ns::ids::#field_ident))));
227-
constraints.push(Box::new(#set_ident.pattern(#e_ident, a_var, v_var)));
244+
constraints.push(Box::new(#set_ident.pattern(#e_ident, #a_var_ident, v_var)));
228245
}
229246
}
230247
}
@@ -239,6 +256,7 @@ fn pattern_impl(input: TokenStream) -> syn::Result<TokenStream> {
239256
let mut constraints: Vec<Box<dyn #crate_path::query::Constraint>> = vec![];
240257
let #ctx_ident = __local_find_context!();
241258
let #set_ident = #set;
259+
#attr_tokens
242260
#entity_tokens
243261
#crate_path::query::intersectionconstraint::IntersectionConstraint::new(constraints)
244262
}

0 commit comments

Comments
 (0)