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

Implement is (not) defined feature #78

Merged
merged 6 commits into from
Jul 24, 2024

Conversation

GuillaumeGomez
Copy link
Contributor

Fixes #62.

I'm not super happy about how I implemented the parser part (the little "cheat") but found it easier to be done this way to keep the same logic as the other operators. But it forced to have some more error checking.

The other downside is that it makes the code to generate the if/else branches a bit messy. If you have ideas on how to make it look better by any chance?

@Kijewski
Copy link
Collaborator

Sorry, but I won't find the time to review before Friday/Saturday.

let (i, left) = Self::filtered(i, level)?;
// This is cheating: to make it easier, we expect `defined` to be provided afterward
// in order to keep the same code logic as the other calls.
let (i, right) = many0(pair(tuple((ws(tag("is ")), ws(opt(tag("not "))))), |i| {
Copy link
Collaborator

@Kijewski Kijewski Jul 15, 2024

Choose a reason for hiding this comment

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

let (i, rhs) = opt(preceded(
    ws(keyword("is")),
    opt(terminated(
        opt(keyword("not")),
        ws(keyword("defined")),
    )),
))(i)?;
let is_neg = match rhs {
    None => return Ok((i, lhs)),
    Some(None) => return Err("expected: (not) defined"),
    Some(Some(None)) => false,
    Some(Some(Some(_))) => true,
};

@GuillaumeGomez
Copy link
Contributor Author

Sorry, but I won't find the time to review before Friday/Saturday.

You say that and then provide suggestion right away. 😆

But in any case, it's not in a hurry. So please review when you have time and motivation to keep this project fun for you. :)

@GuillaumeGomez
Copy link
Contributor Author

Finally applied your suggestion for the parser. Looks much better like this, thanks!

@GuillaumeGomez
Copy link
Contributor Author

The other downside is that it makes the code to generate the if/else branches a bit messy. If you have ideas on how to make it look better by any chance?

Thinking some more about this, I think we can simply create a new very small AST for conditions which would simply remove the conditions (and their associated block) that won't be rendered and then the function can simply loop through them instead of the whole state machine. Gonna do that.

@GuillaumeGomez
Copy link
Contributor Author

Moved the logic out of the write_if method. Much closer to what it originally was.

@Kijewski
Copy link
Collaborator

Moved the logic out of the write_if method. Much closer to what it originally was.

Thank you, much easier to read! I give you a proper review soon-ish.

expr_prec_layer!(muldivmod, is_defined, alt((tag("*"), tag("/"), tag("%"))));

fn is_defined(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
let (_, level) = level.nest(i)?;
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 we can to without the nesting check in here. We only traverse to a lower level, not the same or a higher level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't we need that for Self::filtered(i, level)? just below?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, you can just pass the level as is. In all other cases we also only increment the recursion level on recursions. The code cannot parse an expression like y is defined is defined, so no nesting happens in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Removed this nesting.

Some(Some(Some(_))) => true,
};
match *lhs {
Self::Var(_) => {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please capture the variable name in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

i,
WithSpan::new(
if is_neg {
Self::IsNotDefined(Box::new(lhs))
Copy link
Collaborator

Choose a reason for hiding this comment

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

The use the variable name in here. No need for boxing. This makes the code a lot more simple, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was an incredible idea! Code got simplified by quite a lot! :D

@Kijewski
Copy link
Collaborator

Sorry for the iterative reviews! It's a bit hard for me to wrap my head around this feature.

@GuillaumeGomez GuillaumeGomez force-pushed the is-defined branch 2 times, most recently from 39e9f21 to edf870f Compare July 22, 2024 19:27
@GuillaumeGomez
Copy link
Contributor Author

Sorry for the iterative reviews! It's a bit hard for me to wrap my head around this feature.

It's fine. Don't hesitate if you need some clarifications or any help to understand the code (which, if not easy to understand, should be more documented! So don't hesitate to tell me what's unclear so I can add code comments 😃)

@Kijewski
Copy link
Collaborator

I pushed a commit to remove the line of unsafe code. I actually wanted to look if it was easily possible to remove it, and the best test was implementing it. :D

The code looks good, and after reading your comments properly, I understand it perfectly (I think). Great work!

@GuillaumeGomez
Copy link
Contributor Author

Fixed the merge conflict and applied your suggestion. :)

I pushed a commit to remove the line of unsafe code. I actually wanted to look if it was easily possible to remove it, and the best test was implementing it. :D

That's my first implementation. I removed it to reduce the number of lifetimes. If you prefer with references, then I'm fine with it. :)

The code looks good, and after reading your comments properly, I understand it perfectly (I think). Great work!

Awesome! \o/

Kijewski
Kijewski previously approved these changes Jul 24, 2024
Copy link
Collaborator

@Kijewski Kijewski left a comment

Choose a reason for hiding this comment

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

Thank you! Please revert the changes in the ui test nesting tests, then you can merge the PR.

@GuillaumeGomez
Copy link
Contributor Author

Reverted wrong nested ui tests changes.

@GuillaumeGomez
Copy link
Contributor Author

I'll let you merge (still need your approval to unlock merge buttons 😉 ).

Copy link
Collaborator

@Kijewski Kijewski left a comment

Choose a reason for hiding this comment

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

Thank you, well done!

@Kijewski Kijewski merged commit a7462ef into rinja-rs:master Jul 24, 2024
17 checks passed
@GuillaumeGomez GuillaumeGomez deleted the is-defined branch July 24, 2024 12:43
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.

Add support for is defined
2 participants