Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/expr parser #21

Merged
merged 5 commits into from
May 8, 2024
Merged

Feature/expr parser #21

merged 5 commits into from
May 8, 2024

Conversation

ArnaudBger
Copy link
Contributor

  • Add an expression parser in the substreams repo
  • Add a new pb file for index keys

@ArnaudBger ArnaudBger requested a review from maoueh May 8, 2024 13:43
Cargo.toml Outdated
@@ -18,6 +18,9 @@ rust-version = "1.60"

[workspace.dependencies]
substreams-macro = { version = "0.5.13", path = "./substreams-macro" }
pest= "2.7.10"
pest_derive = "2.7.10"
rstest = "0.19.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this just for testing, if yes, should go in dev-dependencies bucket

Cargo.toml Outdated
@@ -18,6 +18,9 @@ rust-version = "1.60"

[workspace.dependencies]
substreams-macro = { version = "0.5.13", path = "./substreams-macro" }
pest= "2.7.10"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pest= "2.7.10"
pest = "2.7.10"

Comment on lines 27 to 29
pest= "2.7.10"
pest_derive = "2.7.10"
rstest = "0.19.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

Pull from the workspace dependencies set

Suggested change
pest= "2.7.10"
pest_derive = "2.7.10"
rstest = "0.19.0"
pest = { workspace = true }
pest_derive = { workspace = true }
rstest = { workspace = true }

Comment on lines 38 to 39
#[case(TEST_KEYS, "test1 && test2 ", true)]
#[case(TEST_KEYS, "test1 && test6", false)]
Copy link
Contributor

Choose a reason for hiding this comment

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

How do you validate that this is parsed correctly? It parses ok, but the resulting expression should be checked.

In the Go version, I created a string representation of the tree structure, so it can be verified too.

use pest::{iterators::Pair, Parser};
use pest_derive::Parser;

#[derive(Parser)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you check if it's possible to generate the code and commit it? It would be preferable leading to faster compiling for users since less time running the derive macro.

@@ -125,6 +125,7 @@ mod state;

pub mod key;
pub mod store;
pub mod parser;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like that it's called parser, parser doesn't say anything about what is being parsed.

Second, let's not publicly export the module, remove pub. We should only publicly expose the minimum we need for the library to be useful.

Comment on lines 8 to 48
pub fn evaluate_expression(keys: Vec<String>, input: &str) -> bool {
let successful_parse = EParser::parse(Rule::expression, input).unwrap();
return evaluate_rule(successful_parse.clone().into_iter().next().unwrap(), keys);
}

fn evaluate_rule(pair: Pair<Rule>, keys: Vec<String>) -> bool {
match pair.as_rule() {
Rule::expression => {
let inner_pair = pair.into_inner().next().unwrap();
return evaluate_rule(inner_pair, keys);
}
Rule::or => {
let mut result = false;
for inner_pair in pair.into_inner() {
result = result || evaluate_rule(inner_pair, keys.clone());
}
return result;
},
Rule::and => {
let mut result = true;
for inner_pair in pair.into_inner() {
result = result && evaluate_rule(inner_pair, keys.clone());
}
return result;
},
Rule::value => {
let inner_pair = pair.into_inner().next().unwrap();
return evaluate_rule(inner_pair, keys);
}
Rule::keyterm => {
return keys.contains(&pair.as_str().to_string());
}
Rule::singleQuoteKeyTerm => {
return keys.contains(&pair.as_str().to_string().replace("'", ""));
}
Rule::doubleQuoteKeyTerm => {
return keys.contains(&pair.as_str().to_string().replace("\"", ""));
}
_ => {panic!("Unexpected rule encountered")}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to remove all the unwrap here and the panic. Transform the method to return a Result<bool, anyhow::Error>, all error code path should have a .context attached to the error so we now where it came from.

#[grammar = "parser_rule.pest"]
pub struct EParser;

pub fn evaluate_expression(keys: Vec<String>, input: &str) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to receive a owned copy of a Vec<String>. We should accept variant of String like &str too. I would change to

Suggested change
pub fn evaluate_expression(keys: Vec<String>, input: &str) -> bool {
pub fn evaluate_expression<K: AsRef<str>, I: AsRef<str>>(keys: &[K], input: I) -> bool {

And this can be used now with different variations of String, &String and &str:

    evaluate_expression(&vec!["a", "b", "c"], "d");
    evaluate_expression(
        &vec!["a".to_string(), "a".to_string(), "a".to_string()],
        "d",
    );

    let keys = vec!["a".to_string(), "a".to_string(), "a".to_string()];
    let keys_ref = keys.iter().map(|x| x).collect::<Vec<&String>>();
    evaluate_expression(&keys_ref, "d");

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's also find a more meaningful name for this function, it will be the only one exposed IMO so we better find it a very good clear name.


pub fn evaluate_expression(keys: Vec<String>, input: &str) -> bool {
let successful_parse = EParser::parse(Rule::expression, input).unwrap();
return evaluate_rule(successful_parse.clone().into_iter().next().unwrap(), keys);
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's avoid all .clone() in your code, there is a good chance that we can remove all of them.

@ArnaudBger ArnaudBger merged commit 3ee1204 into develop May 8, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants