-
Notifications
You must be signed in to change notification settings - Fork 35
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
Allow ?
and *
parameters to be omitted in tail position
#815
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1142,7 +1142,7 @@ impl<'top> TextBufferView<'top> { | |||||||||
Some(arg) => arg_expr_cache.push(arg), | ||||||||||
None => { | ||||||||||
for param in &signature_params[index..] { | ||||||||||
if param.rest_syntax_policy() == RestSyntaxPolicy::NotAllowed { | ||||||||||
if !param.can_be_omitted() { | ||||||||||
return fatal_parse_error( | ||||||||||
self, | ||||||||||
format!( | ||||||||||
|
@@ -1242,16 +1242,24 @@ impl<'top> TextBufferView<'top> { | |||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
pub fn match_empty_arg_group(self) -> IonMatchResult<'top> { | ||||||||||
recognize(pair(tag("(:"), whitespace_and_then(tag(")"))))(self) | ||||||||||
pub fn match_empty_arg_group( | ||||||||||
self, | ||||||||||
parameter: &'top Parameter, | ||||||||||
) -> IonParseResult<'top, EExpArg<'top, TextEncoding_1_1>> { | ||||||||||
recognize(pair(tag("(:"), whitespace_and_then(tag(")")))) | ||||||||||
.map(|matched_expr| { | ||||||||||
let arg_group = TextEExpArgGroup::new(parameter, matched_expr, &[]); | ||||||||||
EExpArg::new(parameter, EExpArgExpr::ArgGroup(arg_group)) | ||||||||||
}) | ||||||||||
.parse(self) | ||||||||||
} | ||||||||||
|
||||||||||
pub fn match_zero_or_one( | ||||||||||
self, | ||||||||||
parameter: &'top Parameter, | ||||||||||
) -> IonParseResult<'top, Option<EExpArg<'top, TextEncoding_1_1>>> { | ||||||||||
whitespace_and_then(alt(( | ||||||||||
Self::match_empty_arg_group.map(|_| None), | ||||||||||
Self::parser_with_arg(Self::match_empty_arg_group, parameter).map(Some), | ||||||||||
// TODO: Match a non-empty arg group and turn it into a failure with a helpful error message | ||||||||||
Self::match_sexp_value_1_1.map(|maybe_expr| { | ||||||||||
maybe_expr.map(|expr| { | ||||||||||
|
@@ -1285,7 +1293,7 @@ impl<'top> TextBufferView<'top> { | |||||||||
self, | ||||||||||
parameter: &'top Parameter, | ||||||||||
) -> IonParseResult<'top, Option<EExpArg<'top, TextEncoding_1_1>>> { | ||||||||||
if self.match_empty_arg_group().is_ok() { | ||||||||||
if self.match_empty_arg_group(parameter).is_ok() { | ||||||||||
return Err(nom::Err::Failure(IonParseError::Invalid( | ||||||||||
InvalidInputError::new(self).with_description(format!( | ||||||||||
"parameter '{}' is one-or-more (`+`) and cannot accept an empty stream", | ||||||||||
|
@@ -2660,8 +2668,8 @@ mod tests { | |||||||||
} | ||||||||||
|
||||||||||
macro_rules! matcher_tests_with_macro { | ||||||||||
($parser:ident $macro_src:literal $($expect:ident: [$($input:literal),+$(,)?]),+$(,)?) => { | ||||||||||
mod $parser { | ||||||||||
($mod_name:ident $parser:ident $macro_src:literal $($expect:ident: [$($input:literal),+$(,)?]),+$(,)?) => { | ||||||||||
mod $mod_name { | ||||||||||
Comment on lines
-2663
to
+2672
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗺️ This macro creates a module with a bunch of nested unit tests. However, the module name was also the name of the parser. This meant you couldn't write two sets of unit tests that used the same parser entry point. I added another parameter ( |
||||||||||
use super::*; | ||||||||||
$( | ||||||||||
#[test] | ||||||||||
|
@@ -2967,6 +2975,7 @@ mod tests { | |||||||||
} | ||||||||||
|
||||||||||
matcher_tests_with_macro! { | ||||||||||
parsing_sexps | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗺️ In the next few tests, I added a distinct module name. |
||||||||||
match_sexp_1_1 | ||||||||||
"(macro foo (x*) null)" | ||||||||||
expect_match: [ | ||||||||||
|
@@ -3001,6 +3010,7 @@ mod tests { | |||||||||
} | ||||||||||
|
||||||||||
matcher_tests_with_macro! { | ||||||||||
parsing_lists | ||||||||||
match_list_1_1 | ||||||||||
"(macro foo (x*) null)" | ||||||||||
expect_match: [ | ||||||||||
|
@@ -3015,6 +3025,7 @@ mod tests { | |||||||||
} | ||||||||||
|
||||||||||
matcher_tests_with_macro! { | ||||||||||
parsing_eexps | ||||||||||
match_e_expression | ||||||||||
"(macro foo (x*) null)" | ||||||||||
expect_match: [ | ||||||||||
|
@@ -3046,6 +3057,25 @@ mod tests { | |||||||||
] | ||||||||||
} | ||||||||||
|
||||||||||
matcher_tests_with_macro! { | ||||||||||
allow_omitting_trailing_optionals | ||||||||||
match_e_expression | ||||||||||
"(macro foo (a b+ c? d*) null)" | ||||||||||
expect_match: [ | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth adding an expectation that the explicit empty argument group |
||||||||||
"(:foo 1 2)", | ||||||||||
"(:foo 1 2 3)", | ||||||||||
"(:foo 1 2 3 4)", | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
...unless these are already covered elsewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The text reader doesn't have non-empty arg group support yet. |
||||||||||
"(:foo 1 2 3 4 5 6)", // implicit rest | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
...unless these are already covered elsewhere. |
||||||||||
"(:foo 1 2 3 (:))", // explicit empty stream | ||||||||||
"(:foo 1 2 (:) 4)", | ||||||||||
"(:foo 1 2 (:) (:))", | ||||||||||
], | ||||||||||
expect_mismatch: [ | ||||||||||
"(:foo 1)", | ||||||||||
"(:foo)", | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
...unless this is already covered elsewhere. |
||||||||||
] | ||||||||||
} | ||||||||||
|
||||||||||
#[rstest] | ||||||||||
#[case::empty("(:)")] | ||||||||||
#[case::empty_with_extra_spacing("(: )")] | ||||||||||
|
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.
🗺️ The original impl was just returning the matched slice. Now there's a concrete type that represents an argument group, so we instantiate one of those and return it.