Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
b9dd50f
feat(fzn-rs): Started working on a more ergonomic
maartenflippo Jul 3, 2025
8f5c966
feat(fzn-rs): Implement first go at the derive macro
maartenflippo Jul 4, 2025
442a6b6
fix(fzn-rs): Don't panic in an error case, but emit a compiler error
maartenflippo Jul 4, 2025
f41d13b
refactor(fzn-rs): Attach spans to AST in preparation for error messages
maartenflippo Jul 4, 2025
316dbea
refactor(fzn-rs): Simplify the code generation code
maartenflippo Jul 4, 2025
92aa520
feat(fzn-rs): Improve error messages when parsing instance
maartenflippo Jul 4, 2025
63d013d
feat(fzn-rs): Resolve arrays from the AST
maartenflippo Jul 4, 2025
da96b0f
refactor(fzn-rs): Cleanup implementation and add documentation
maartenflippo Jul 4, 2025
b4a9bb9
docs(fzn-rs): Add root module documentation to fzn-rs
maartenflippo Jul 4, 2025
f61f065
feat(fzn-rs): Explicitly set the name of a constraint in derive macro
maartenflippo Jul 4, 2025
e112887
feat(fzn-rs): Implement a basic FZN parser, without annotation support
maartenflippo Jul 5, 2025
1a2b78f
refactor(fzn-rs): Update documentation and generalize variable arguments
maartenflippo Jul 5, 2025
713715c
feat(fzn-rs): Start implemented typed annotation parsing
maartenflippo Jul 5, 2025
265d461
feat(fzn-rs): Implement parsing of nested annotations
maartenflippo Jul 6, 2025
4a76f3f
feat(fzn-rs): Allow typed annotations on the solve item
maartenflippo Jul 6, 2025
4e899d9
feat(fzn-rs): Implement array annotation arguments
maartenflippo Jul 6, 2025
47be477
refactor(fzn-rs): Implement parsing of comments
maartenflippo Jul 8, 2025
938be6e
feat(fzn-rs): Parse annotations in variables, arrays, constraints, an…
maartenflippo Jul 8, 2025
70b202b
feat(fzn-rs): Ignore predicate declarations in flatzinc
maartenflippo Jul 8, 2025
54fdfc8
feat(fzn-rs): Allow constraint arguments to be separate structs
maartenflippo Jul 11, 2025
a60b0b2
docs(fzn-rs): Add example with constraint args as separate struct
maartenflippo Jul 11, 2025
6c47fd0
refactor(fzn-rs): Replace '_' with '-' in crate names
maartenflippo Jul 11, 2025
b8fba22
refactor(fzn-rs): Separate annotations for variables, constraints, an…
maartenflippo Jul 11, 2025
a06b90f
feat(fzn-rs): Parse annotation arguments in struct
maartenflippo Jul 11, 2025
07a1ca5
feat(fzn-rs): Implement RangeList::iter for i64 elements
maartenflippo Jul 11, 2025
0281a3c
feat(fzn-rs): Implement support for i32 as an integer type
maartenflippo Jul 15, 2025
f064a8c
refactor(fzn-rs): Remove redundent function
maartenflippo Jul 24, 2025
b3e68da
refactor(fzn-rs): Rename VariableArg to VariableExpr, and use it as t…
maartenflippo Jul 24, 2025
aa3bead
refactor(fzn-rs): Clean up the implementation into modules + document…
maartenflippo Jul 25, 2025
a67c74a
fix(fzn-rs): Fix numerous compiler issues after previous refactor
maartenflippo Jul 25, 2025
a8bd3fd
refactor(fzn-rs): Improve documentation of the API
maartenflippo Jul 25, 2025
c901201
refactor(fzn-rs): Remove useless function
maartenflippo Jul 25, 2025
03a649e
fix(fzn-rs): Ignore consequtive lines with comments
maartenflippo Jul 28, 2025
9be118d
feat(fzn-rs): Implement display for token
maartenflippo Jul 28, 2025
afe3e57
feat(fzn-rs): Check argument length of constraint and provide span in…
maartenflippo Jul 28, 2025
87fffcd
docs(fzn-rs): Correct documentation on Ast and clarify the two annota…
maartenflippo Jul 28, 2025
be0d583
refactor(fzn-rs): Use arrayexpr in annotation arguments
maartenflippo Jul 28, 2025
945b37e
chore(fzn-rs): Remove commented code
maartenflippo Jul 28, 2025
a552f88
feat(pumpkin-solver): Ported first few steps of FZN compiler to fzn-rs
maartenflippo Jul 11, 2025
5e1aaf0
feat(pumpkin-solver): Ported more steps of FZN compiler to fzn-rs
maartenflippo Jul 11, 2025
95baa01
refactor(pumpkin-solver): Update the post_constraints to fzn-rs
maartenflippo Jul 15, 2025
649e002
refactor(pumpkin-solver): Create the objective and search strategy fr…
maartenflippo Jul 24, 2025
1d997f3
refactor(pumpkin-solver): Remove unused code from flatzinc compiler
maartenflippo Jul 24, 2025
b5782c5
refactor(pumpkin-solver): Fix tests and a few clippy warnings
maartenflippo Jul 24, 2025
5c07844
refactor(pumpkin-solver): More updates to fzn-rs API
maartenflippo Jul 25, 2025
17c75a3
refactor(pumpkin-solver): Remove `flatzinc` dependency
maartenflippo Jul 25, 2025
033bdd0
fix(pumpkin-solver): Fix handling of output_array annotation
maartenflippo Jul 25, 2025
5ad2a56
refactor(pumpkin-solver): Print flatzinc parse errors nicely
maartenflippo Jul 28, 2025
a969452
refactor(pumpkin-solver): Cleanup implementation
maartenflippo Jul 28, 2025
7db5d9e
fix(pumpkin-solver): Print incorrect arguments error nicely
maartenflippo Jul 28, 2025
c86174b
fix(pumpkin-solver): Add the exploration strategy argument to search …
maartenflippo Jul 28, 2025
e0d5bf8
fix(pumpkin-solver): Use ArrayExpr for annotation arguments
maartenflippo Jul 28, 2025
0254746
refactor(pumpkin-solver): Use arrayexpr in annotation arguments
maartenflippo Jul 28, 2025
c15f9e7
fix(pumpkin-solver): Fix naming of value selection strategy
maartenflippo Jul 28, 2025
95912d6
fix(pumpkin-solver): Fix argument order in array_min/max constraints
maartenflippo Jul 28, 2025
c26239b
fix(pumpkin-solver): Fix argument order in bool2int
maartenflippo Jul 28, 2025
eb1dff2
fix(pumpkin-solver): Fix handling of set_in constraints
maartenflippo Jul 28, 2025
e4e1526
fix(pumpkin-solver): Fix typos from refactor
maartenflippo Jul 28, 2025
c2718c5
fix(pumpkin-solver): Always resolve the representative of a variable
maartenflippo Jul 29, 2025
2d6a99a
feat(fzn-rs): Started working on a more ergonomic
maartenflippo Jul 3, 2025
3374257
feat(fzn-rs): Implement first go at the derive macro
maartenflippo Jul 4, 2025
1578152
fix(fzn-rs): Don't panic in an error case, but emit a compiler error
maartenflippo Jul 4, 2025
8f46242
refactor(fzn-rs): Attach spans to AST in preparation for error messages
maartenflippo Jul 4, 2025
2bc861d
refactor(fzn-rs): Simplify the code generation code
maartenflippo Jul 4, 2025
fc44c2d
feat(fzn-rs): Improve error messages when parsing instance
maartenflippo Jul 4, 2025
fe14636
feat(fzn-rs): Resolve arrays from the AST
maartenflippo Jul 4, 2025
0553e5c
refactor(fzn-rs): Cleanup implementation and add documentation
maartenflippo Jul 4, 2025
5cbf8cd
docs(fzn-rs): Add root module documentation to fzn-rs
maartenflippo Jul 4, 2025
fa20c07
feat(fzn-rs): Explicitly set the name of a constraint in derive macro
maartenflippo Jul 4, 2025
d130855
feat(fzn-rs): Implement a basic FZN parser, without annotation support
maartenflippo Jul 5, 2025
8b0bb06
refactor(fzn-rs): Update documentation and generalize variable arguments
maartenflippo Jul 5, 2025
6fe939b
feat(fzn-rs): Start implemented typed annotation parsing
maartenflippo Jul 5, 2025
23b27b0
feat(fzn-rs): Implement parsing of nested annotations
maartenflippo Jul 6, 2025
8101eb6
feat(fzn-rs): Allow typed annotations on the solve item
maartenflippo Jul 6, 2025
a922bae
feat(fzn-rs): Implement array annotation arguments
maartenflippo Jul 6, 2025
64c2be6
refactor(fzn-rs): Implement parsing of comments
maartenflippo Jul 8, 2025
17688ca
feat(fzn-rs): Parse annotations in variables, arrays, constraints, an…
maartenflippo Jul 8, 2025
bdd8300
feat(fzn-rs): Ignore predicate declarations in flatzinc
maartenflippo Jul 8, 2025
076b84d
feat(fzn-rs): Allow constraint arguments to be separate structs
maartenflippo Jul 11, 2025
3c41a22
docs(fzn-rs): Add example with constraint args as separate struct
maartenflippo Jul 11, 2025
7259330
refactor(fzn-rs): Replace '_' with '-' in crate names
maartenflippo Jul 11, 2025
24c8094
refactor(fzn-rs): Separate annotations for variables, constraints, an…
maartenflippo Jul 11, 2025
cd1d10d
feat(fzn-rs): Parse annotation arguments in struct
maartenflippo Jul 11, 2025
d23a154
feat(fzn-rs): Implement RangeList::iter for i64 elements
maartenflippo Jul 11, 2025
e3bb899
feat(fzn-rs): Implement support for i32 as an integer type
maartenflippo Jul 15, 2025
730c2d0
refactor(fzn-rs): Remove redundent function
maartenflippo Jul 24, 2025
cb09d01
refactor(fzn-rs): Rename VariableArg to VariableExpr, and use it as t…
maartenflippo Jul 24, 2025
2623657
refactor(fzn-rs): Clean up the implementation into modules + document…
maartenflippo Jul 25, 2025
c92f4a0
fix(fzn-rs): Fix numerous compiler issues after previous refactor
maartenflippo Jul 25, 2025
c695f9b
refactor(fzn-rs): Improve documentation of the API
maartenflippo Jul 25, 2025
ca822f0
refactor(fzn-rs): Remove useless function
maartenflippo Jul 25, 2025
ca41b82
fix(fzn-rs): Ignore consequtive lines with comments
maartenflippo Jul 28, 2025
3553296
feat(fzn-rs): Implement display for token
maartenflippo Jul 28, 2025
59963a9
feat(fzn-rs): Check argument length of constraint and provide span in…
maartenflippo Jul 28, 2025
6313b9c
docs(fzn-rs): Correct documentation on Ast and clarify the two annota…
maartenflippo Jul 28, 2025
9d952b5
refactor(fzn-rs): Use arrayexpr in annotation arguments
maartenflippo Jul 28, 2025
04e6b7f
chore(fzn-rs): Remove commented code
maartenflippo Jul 28, 2025
438aa3b
refactor: Update cargo.lock
maartenflippo Aug 13, 2025
fc9e7f8
feat(fzn-rs): Implement parsing of the domain of array elements
maartenflippo Aug 15, 2025
d9a48e4
Merge branch 'feat/improved-fzn-parser' into feat/pumpkin-use-fzn-rs
maartenflippo Aug 13, 2025
b0ad0ca
fix: Warm start annotations
maartenflippo Aug 13, 2025
2c85f16
refactor(pumpkin-solver): Fix identification of output arrays
maartenflippo Aug 15, 2025
244b353
refactor(pumpkin-solver): Support int_lin_*_imp constraints
maartenflippo Aug 15, 2025
0dff70d
WIP
maartenflippo Sep 4, 2025
eeb8e53
fix(pumpkin-solver): Correctly create domain if flatzinc variable is …
maartenflippo Sep 8, 2025
6be52f2
refactor(fzn-rs): Move away from using Rc in errors
maartenflippo Sep 9, 2025
2b6bfc9
Merge branch 'feat/improved-fzn-parser' into feat/pumpkin-use-fzn-rs
maartenflippo Sep 9, 2025
a33363e
fix(pumpkin-solver): Compiler errors after updating parser
maartenflippo Sep 9, 2025
25ac7e8
feat(fzn-rs): Allow access to error values
maartenflippo Sep 9, 2025
a49eea9
Merge branch 'feat/improved-fzn-parser' into feat/pumpkin-use-fzn-rs
maartenflippo Sep 9, 2025
44cbb24
refactor(fzn-rs): Remove the explicit lexing stage from the fzn parser
maartenflippo Sep 10, 2025
f760140
Merge branch 'feat/improved-fzn-parser' into feat/pumpkin-use-fzn-rs
maartenflippo Sep 10, 2025
8c5b945
refactor(pumpkin-solver): Update to the revised error type in fzn-rs
maartenflippo Sep 10, 2025
9d26f9f
Revert "refactor(pumpkin-solver): Update to the revised error type in…
maartenflippo Sep 11, 2025
c5dd030
Revert "Merge branch 'feat/improved-fzn-parser' into feat/pumpkin-use…
maartenflippo Sep 11, 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
72 changes: 53 additions & 19 deletions Cargo.lock

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

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

