diff --git a/datapond-derive/tests/fail/kwargs.rs b/datapond-derive/tests/fail/kwargs.rs new file mode 100644 index 0000000..69836c6 --- /dev/null +++ b/datapond-derive/tests/fail/kwargs.rs @@ -0,0 +1,28 @@ +use datapond_derive::datapond; + +fn test1() { + let inp = vec![(1, 2, 0), (2, 3, 0)]; + let out; + datapond! { + input inp(x: u32, y: u32, z: u32) + output out(x: u32, y: u32) + out(x, y) :- inp(.y=y, .y=x). + }; + assert_eq!(out.len(), 2); +} + +fn test2() { + let inp = vec![(1, 2, 0), (2, 3, 0)]; + let out; + datapond! { + input inp(x: u32, y: u32, z: u32) + output out(x: u32, y: u32) + out(x, y) :- inp(.a=y, .y=x). + }; + assert_eq!(out.len(), 2); +} + +fn main() { + test1(); + test2(); +} \ No newline at end of file diff --git a/datapond-derive/tests/fail/kwargs.stderr b/datapond-derive/tests/fail/kwargs.stderr new file mode 100644 index 0000000..1937032 --- /dev/null +++ b/datapond-derive/tests/fail/kwargs.stderr @@ -0,0 +1,11 @@ +error: Parameter already bound: y + --> $DIR/kwargs.rs:9:33 + | +9 | out(x, y) :- inp(.y=y, .y=x). + | ^ + +error: Unknown parameter a in predicate inp. Available parameters are: x,y,z. + --> $DIR/kwargs.rs:20:27 + | +20 | out(x, y) :- inp(.a=y, .y=x). + | ^ diff --git a/datapond-derive/tests/pass/kwargs.rs b/datapond-derive/tests/pass/kwargs.rs new file mode 100644 index 0000000..fbec88a --- /dev/null +++ b/datapond-derive/tests/pass/kwargs.rs @@ -0,0 +1,23 @@ +use datapond_derive::datapond; + +fn main() { + let inp = vec![(1, 2, 0), (2, 3, 0)]; + let out; + let out2; + datapond! { + input inp(x: u32, y: u32, z: u32) + + output out(x: u32, y: u32) + out(x, y) :- inp(.y=y, .x=x). + + output out2(x: u32, y: u32) + out2(a, b) :- inp(.y=a, .x=b). + }; + assert_eq!(out.len(), 2); + assert_eq!(out[0], (1, 2)); + assert_eq!(out[1], (2, 3)); + + assert_eq!(out2.len(), 2); + assert_eq!(out2[0], (2, 1)); + assert_eq!(out2[1], (3, 2)); +} diff --git a/src/typechecker.rs b/src/typechecker.rs index 97665b0..92acf30 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -117,42 +117,46 @@ fn check_body( .collect() } past::ArgList::Named(named_args) => { - let kwargs: HashMap<_, _> = named_args - .into_iter() - .map(|named_arg| (named_arg.param.to_string(), named_arg.arg)) - .collect(); - let mut args = Vec::new(); + let mut kwargs = HashMap::new(); let mut used_parameters = HashSet::new(); + for named_arg in named_args { + let param_name = named_arg.param.to_string(); + if used_parameters.contains(¶m_name) { + return Err(Error::new( + format!("Parameter already bound: {}", param_name), + named_arg.param.span(), + )); + } + used_parameters.insert(param_name.clone()); + kwargs.insert(param_name, named_arg); + } + let mut args = Vec::new(); + let mut available_parameters = HashSet::new(); for parameter in &decl.parameters { let param_name = parameter.name.to_string(); let arg = match kwargs.get(¶m_name) { - Some(ident) => { + Some(past::NamedArg { arg: ident, .. }) => { let ident_str = ident.to_string(); - if used_parameters.contains(&ident_str) { - return Err(Error::new( - format!("Parameter already bound: {}", ident_str), - ident.span(), - )); - } used_parameters.insert(ident_str); ast::Arg::Ident(ident.clone()) } None => ast::Arg::Wildcard, }; + available_parameters.insert(param_name); args.push(arg); } for key in kwargs.keys() { - if !used_parameters.contains(key) { - let available_parameters: Vec<_> = used_parameters.iter().map(|parameter| parameter.to_string()).collect(); + if !available_parameters.contains(key) { + let available_parameters: Vec<_> = available_parameters.into_iter().collect(); + let parameter_span = kwargs[key].param.span(); return Err(Error::new( format!("Unknown parameter {} in predicate {}. Available parameters are: {}.", key, literal.predicate, available_parameters.join(","), ), - literal.predicate.span(), + parameter_span, )); } } - if kwargs.len() != used_parameters.len() {} args } };