Skip to content
This repository has been archived by the owner on Sep 12, 2018. It is now read-only.

Crudely parse or and or-join. (#388) #390

Closed
wants to merge 13 commits into from
Closed

Crudely parse or and or-join. (#388) #390

wants to merge 13 commits into from

Conversation

rnewman
Copy link
Collaborator

@rnewman rnewman commented Mar 23, 2017

I'm really unhappy about the amount of boilerplate in this parser. But it works.

Note that unlike DataScript's parser, this one doesn't validate the contents of the variable lists.

@rnewman rnewman self-assigned this Mar 23, 2017
@rnewman rnewman requested a review from ncalexan March 23, 2017 21:14
Copy link
Member

@ncalexan ncalexan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delayed review. This looks fine! I hope to help plate the boilers less densely shortly.


NonMatchingVariablesInOrClause {
// TODO: flesh out.
description("non-matching variables in 'or' clause")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does non-matching differ from unmatched? Or mismatched?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're asking about wording.

IMO there's not a lot of clarity in the distinction, if any, but:

  • "Unmatched variables" kinda means we were looking for one or two and didn't get them.
  • "Mismatched variables" means we were combining two things and they didn't match. A mismatch is bidirectional.
  • "Non-matching variables" implies quite weakly that some expected property didn't hold.

That's the weak reasoning behind this wording: this error means that the expected property — that every arm in the or uses all of the variables in the rule vars, which might themselves be derived from the arms — doesn't hold. There can be an arbitrary number of arms.

query/src/lib.rs Outdated
@@ -515,3 +516,79 @@ pub struct FindQuery {
pub where_clauses: Vec<WhereClause>,
// TODO: in_rules;
}

pub trait ContainsVariables {
fn acc_mentioned_variables(&self, acc: &mut BTreeSet<Variable>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider accumulate_mentioned_variables.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or mentioned_variables, and then make the other function collect_mentioned_variables.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did both: accumulate_mentioned_variables, called by collect_mentioned_variables.

// Let's do some detailed parse checks.
let mut arms = clauses.into_iter();
match (arms.next(), arms.next(), arms.next()) {
(Some(left),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: one line or different indentation. (Maybe this is an artifact of GH's layout, but it looks dedented one space.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 135 has a relative 4-space indent, but match is five characters long.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see — you were referring to 136. Fixed.

[?artist :artist/type ?type])]"#;
let parsed = parse_find_string(query).unwrap();
match parsed.where_clauses.into_iter().next().unwrap() {
WhereClause::OrJoin(or_join) => assert!(validate_or_join(&or_join).is_err()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style can be quite difficult to debug when you break the tests, since you'll get unhelpful panics from the unwrap invocations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched for expects.

}
}

/// Test that two arms of an `or-join` can contain different variables if they both
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These descriptions really helped. Thanks!

},
_ => unimplemented!(),
}
cc.apply_clause(schema, where_clause);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ignores the result, doesn't it? You want the trailing ? or some other chaining operator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised the compiler doesn't yell about this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too!

});

def_value_parser_fn!(Where, rule_vars, Vec<Variable>, input, {
satisfy_map(|x: edn::Value| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took a long time, but I think I've figured out improvements to this pattern, which should allow us to write something like:

or(vector(), list()).of(many1::<Vec<_>, _>(Query::variable())

which I think is a significant improvement.

query/src/lib.rs Outdated
@@ -474,13 +474,30 @@ pub struct Predicate {
pub args: Vec<FnArg>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnifyVars {
Implicit,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a comment on the difference here. Am I correct that Implicit is equivalent to Explicit with the complete set of variables occurring in all (equivalently, each) clause?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but — as far as I understand Datomic and DataScript, at any rate — implicit means that the extracted variables are all 'free', and explicit means that the specified join vars are all bound by the time the pattern is processed (and Datomic 'pushes down' until that's true).

So this is fine:

[:find ?x :where
 (or [?x :foo/bar ?y]
     [?x :foo/baz ?y])
 [?y :foo/knows "John"]]

and this is fine:

[:find ?x :where
 [?x :foo/name "John"]
 (or-join [?x]
   [?x :foo/friend ?y]
   (and [?x :foo/friend ?z] [?z :foo/parent ?y]))]

@rnewman
Copy link
Collaborator Author

rnewman commented Mar 27, 2017

8adb6d9

@rnewman rnewman closed this Mar 27, 2017
@rnewman rnewman deleted the rnewman/or-join branch March 27, 2017 23:33
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants