From d1acb29431936138522b7da2d4c9dac22246f105 Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Thu, 9 Nov 2023 11:09:54 +0100 Subject: [PATCH 1/4] support wildcard terms --- nemo/src/io/parser.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/nemo/src/io/parser.rs b/nemo/src/io/parser.rs index f262fc364..23e4cd8b9 100644 --- a/nemo/src/io/parser.rs +++ b/nemo/src/io/parser.rs @@ -302,6 +302,8 @@ pub struct RuleParser<'a> { sources: RefCell>, /// Declarations of predicates with their types. predicate_declarations: RefCell>>, + /// Number counting up for generating distinct wildcards. + wildcard_generator: RefCell, } impl<'a> RuleParser<'a> { @@ -1003,6 +1005,7 @@ impl<'a> RuleParser<'a> { self.parse_parenthesised_term(), self.parse_function_term(), self.parse_aggregate(), + self.parse_wildcard(), )), multispace_or_comment0, )(input) @@ -1012,6 +1015,20 @@ impl<'a> RuleParser<'a> { ) } + /// Parse a wildcard variable. + pub fn parse_wildcard(&'a self) -> impl FnMut(Span<'a>) -> IntermediateResult { + traced( + "parse_wildcard", + map_res(space_delimited_token("_"), |_| { + let wildcard = format!("__WILDCARD_{}", self.wildcard_generator.borrow()); + *self.wildcard_generator.borrow_mut() += 1; + Ok::<_, ParseError>(Term::Primitive(PrimitiveTerm::Variable( + Variable::Universal(wildcard.into()), + ))) + }), + ) + } + /// Parse a parenthesised term tree. pub fn parse_parenthesised_term(&'a self) -> impl FnMut(Span<'a>) -> IntermediateResult { traced( From 804462d85c1a0f9935e58c44b40a805518db8590 Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Thu, 9 Nov 2023 11:18:48 +0100 Subject: [PATCH 2/4] add blackbox test --- resources/testcases/wildcards/run.rls | 3 +++ resources/testcases/wildcards/run/result.csv | 4 ++++ resources/testcases/wildcards/sources/main.csv | 13 +++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 resources/testcases/wildcards/run.rls create mode 100644 resources/testcases/wildcards/run/result.csv create mode 100644 resources/testcases/wildcards/sources/main.csv diff --git a/resources/testcases/wildcards/run.rls b/resources/testcases/wildcards/run.rls new file mode 100644 index 000000000..4a1fddc00 --- /dev/null +++ b/resources/testcases/wildcards/run.rls @@ -0,0 +1,3 @@ +@source input[3]: load-csv("sources/main.csv"). + +result(?x) :- input(?x, _, _). \ No newline at end of file diff --git a/resources/testcases/wildcards/run/result.csv b/resources/testcases/wildcards/run/result.csv new file mode 100644 index 000000000..8be4b36f6 --- /dev/null +++ b/resources/testcases/wildcards/run/result.csv @@ -0,0 +1,4 @@ +2 +4 +6 +8 \ No newline at end of file diff --git a/resources/testcases/wildcards/sources/main.csv b/resources/testcases/wildcards/sources/main.csv new file mode 100644 index 000000000..16c2de3c5 --- /dev/null +++ b/resources/testcases/wildcards/sources/main.csv @@ -0,0 +1,13 @@ +2,0,0 +2,0,1 +2,1,0 +2,5,2 +2,5,3 +4,0,0 +4,2,1 +4,5,1 +6,2,0 +6,9,2 +8,1,2 +8,4,2 +8,5,1 \ No newline at end of file From 8a2e530be4ca75917edcd915c4aa2a9337da5b1c Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Thu, 9 Nov 2023 15:51:45 +0100 Subject: [PATCH 3/4] move wildcard testcase --- resources/testcases/regression/wildcards/regression.yaml | 5 +++++ resources/testcases/{ => regression}/wildcards/run.rls | 0 .../testcases/{ => regression}/wildcards/run/result.csv | 0 .../testcases/{ => regression}/wildcards/sources/main.csv | 0 4 files changed, 5 insertions(+) create mode 100644 resources/testcases/regression/wildcards/regression.yaml rename resources/testcases/{ => regression}/wildcards/run.rls (100%) rename resources/testcases/{ => regression}/wildcards/run/result.csv (100%) rename resources/testcases/{ => regression}/wildcards/sources/main.csv (100%) diff --git a/resources/testcases/regression/wildcards/regression.yaml b/resources/testcases/regression/wildcards/regression.yaml new file mode 100644 index 000000000..996dd0f29 --- /dev/null +++ b/resources/testcases/regression/wildcards/regression.yaml @@ -0,0 +1,5 @@ +issue: 408 +title: writing wildcard instead of a variable name +tracker: https://github.com/knowsys/nemo/issues/408 +type: feature + diff --git a/resources/testcases/wildcards/run.rls b/resources/testcases/regression/wildcards/run.rls similarity index 100% rename from resources/testcases/wildcards/run.rls rename to resources/testcases/regression/wildcards/run.rls diff --git a/resources/testcases/wildcards/run/result.csv b/resources/testcases/regression/wildcards/run/result.csv similarity index 100% rename from resources/testcases/wildcards/run/result.csv rename to resources/testcases/regression/wildcards/run/result.csv diff --git a/resources/testcases/wildcards/sources/main.csv b/resources/testcases/regression/wildcards/sources/main.csv similarity index 100% rename from resources/testcases/wildcards/sources/main.csv rename to resources/testcases/regression/wildcards/sources/main.csv From 3a44259e8ed86a7ec22b6aa3f296a4e8b915b10c Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Thu, 9 Nov 2023 18:00:19 +0100 Subject: [PATCH 4/4] add error message for wildcards in the head of a rule --- nemo/src/io/parser.rs | 6 ++---- nemo/src/io/parser/types.rs | 3 +++ nemo/src/model/rule_model/rule.rs | 4 ++++ nemo/src/model/rule_model/term.rs | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/nemo/src/io/parser.rs b/nemo/src/io/parser.rs index 23e4cd8b9..01195aad3 100644 --- a/nemo/src/io/parser.rs +++ b/nemo/src/io/parser.rs @@ -1020,11 +1020,9 @@ impl<'a> RuleParser<'a> { traced( "parse_wildcard", map_res(space_delimited_token("_"), |_| { - let wildcard = format!("__WILDCARD_{}", self.wildcard_generator.borrow()); + let wildcard = Variable::create_wildcard(*self.wildcard_generator.borrow()); *self.wildcard_generator.borrow_mut() += 1; - Ok::<_, ParseError>(Term::Primitive(PrimitiveTerm::Variable( - Variable::Universal(wildcard.into()), - ))) + Ok::<_, ParseError>(Term::Primitive(PrimitiveTerm::Variable(wildcard))) }), ) } diff --git a/nemo/src/io/parser/types.rs b/nemo/src/io/parser/types.rs index 30e6a7dcb..dc3ec550e 100644 --- a/nemo/src/io/parser/types.rs +++ b/nemo/src/io/parser/types.rs @@ -134,6 +134,9 @@ pub enum ParseError { /// An existentially quantified variable occurs in the body of a rule. #[error(r#"Variable "{0}" occurs existentially quantified in the rule body."#)] BodyExistential(String), + /// A wildcard pattern was used inside of the rule head. + #[error(r#"The head of a rule must not contain any wildcard patterns."#)] + WildcardInHead, /// The universal variable does not occur in a positive body literal. #[error(r#"The universal variable "{0}" does not occur in a positive body literal."#)] UnsafeHeadVariable(String), diff --git a/nemo/src/model/rule_model/rule.rs b/nemo/src/model/rule_model/rule.rs index 4300dcb8e..825e842d9 100644 --- a/nemo/src/model/rule_model/rule.rs +++ b/nemo/src/model/rule_model/rule.rs @@ -130,6 +130,10 @@ impl Rule { // Head atoms may only use variables that are either bound or derived for variable in head.iter().flat_map(|a| a.variables()) { + if variable.is_wildcard() { + return Err(ParseError::WildcardInHead); + } + if variable.is_universal() && !safe_variables.contains(variable) && !derived_variables.contains(variable) diff --git a/nemo/src/model/rule_model/term.rs b/nemo/src/model/rule_model/term.rs index 3ec24c358..ff880bc55 100644 --- a/nemo/src/model/rule_model/term.rs +++ b/nemo/src/model/rule_model/term.rs @@ -27,6 +27,8 @@ pub enum Variable { } impl Variable { + const WILDCARD_PREFIX: &'static str = "__WILDCARD"; + /// Return the name of the variable. pub fn name(&self) -> String { match self { @@ -43,6 +45,19 @@ impl Variable { pub fn is_existential(&self) -> bool { matches!(self, Variable::Existential(_)) } + + /// Return whether this variable was generated by a wildcard pattern. + pub fn is_wildcard(&self) -> bool { + match self { + Variable::Universal(ident) => ident.0.starts_with(Self::WILDCARD_PREFIX), + Variable::Existential(_) => false, + } + } + + /// Make a wildcard variable with the given unique index. + pub fn create_wildcard(index: usize) -> Variable { + Variable::Universal(format!("{}{}", Self::WILDCARD_PREFIX, index).into()) + } } impl Display for Variable {