Skip to content

Add newline to the parsing grammar to resolve some bugs #56

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

jmbeck15
Copy link

@jmbeck15 jmbeck15 commented Aug 17, 2022

Summary

By adding newline (\n) to the grammar, this pull request will:

It may be easier to review by commit than in bulk.

Description

Newlines are special for GCode; they represent the end of the current command and the start of a new one. This commit makes newlines a first-class token, and parses with them under consideration.

How I tested

I added some tests, un-ignored or fixed other tests, and executed cargo test.

I also ran a bunch of gcode files through the parser as a sanity check. In retrospect, I should have created tests from them, so I may go back and do that if I need to make further changes.

Fix skip_whitespace test and add respect_newlines test.
Removed part of the test that was simply invalid.
Tweaked it to return the expected newline character.
@jmbeck15 jmbeck15 force-pushed the fix-newline-detection branch from 8d7dfa8 to c11ad15 Compare August 17, 2022 13:26
Both fail, and are the basis for a new bug report.
@jmbeck15 jmbeck15 force-pushed the fix-newline-detection branch from c11ad15 to 2107449 Compare August 17, 2022 13:34
@jmbeck15 jmbeck15 marked this pull request as ready for review August 17, 2022 13:36
Copy link
Owner

@Michael-F-Bryan Michael-F-Bryan left a comment

Choose a reason for hiding this comment

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

Thanks for making this PR @jmbeck15! I think adding newlines as an explicit token in the language totally makes sense.

Overall I'm pretty happy with the PR, but there were a couple small questions I'd like to get your thoughts on.

Comment on lines +181 to +196
fn tokenize_newline(&mut self) -> Option<Token<'input>> {
let start = self.current_position;
let line = self.current_line;
let value = "\n";
self.current_position += 1;
self.current_line += 1;
Some(Token {
kind: TokenType::Newline,
value,
span: Span {
start,
line,
end: start + 1,
},
})
}
Copy link
Owner

Choose a reason for hiding this comment

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

If the only way this function can be called is when we're looking at a \n character, would it be enough to add something like debug_assert!(self.rest().starts_with("\n")) and return a Token<'input>?

Otherwise, we should actually check for the newline character and return None if it isn't found.

Either way, I think we should try to use the newline characters from the original stream instead of writing let value = "\n".

Copy link
Author

Choose a reason for hiding this comment

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

I think what you're getting at is "this code has a weird smell", and I think you're right. 😁 But I'm honestly not sure how to construct this better. If we don't explicitly set value = "\n", we'd have to chomp() or somehow otherwise pull the newline from the string, which is a computational expense that seems superfluous.

As for your suggestion

would it be enough to add something like debug_assert!(self.rest().starts_with("\n")) and return a Token<'input>

I don't think I understand. The debug_assert is something I could add; seems reasonable. But how would you return the Token<'input>?

@@ -371,4 +404,44 @@ mod tests {

assert_eq!(got.value, "+3.14");
}

#[test]
fn two_multi() {
Copy link
Owner

Choose a reason for hiding this comment

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

It would probably be more readable if we rewrite the test to be something like this:

let expected = vec![
  ("G", 0),
  ("0", 0),
  ("X", 0),
  ...
];

let actual: Vec<_> = Lexer::new("...")
  .map(|tok| (tok.value, tok.span.line))
  .collect();

assert_eq!(actual, expected);

Copy link
Author

Choose a reason for hiding this comment

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

For long g-code example tests, that's probably the way to go. Maybe we could create some helper methods that make testing easier. We're really missing these long-form g-code verification tests, so I took this one from the pull request @dr0ps made.

How about we keep this test as is for now, and rework these kinds of tests in the future?

@jmbeck15
Copy link
Author

@Michael-F-Bryan I pushed two more commits, one of which fixes #55. I was going to wait until after this pull request was merged to fix #55, but it turns out very much related to the newline handling.

If you prefer these fixes in separate pull requests, just let me know.

@jpursell
Copy link

jpursell commented May 5, 2024

Howdy! In case anyone is still out there, I tried using this library and ran into a bug. The library panicked on the gcode generated by DrawingBotV3-Free. Anyways, I am interested to know if this change would fix it. I'm hoping this message will motivate someone to resolve this merge request.

@jmbeck15
Copy link
Author

@jpursell if you send me some minimal example code that panics the library, I'll check it out. I'm curious.

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.

Duplicate arugments when a newline preceeds arguments without an explicit command There's a funny bug with the way newlines are handled
3 participants