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

Use mentat-parser-utils in tx-parser. Initial stab at #235 #249

Merged
merged 1 commit into from
Feb 10, 2017

Conversation

joewalker
Copy link
Member

Work in progress patch just to see if I'm on the right lines.


use combine::{any, eof, many, optional, parser, satisfy_map, token, Parser, ParseResult, Stream};
use combine::combinator::{Expected, FnParser};
use edn::symbols::NamespacedKeyword;
use edn::types::Value;
use mentat_tx::entities::*;
use mentat_tx::entities::{Entid, EntidOrLookupRef, Entity, LookupRef, ValueOrLookupRef};
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason to import stuff explicitly instead of everything?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's good style. Using * means that a simple addition in a different crate can cause this one to fail to compile.

Copy link
Contributor

Choose a reason for hiding this comment

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

There's a clash of styles it would seem, as @ncalexan opted for * in the first place. Have we settled on a single style for our codebase? My question was directed to the fact that I didn't see how it this change was related to the issue being fixed (it could've been for what I know, I still don't have a solid grasp on rust).

Copy link
Member Author

@joewalker joewalker Feb 6, 2017

Choose a reason for hiding this comment

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

I changed it because I misread an error message telling me it wasn't needed (it was actually the one down around line 274 in mod tests).

I agree with @rnewman's logic, and I'd also add that some specificity about what's required and what isn't is a good thing from a purely visual POV to humans too

