Structure the Harmony core to rely on a DAG for declaring & executing Scores

This commit is contained in:
Ian Letourneau
2025-09-12 20:43:50 -04:00
commit a351fd1228
8 changed files with 1235 additions and 0 deletions

11
harmony-derive/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "harmony-derive"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
quote = "1.0.40"
syn = "2.0.106"

58
harmony-derive/src/lib.rs Normal file
View File

@@ -0,0 +1,58 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{Attribute, Ident, ItemStruct, parse_macro_input};
// This was an attempt to get a derive macro to simplify implementing new scores
//
// The goal was to get a syntax similar to this:
// #[derive(Score)]
// #[interpret(MyInterpret)]
// #[dependencies(OtherScore("foo"), OtherScore("bar"), ExtraScore("baz"))]
// struct MyScore {
// foo: LinkedValue<String>,
// bar: LinkedValue<String>,
// baz: LinkedValue<Vec<u32>>,
// }
#[proc_macro_derive(Score, attributes(interpret))]
pub fn score_derive(input: TokenStream) -> TokenStream {
let input_struct = parse_macro_input!(input as ItemStruct);
let score_name = &input_struct.ident;
let interpret_name = get_interpret_name(&input_struct.attrs);
let expanded = quote! {
#[derive(Debug)]
pub struct #interpret_name {
score: #score_name,
}
impl<T: Topology> crate::Score<T> for #score_name {
fn create_interpret(&self) -> Box<dyn crate::Interpret<T>> {
Box::new(#interpret_name {
score: self.clone(),
})
}
}
};
TokenStream::from(expanded)
}
fn get_interpret_name(attrs: &Vec<Attribute>) -> Ident {
for attr in attrs {
if attr.path().is_ident("interpret") {
let nested_meta = attr
.parse_args_with(
syn::punctuated::Punctuated::<syn::Ident, syn::Token![,]>::parse_terminated,
)
.unwrap();
if let Some(ident) = nested_meta.first() {
return ident.clone();
}
}
}
// Return a default or panic if the attribute is not found
Ident::new("DefaultInterpret", proc_macro::Span::call_site().into())
}