-
-
Notifications
You must be signed in to change notification settings - Fork 209
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
Make get
and set
arguments take identifiers instead of strings
#203
Make get
and set
arguments take identifiers instead of strings
#203
Conversation
aac0465
to
3fa7b16
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot, that's quite the refactoring! 💪
Would it be possible to add 1-2 tests with new expressions that were previously unsupported? E.g. Vector2::new(2.5, -1.0)
or so. You can also extend existing tests.
bors try
pub fn handle_any(&mut self, key: &str) -> Option<Option<KvValue>> { | ||
self.map.remove(&ident(key)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Option<Option>
? Should rather be ParseResult
, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no way for this to fail. Updated the doc comments of these handle_*
funcs to clarify.
let errors = self | ||
.map | ||
.keys() | ||
.map(|ident| error(format!("unrecognized key `{ident}`"), ident)); | ||
Err(errors | ||
.reduce(|mut a, b| { | ||
a.combine(b); | ||
a | ||
}) | ||
.unwrap()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This repeats "unrecognized key"
N times in the output, no? Why this change, is it not harder to read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicer error spans, pointing to the key in question. The case of multiple unrecognized keys should be pretty rare I think.
godot-macros/src/util.rs
Outdated
struct ParserState<'a> { | ||
tokens: std::slice::Iter<'a, TokenTree>, | ||
prev: Option<&'a TokenTree>, | ||
curr: Option<&'a TokenTree>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typical abbreviation for "current" is "cur", see e.g. CurDir
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
godot-macros/src/util.rs
Outdated
fn parse_opt_value(&mut self) -> ParseResult<Option<KvValue>> { | ||
Ok(match self.curr { | ||
// End of input directly after a key | ||
None => None, | ||
// Comma following key | ||
Some(tt) if is_punct(tt, ',') => { | ||
self.next(); | ||
None | ||
} | ||
// Equals sign following key | ||
Some(tt) if is_punct(tt, '=') => { | ||
self.next(); | ||
Some(self.parse_value()?) | ||
} | ||
Some(tt) => { | ||
return bail("expected `=` or `)`", tt); | ||
} | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I repeatedly find myself confused about an all-surrounding Ok(...)
and wonder why returning Result
in the first place 😬
Could you avoid ?
inside Ok(...)
? Just split the expressions, like:
let result = match self.cur {
...
};
Ok(result)
That makes clear that only if you reach the last line, Ok
is being returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure!
godot-macros/src/util.rs
Outdated
impl PartialEq for KvValue { | ||
fn eq(&self, other: &Self) -> bool { | ||
let to_strings = |kv: &Self| { | ||
kv.tokens | ||
.iter() | ||
.map(ToString::to_string) | ||
.collect::<Vec<String>>() | ||
}; | ||
to_strings(self) == to_strings(other) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very expensive comparison:
- allocate N + M strings, for each token on either side
- copy all strings into two vectors
Where is this needed? Is there no way to compare tokens directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, why PartialEq
and not also Eq
, is there no total equality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither TokenTree
nor TokenStream
implements even PartialEq
unfortunately.
Note that this is in a #[cfg(test)]
block. But I'll move it outside to make it more discoverable, annotate it with #[cfg(test)]
, and add a comment.
We don't need Eq
, whether it's semantically correct or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. But why move it outside if it's only used inside #[cfg(test)]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to have all trait implementations for a type together in the file, I think. Let me know if you'd prefer it to be moved back, I don't care much either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, but in this case it probably makes sense to have "test-only code" together -- since it's not really a trait that can be used elsewhere with the type.
godot-macros/src/util.rs
Outdated
Some(self.parse_value()?) | ||
} | ||
Some(tt) => { | ||
return bail("expected `=` or `)`", tt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message is a bit less expressive than before:
- maybe mention that
=
or)
are expected after a key - also,
,
is a possible token after a key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some extra code to give a more helpful error in case the preceding expression was nontrivial (more than one token tree). Edit: also reworded the error message to be less about tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for clarification 👍
godot-macros/src/util.rs
Outdated
( | ||
$($key:expr => $value:expr),* | ||
$(,)? | ||
) => { | ||
{ | ||
let mut map = std::collections::HashMap::new(); | ||
$( | ||
map.insert($key, $value); | ||
map.insert(ident($key), $value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice change! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do even better: since the keys are always identifiers, we don't even need the quotes.
tryBuild succeeded: |
Sure thing, added a couple of tests and rearranged the existing ones for better coverage. |
All done in a new commit. Let me know when it's ready to squash. |
godot-macros/src/util.rs
Outdated
impl PartialEq for KvValue { | ||
fn eq(&self, other: &Self) -> bool { | ||
let to_strings = |kv: &Self| { | ||
kv.tokens | ||
.iter() | ||
.map(ToString::to_string) | ||
.collect::<Vec<String>>() | ||
}; | ||
to_strings(self) == to_strings(other) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, but in this case it probably makes sense to have "test-only code" together -- since it's not really a trait that can be used elsewhere with the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c0a80b3
to
14682fe
Compare
Refactors `KvParser` to accept some expressions, as long as they don't contain a comma at the top level outside any pair of `[]`, `()` or `{}`. Also tightens up spans in error messages quite a bit.
14682fe
to
1b32155
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot!
bors r+
Build succeeded: |
Refactors
KvParser
to accept some expressions, as long as they don'tcontain a comma at the top level outside any pair of
[]
,()
or{}
.Also tightens up spans in error messages quite a bit.
Prelude for #199.