@@ -51,6 +51,25 @@ pub fn parse_rules_top(input: TokenStream) -> Result<TokenStream> {
5151 } ;
5252 let input_abs = resolve_path ( & input_path. value ) ;
5353 let output_abs = resolve_path ( & output_path. value ) ;
54+ let output_yaml = std:: fs:: read_to_string ( & output_abs) . map_err ( |error| {
55+ syn:: Error :: new (
56+ output_path. literal_span ,
57+ format ! (
58+ "rules!: failed to read output schema `{}`: {error}" ,
59+ output_abs. display( )
60+ ) ,
61+ )
62+ } ) ?;
63+ let output_leaf_kinds = yeast_schema:: node_types_yaml:: leaf_kinds_from_yaml ( & output_yaml)
64+ . map_err ( |error| {
65+ syn:: Error :: new (
66+ output_path. literal_span ,
67+ format ! (
68+ "rules!: failed to parse output schema `{}`: {error}" ,
69+ output_abs. display( )
70+ ) ,
71+ )
72+ } ) ?;
5473
5574 let list = expect_group ( & mut tokens, proc_macro2:: Delimiter :: Bracket ) ?;
5675 if let Some ( token) = tokens. next ( ) {
@@ -63,8 +82,15 @@ pub fn parse_rules_top(input: TokenStream) -> Result<TokenStream> {
6382 let emitted_items = split_top_level_commas ( list. stream ( ) )
6483 . into_iter ( )
6584 . map ( |item| {
66- if has_top_level_arrow ( & item) {
67- Ok ( crate :: rule_parse:: parse_rule ( item) ?. lower ( ) )
85+ let rule_body = if has_top_level_arrow ( & item) {
86+ Some ( item. clone ( ) )
87+ } else {
88+ explicit_rule_body ( & item)
89+ } ;
90+ if let Some ( rule_body) = rule_body {
91+ let mut rule = crate :: rule_parse:: parse_rule ( rule_body) ?;
92+ rule. resolve_bare_leaf_content ( & output_leaf_kinds) ?;
93+ Ok ( rule. lower ( ) )
6894 } else {
6995 Ok ( item)
7096 }
@@ -84,6 +110,7 @@ pub fn parse_rules_top(input: TokenStream) -> Result<TokenStream> {
84110
85111struct NamedString {
86112 value : String ,
113+ literal_span : Span ,
87114}
88115
89116fn parse_named_string_arg (
@@ -109,7 +136,10 @@ fn parse_named_string_arg(
109136 format ! ( "`{expected_name}` must be a string literal path" ) ,
110137 )
111138 } ) ?;
112- Ok ( NamedString { value } )
139+ Ok ( NamedString {
140+ value,
141+ literal_span : literal. span ( ) ,
142+ } )
113143}
114144
115145fn string_literal_value ( literal : & proc_macro2:: Literal ) -> Option < String > {
@@ -152,6 +182,17 @@ fn has_top_level_arrow(stream: &TokenStream) -> bool {
152182 } )
153183}
154184
185+ fn explicit_rule_body ( stream : & TokenStream ) -> Option < TokenStream > {
186+ let expression = syn:: parse2 :: < syn:: ExprMacro > ( stream. clone ( ) ) . ok ( ) ?;
187+ let is_rule = expression
188+ . mac
189+ . path
190+ . segments
191+ . last ( )
192+ . is_some_and ( |segment| segment. ident == "rule" ) ;
193+ is_rule. then_some ( expression. mac . tokens )
194+ }
195+
155196type Tokens = std:: iter:: Peekable < proc_macro2:: token_stream:: IntoIter > ;
156197
157198fn expect_ident ( tokens : & mut Tokens , message : & str ) -> Result < syn:: Ident > {
@@ -212,4 +253,12 @@ mod tests {
212253 & quote! { { match x { 1 => 2 , _ => 3 } } }
213254 ) ) ;
214255 }
256+
257+ #[ test]
258+ fn recognizes_explicit_rule_macros ( ) {
259+ assert ! ( explicit_rule_body( & quote! { rule!( ( a) => ( b) ) } ) . is_some( ) ) ;
260+ assert ! ( explicit_rule_body( & quote! { yeast:: rule!( ( a) => ( b) ) } ) . is_some( ) ) ;
261+ assert ! ( explicit_rule_body( & quote! { other!( ( a) => ( b) ) } ) . is_none( ) ) ;
262+ assert ! ( explicit_rule_body( & quote! { make_rule( ) } ) . is_none( ) ) ;
263+ }
215264}
0 commit comments