@@ -40,6 +40,7 @@ macro_rules! matches_plain_symbol {
/// The provided `$body` will be evaluated with `$input` bound to the input stream.
///
/// `$body`, when run, should return a `ParseResult` of the appropriate result type.
#[macro_export]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should probably go ahead and export all of these macros.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nothing in query-parser should be exported; if it's directly applicable to other crates, it should be moved to parser-utils.

Copy link
Contributor

@victorporof victorporof Feb 6, 2017

Choose a reason for hiding this comment

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

I thought not putting these in the parser_utils crate in the first place was intentional. If moved there though, they should indeed be exported as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It was: they're very EDN-specific, and I wasn't sure how reusable they would be.

It looks like @joewalker could directly use def_value_satisfy_parser_fn!, though…

Copy link
Member Author

Choose a reason for hiding this comment

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

So I'm moving all (?) the parser utils from parser_utils to parser-utils, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding is: if it can be used in other places as well, then yes. However, indeed in this particular case def_value_satisfy_parser_fn could be used instead of def_parser_fn, so maybe don't move unless there's a really good reason to?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The macros are built on each other, so you can't move and use d_v_s_p_f without moving the others.

Copy link
Member Author

Choose a reason for hiding this comment

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

@victorporof You're saying we could use def_value_satisfy_parser_fn here? I'm not sure how - what should the path be?

def_value_satisfy_parser_fn!(Tx, integer, i64, Value::Integer(x));

Doesn't work

@@ -13,12 +13,17 @@
extern crate edn;
extern crate combine;
extern crate mentat_tx;
extern crate mentat_parser_utils;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is also likely to require a macro_use pragma if you plan on using the macros there. I'd be surprised if not.

@@ -30,9 +35,19 @@ fn fn_parser<O, I>(f: fn(I) -> ParseResult<O, I>, err: &'static str) -> TxParser
parser(f).expected(err)
}

def_parser_fn!(Tx, integer, Value, i64, input, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should probably not have to define macros here. There's another def_parser_fn! as well in the mentat query parser crate. Did you intend to redefine it here? I'm having a hard time understanding what this is for as well, since it's not used anywhere.

Copy link
Collaborator

Choose a reason for hiding this comment

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

def_parser_fn! is a macro invocation, not a macro definition.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah! I misread this!

@ncalexan
Copy link
Member

ncalexan commented Feb 6, 2017

There's a clash of styles it would seem, as @ncalexan opted for * in the first place. Have we settled on a single style for our codebase? My question was directed to the fact that I didn't see how it this change was related to the issue being fixed (it could've been for what I know, I still don't have a solid grasp on rust).

Yeah, I think this was a mistake. In some cases it makes sense -- for example, during development -- but in most cases it's best to just list them. Filed #251 to unify on the non-use ... * form throughout the code base.

@victorporof
Copy link
Contributor

@joewalker Is this still WIP?

@joewalker
Copy link
Member Author

Is this still WIP?

Erm, maybe?

Do we have any test coverage info anywhere? All the tests pass, but can I be sure we're testing it properly?
Are there other places where we should do this?
I'm still not very confident that I did it correctly TBH.

@joewalker
Copy link
Member Author

Something that makes me think I'm not done yet - The pre-PR version of entities contains "[:db/add|:db/retract ...]", but the current post PR version doesn't contain this anywhere.

@joewalker joewalker mentioned this pull request Feb 9, 2017
@joewalker
Copy link
Member Author

Elsewhere @rnewman said (of lib.rs:48)

This can be phrased in terms of def_value_satisfy_parser_fn!.

Which sounds likely, except that I'm stuck on what $transformer should be. I'd like to say:

def_value_satisfy_parser_fn!(Tx, integer, i64, if let Value::Integer(y) = x {
        Some(y)
    } else {
        None
    });

But nope. Any ideas?

@joewalker
Copy link
Member Author

joewalker commented Feb 9, 2017

About to squash and rebase. Head was at 12f17c1

.parse_stream(input);
}
// The above using a def_value_satisfy_parser_fn
//def_value_satisfy_parser_fn!(Tx, integer, i64, if let Value::Integer(y) = x { Some(y) } else { None });
Copy link
Collaborator

Choose a reason for hiding this comment

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

In practice this allows you to simply pass a function that accepts an &edn::Value and
returns an Option<$result_type>: if a suitable value is at the front of the stream,
it will be converted and returned by the parser; otherwise, the parse will fail.

So if you define a function:

fn value_to_i64(val: &Value) -> Option<i64> {
    if let &Value::Integer(i) = val { Some(i) } else { None }
}

you can use it here:

def_value_satisfy_parser_fn!(Tx, integer, i64, value_to_i64);

Now it turns out we already have that function: it's Value::as_integer. So this should work:

def_value_satisfy_parser_fn!(Tx, integer, i64, Value::as_integer);

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, except that the signature of as_integer is (&self) -> Option<&i64>, and we want -> Option<i64>. So maybe I just include the function as stated?

Copy link
Collaborator

Choose a reason for hiding this comment

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

i64 implements Copy, so we should fix the definition of as_integer (and as_float, as_boolean). No sense returning a reference type when the raw type is no larger than a machine address!

I can do that very quickly in a side PR. Momento…

Copy link
Collaborator

Choose a reason for hiding this comment

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

// Manually expanding the above:
// def_value_parser_fn!(Tx, integer, i64, input, {
// satisfy_map(|x: edn::Value| $transformer(&x) ).parse_stream(input)
// satisfy_map(|x: edn::Value| if let Value::Integer(y) = x { Some(y) } else { None }).parse_stream(input)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The trick is that $transformer is a path, not a block, so you can't pass an expression to this macro.

.parse_stream(input);
}
def_parser_fn!(Tx, keyword, Value, NamespacedKeyword, input, {
return satisfy_map(|x: Value| if let Value::NamespacedKeyword(y) = x {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, this would be Value::as_namespaced_keyword.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, and if this can't be done, because you get back a ref, then we perhaps want Value::to_namespaced_keyword, which would simply be

pub fn to_namespaced_keyword(&self) -> Option<NamespacedKeyword> {
    self.as_namespaced_keyword().map(|x| x.clone())
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Didn't see this comment until I'd worked it out myself, except I wrote:

fn value_to_namespaced_keyword(val: &Value) -> Option<NamespacedKeyword> {
    if let &Value::NamespacedKeyword(ref i) = val { Some(i.clone()) } else { None }
}

I think it makes sense to do this in Value though.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did this using macros: joewalker@a4ba10c

match r {
// TODO: abstract the "match Vector, parse internal stream" pattern to remove this boilerplate.
def_parser_fn!(Tx, add, Value, Entity, input, {
return satisfy_map(|x: Value| -> Option<Entity> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can omit the return and the closing semicolon, saving yourself some space.

@joewalker
Copy link
Member Author

I think we can land this. OK?

return satisfy_map(|x: Value| if let Value::Integer(y) = x {
Some(y)
fn value_to_namespaced_keyword(val: &Value) -> Option<NamespacedKeyword> {
if let &Value::NamespacedKeyword(ref i) = val { Some(i.clone()) } else { None }
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you can use val.as_namespaced_keyword().map(|x| x.clone()) here.

Copy link
Member Author

@joewalker joewalker Feb 10, 2017

Choose a reason for hiding this comment

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

Or even:

def_value_satisfy_parser_fn!(Tx, keyword, NamespacedKeyword, Value::into_namespaced_keyword);

Since the landing of 49f91b0

Copy link
Member Author

Choose a reason for hiding this comment

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

Sigh. Nope. I'll do it your way and land it, and work out the fancy thing later.

…ctorporof

Move macros query-parser/…/parser_utils.rs → parser-utils/…/query.rs

Signed-off-by: Joe Walker <jwalker@mozilla.com>
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.

4 participants