Expand Down
2 changes: 1 addition & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
allowed-duplicate-crates = ["regex-automata", "regex-syntax"]
allowed-duplicate-crates = ["regex-automata", "regex-syntax", "convert_case"]
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 a value 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
}
}
}
}
55 changes: 55 additions & 0 deletions fzn-rs-derive/src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use convert_case::Case;
use convert_case::Casing;

/// Get the name of the constraint or annotation from the variant. This either is converting the
/// variant name to snake case, or retrieving the value from the `#[name(...)]` attribute.
pub(crate) fn get_explicit_name(variant: &syn::Variant) -> syn::Result<String> {
variant
.attrs
.iter()
// Find the attribute with a `name` as the path.
.find(|attr| attr.path().get_ident().is_some_and(|ident| ident == "name"))
// Parse the arguments of the attribute to a string literal.
.map(|attr| {
attr.parse_args::<syn::LitStr>()
.map(|string_lit| string_lit.value())
})
// If no `name` attribute exists, return the snake-case version of the variant name.
.unwrap_or_else(|| Ok(variant.ident.to_string().to_case(Case::Snake)))
}

/// Returns the type of the arguments for the variant if the variant has exactly the following
/// shape:
///
/// ```ignore
/// #[args]
/// Variant(Type)
/// ```
pub(crate) fn get_args_type(variant: &syn::Variant) -> Option<&syn::Type> {
let has_args_attr = variant
.attrs
.iter()
.any(|attr| attr.path().get_ident().is_some_and(|ident| ident == "args"));

if !has_args_attr {
return None;
}

if variant.fields.len() != 1 {
// If there is not exactly one argument for this variant, then it cannot be a struct
// constraint.
return None;
}

let field = variant
.fields
.iter()
.next()
.expect("there is exactly one field");

if field.ident.is_none() {
Some(&field.ty)
} else {
None
}
}
Loading