Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b2c3edc
feat(fzn-rs): Started working on a more ergonomic
maartenflippo Jul 3, 2025
4832931
feat(fzn-rs): Implement first go at the derive macro
maartenflippo Jul 4, 2025
d93b3dc
fix(fzn-rs): Don't panic in an error case, but emit a compiler error
maartenflippo Jul 4, 2025
e502505
refactor(fzn-rs): Attach spans to AST in preparation for error messages
maartenflippo Jul 4, 2025
8d188a3
refactor(fzn-rs): Simplify the code generation code
maartenflippo Jul 4, 2025
1546a42
feat(fzn-rs): Improve error messages when parsing instance
maartenflippo Jul 4, 2025
cbd928d
feat(fzn-rs): Resolve arrays from the AST
maartenflippo Jul 4, 2025
3e128a6
refactor(fzn-rs): Cleanup implementation and add documentation
maartenflippo Jul 4, 2025
acff028
docs(fzn-rs): Add root module documentation to fzn-rs
maartenflippo Jul 4, 2025
1c2feab
feat(fzn-rs): Explicitly set the name of a constraint in derive macro
maartenflippo Jul 4, 2025
aa4bbc9
feat(fzn-rs): Implement a basic FZN parser, without annotation support
maartenflippo Jul 5, 2025
9cb3af9
refactor(fzn-rs): Update documentation and generalize variable arguments
maartenflippo Jul 5, 2025
e1d0052
feat(fzn-rs): Start implemented typed annotation parsing
maartenflippo Jul 5, 2025
bab22ac
feat(fzn-rs): Implement parsing of nested annotations
maartenflippo Jul 6, 2025
503ab69
feat(fzn-rs): Allow typed annotations on the solve item
maartenflippo Jul 6, 2025
888d5ac
feat(fzn-rs): Implement array annotation arguments
maartenflippo Jul 6, 2025
10efe95
refactor(fzn-rs): Implement parsing of comments
maartenflippo Jul 8, 2025
3c52b9c
feat(fzn-rs): Parse annotations in variables, arrays, constraints, an…
maartenflippo Jul 8, 2025
1178c9a
feat(fzn-rs): Ignore predicate declarations in flatzinc
maartenflippo Jul 8, 2025
ef3c05a
feat(fzn-rs): Allow constraint arguments to be separate structs
maartenflippo Jul 11, 2025
1015e5c
docs(fzn-rs): Add example with constraint args as separate struct
maartenflippo Jul 11, 2025
b19b326
refactor(fzn-rs): Replace '_' with '-' in crate names
maartenflippo Jul 11, 2025
127520d
refactor(fzn-rs): Separate annotations for variables, constraints, an…
maartenflippo Jul 11, 2025
3275baa
feat(fzn-rs): Parse annotation arguments in struct
maartenflippo Jul 11, 2025
433c34e
feat(fzn-rs): Implement RangeList::iter for i64 elements
maartenflippo Jul 11, 2025
1e7eff6
feat(fzn-rs): Implement support for i32 as an integer type
maartenflippo Jul 15, 2025
93be6b3
refactor(fzn-rs): Remove redundent function
maartenflippo Jul 24, 2025
4ade384
refactor(fzn-rs): Rename VariableArg to VariableExpr, and use it as t…
maartenflippo Jul 24, 2025
e13302f
refactor(fzn-rs): Clean up the implementation into modules + document…
maartenflippo Jul 25, 2025
d2a1963
fix(fzn-rs): Fix numerous compiler issues after previous refactor
maartenflippo Jul 25, 2025
6490f87
refactor(fzn-rs): Improve documentation of the API
maartenflippo Jul 25, 2025
345ec0b
refactor(fzn-rs): Remove useless function
maartenflippo Jul 25, 2025
345912d
fix(fzn-rs): Ignore consequtive lines with comments
maartenflippo Jul 28, 2025
8a9d928
feat(fzn-rs): Implement display for token
maartenflippo Jul 28, 2025
2b68ecf
feat(fzn-rs): Check argument length of constraint and provide span in…
maartenflippo Jul 28, 2025
fd8253b
docs(fzn-rs): Correct documentation on Ast and clarify the two annota…
maartenflippo Jul 28, 2025
e0e0841
refactor(fzn-rs): Use arrayexpr in annotation arguments
maartenflippo Jul 28, 2025
7184bc3
chore(fzn-rs): Remove commented code
maartenflippo Jul 28, 2025
43a9120
refactor: Update cargo.lock
maartenflippo Aug 13, 2025
beea64d
feat(fzn-rs): Implement parsing of the domain of array elements
maartenflippo Aug 15, 2025
e7202d4
refactor(fzn-rs): Move away from using Rc in errors
maartenflippo Sep 9, 2025
ef2b6c7
feat(fzn-rs): Allow access to error values
maartenflippo Sep 9, 2025
2656a36
refactor(fzn-rs): Remove the explicit lexing stage from the fzn parser
maartenflippo Sep 10, 2025
1c2ccaf
Revert "refactor(fzn-rs): Remove the explicit lexing stage from the f…
maartenflippo Sep 11, 2025
dfefbe2
docs(fzn-rs): Comments and naming of types
maartenflippo Oct 2, 2025
7d97aaf
refactor(fzn-rs): Update docs
maartenflippo Dec 10, 2025
a57c6dc
fix(fzn-rs): Remove cargo features
maartenflippo Dec 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 133 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[workspace]
members = ["./pumpkin-solver", "./drcp-format", "./pumpkin-solver-py", "./pumpkin-macros", "./drcp-debugger", "./pumpkin-crates/*"]
default-members = ["./pumpkin-solver", "./drcp-format", "./pumpkin-solver-py", "./pumpkin-macros", "./pumpkin-crates/*"]
members = ["./pumpkin-solver", "./drcp-format", "./pumpkin-solver-py", "./pumpkin-macros", "./drcp-debugger", "./pumpkin-crates/*", "./fzn-rs", "./fzn-rs-derive"]
resolver = "2"

[workspace.package]
Expand Down
22 changes: 22 additions & 0 deletions fzn-rs-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "fzn-rs-derive"
version = "0.1.0"
repository.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true

[lib]
proc-macro = true

[dependencies]
convert_case = "0.8.0"
proc-macro2 = "1.0.95"
quote = "1.0.40"
syn = { version = "2.0.104", features = ["extra-traits"] }

[dev-dependencies]
fzn-rs = { path = "../fzn-rs/" }

[lints]
workspace = true
114 changes: 114 additions & 0 deletions fzn-rs-derive/src/annotation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use quote::quote;

/// Construct a token stream that initialises an annotation with name `value_type` and the arguments
/// described in `fields`.
pub(crate) fn initialise_value(
value_type: &syn::Ident,
fields: &syn::Fields,
) -> proc_macro2::TokenStream {
// For every field, initialise the value for that field.
let field_values = fields.iter().enumerate().map(|(idx, field)| {
let ty = &field.ty;

// If the field has a name, then prepend the field value with the name: `name: value`.
let value_prefix = if let Some(ident) = &field.ident {
quote! { #ident: }
} else {
quote! {}
};

// If there is an `#[annotation]` attribute on the field, then the value is the result of
// parsing a nested annotation. Otherwise, we look at the type of the field
// and parse the value corresponding to that type.
if field.attrs.iter().any(|attr| {
attr.path()
.get_ident()
.is_some_and(|ident| ident == "annotation")
}) {
quote! {
#value_prefix <#ty as ::fzn_rs::FromNestedAnnotation>::from_argument(
&arguments[#idx],
)?
}
} else {
quote! {
#value_prefix <#ty as ::fzn_rs::FromAnnotationArgument>::from_argument(
&arguments[#idx],
)?
}
}
});

// Complete the value initialiser by prepending the type name to the field values.
let value_initialiser = match fields {
syn::Fields::Named(_) => quote! { #value_type { #(#field_values),* } },
syn::Fields::Unnamed(_) => quote! { #value_type ( #(#field_values),* ) },
syn::Fields::Unit => quote! { #value_type },
};

let num_arguments = fields.len();

// Output the final initialisation, with checking of number of arguments.
quote! {
if arguments.len() != #num_arguments {
return Err(::fzn_rs::InstanceError::IncorrectNumberOfArguments {
expected: #num_arguments,
actual: arguments.len(),
span: annotation.span,
});
}

Ok(Some(#value_initialiser))
}
}

/// Create the parsing code for one annotation corresponding to the given variant.
pub(crate) fn variant_to_annotation(variant: &syn::Variant) -> proc_macro2::TokenStream {
// Determine the flatzinc annotation name.
let name = match crate::common::get_explicit_name(variant) {
Ok(name) => name,
Err(_) => {
return quote! {
compile_error!("Invalid usage of #[name(...)]");
};
}
};

let variant_name = &variant.ident;

// If variant argument is a struct, then delegate parsing of the annotation arguments to that
// struct.
if let Some(constraint_type) = crate::common::get_args_type(variant) {
return quote! {
::fzn_rs::ast::Annotation::Call(::fzn_rs::ast::AnnotationCall {
name,
arguments,
}) if name.as_ref() == #name => {
let args = <#constraint_type as ::fzn_rs::FlatZincAnnotation>::from_ast_required(annotation)?;
let value = #variant_name(args);
Ok(Some(value))
}
};
}

// If the variant has no arguments, parse an atom annotaton. Otherwise, initialise the values
// of the variant arguments.
if matches!(variant.fields, syn::Fields::Unit) {
quote! {
::fzn_rs::ast::Annotation::Atom(ident) if ident.as_ref() == #name => {
Ok(Some(#variant_name))
}
}
} else {
let value = initialise_value(&variant.ident, &variant.fields);

quote! {
::fzn_rs::ast::Annotation::Call(::fzn_rs::ast::AnnotationCall {
name,
arguments,
}) if name.as_ref() == #name => {
#value
}
}
}
}
Loading