-
Notifications
You must be signed in to change notification settings - Fork 38
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 for easier test bootstrapping #70
Comments
We could implement |
Good idea! Those aren't generic in my program, but let me experiment and see if that'll work. |
Okay I got it working, but it requires a custom impl<S: PartialEq<O>, O> PartialEq<Expression<O>> for Expression<S> {
fn eq(&self, other: &Expression<O>) -> bool {
match (self, other) {
(Expression::StringLiteral(span), Expression::StringLiteral(other_span)) => {
span == other_span
}
(Expression::Path { span, path }, Expression::Path { span: other_span, path: other_path }) => {
span == other_span && path == other_path
}
(Expression::Negative { span, expr }, Expression::Negative { span: other_span, expr: other_expr }) => {
span == other_span && **expr == **other_expr
}
(Expression::Helper { span, name, args }, Expression::Helper { span: other_span, name: other_name, args: other_args }) => {
span == other_span && name == other_name && args == other_args
}
(Expression::LegacyHelper { span, name, args }, Expression::LegacyHelper { span: other_span, name: other_name, args: other_args }) => {
span == other_span && name == other_name && args == other_args
}
_ => false
}
}
} I also haven't been able to find a crate that provides a derive macro or anything to provide a better experience here, though I'm sure it's possible to create one. I still think this is a better approach than my suggestion (it's really nice not needing to wrap every string literal in |
This may be a dealbreaker though: Because assert_eq!(
path(sp("@value.c")),
Ok((
".c",
Expression::Path {
span: "@value",
path: vec![PathPart::Part("@value")]
}
))
); Which means you either have to split checking |
Hmm yeah. Did you consider writing a function that converts Or alternatively, don't use |
How would you go about writing a generic parser like that without specifying tons of trait bounds all over the place? I tried defining one trait which has all of the rest in |
Hmm yeah, you're right, I didn't consider you could need those. |
I ended up just doing this, and making my own |
Cool! Can I consider this issue solved? |
Well I was able to implement a solution that worked for me, but I think this is still a pain point and warrants further study. Also, if the default behavior of I'd suggest keeping this issue open as a reminder. |
I also had to write tests while trying to add nom_locate to an existing parser. I ended up with two traits to add (resp. remove) span information as needed for assertions: https://github.com/vtavernier/glsl/blob/aec07998d7882d92d8eb2d835379321056782aef/glsl/src/parse_tests.rs#L5-L107 /// Trait to add span information. ParseInput is a type alias for nom_locate::Span
trait Span<'c, 'd, 'e>: Sized {
fn span(self) -> crate::parsers::ParseInput<'c, 'd, 'e>;
}
impl<'c> Span<'c, 'static, 'static> for &'c str {
fn span(self) -> crate::parsers::ParseInput<'c, 'static, 'static> {
crate::parsers::ParseInput::new_extra(self, None)
}
}
// impls for other AST nodes
/// Trait to remove span information, to be implemented on syntax nodes
trait Unspan: Sized {
type Unspanned: Sized;
fn unspan(self) -> Self::Unspanned;
}
/// Simplest impl: nom_locate::Span back to str
impl<'c> Unspan for ParseInput<'c, '_, '_> {
type Unspanned = &'c str;
fn unspan(self) -> Self::Unspanned {
self.fragment()
}
}
// impls for Result, Option, etc.
/// Usage example:
assert_eq!(parser("test".span()).unspan(), Ok("test")) The rest of the test harness relies on the fact that AST nodes are wrapped in a Regardless of the approach chosen I think we still need to have a way to compare span information for tests when needed, so having PartialEq implemented for LocatedSpan "ignoring" the span information wouldn't be the best bet IMO. |
I came across this thread after I solved this (in yet a different way): struct EasySpan<T, X> {
input: LocatedSpan<T, X>,
}
impl<T, X> EasySpan<T, X>
where
LocatedSpan<T, X>: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
{
fn next(&mut self, count: usize) -> LocatedSpan<T, X> {
let (after, before) = self.input.take_split(count);
self.input = after;
before
}
}
#[test]
fn test_tokenise() {
let input = Span::new("A 1234");
let mut span = EasySpan { input };
let want = Tokens::new(&[
Token::new(TokenType::Word, span.next(1)),
Token::new(TokenType::Whitespace, span.next(2)),
Token::new(TokenType::Number, span.next(4)),
]);
let got = tokenise(input).expect("token parsing failed");
assert_eq!(got, want);
}
|
While implementing a recent parser, I came upon an annoyance: manually slicing spans when writing test cases was very tedious.
So I modified nom_locate with a feature that allows for much easier testing by ignoring location information during test. It works by utilizing the fact that
line
should never be zero in normal operation:This allows me to define a function to easily use in tests:
And use it in a test like so:
I've never run into a case where span location was an issue, since it's always automatically derived correctly. So testing it in tests is a lot of tedium for very little if any benefit.
Let me know if there's a way of doing this I'm missing.
The text was updated successfully, but these errors were encountered: