@@ -6,10 +6,10 @@ mod flatten_grammar;
66mod intern_symbols;
77mod process_inlines;
88
9- use super :: Error ;
9+ use super :: { rules :: Precedence , Error } ;
1010use std:: {
1111 cmp:: Ordering ,
12- collections:: { hash_map, HashMap } ,
12+ collections:: { hash_map, HashMap , HashSet } ,
1313 mem,
1414} ;
1515
@@ -57,7 +57,7 @@ pub(crate) fn prepare_grammar(
5757 InlinedProductionMap ,
5858 AliasMap ,
5959) > {
60- validate_precedence_orderings ( & input_grammar. precedence_orderings ) ?;
60+ validate_named_precedences ( input_grammar) ?;
6161
6262 let interned_grammar = intern_symbols ( input_grammar) ?;
6363 let ( syntax_grammar, lexical_grammar) = extract_tokens ( interned_grammar) ?;
@@ -69,12 +69,14 @@ pub(crate) fn prepare_grammar(
6969 Ok ( ( syntax_grammar, lexical_grammar, inlines, default_aliases) )
7070}
7171
72- /// Make sure that there are no conflicting orderings. For any two precedence
73- /// names `a` and `b`, if `a` comes before `b` in some list, then it cannot come
74- // *after* `b` in any list.
75- fn validate_precedence_orderings ( order_lists : & [ Vec < String > ] ) -> Result < ( ) > {
72+ /// Check that all of the named precedences used in the grammar are declared
73+ /// within the `precedences` lists, and also that there are no conflicting
74+ /// precedence orderings declared in those lists.
75+ fn validate_named_precedences ( grammar : & InputGrammar ) -> Result < ( ) > {
76+ // For any two precedence names `a` and `b`, if `a` comes before `b`
77+ // in some list, then it cannot come *after* `b` in any list.
7678 let mut pairs = HashMap :: new ( ) ;
77- for list in order_lists {
79+ for list in & grammar . precedence_orderings {
7880 for ( i, mut name1) in list. iter ( ) . enumerate ( ) {
7981 for mut name2 in list. iter ( ) . skip ( i + 1 ) {
8082 if name2 == name1 {
@@ -101,5 +103,128 @@ fn validate_precedence_orderings(order_lists: &[Vec<String>]) -> Result<()> {
101103 }
102104 }
103105 }
106+
107+ // Check that no rule contains a named precedence that is not present in
108+ // any of the `precedences` lists.
109+ fn validate ( rule_name : & str , rule : & Rule , names : & HashSet < & String > ) -> Result < ( ) > {
110+ match rule {
111+ Rule :: Repeat ( rule) => validate ( rule_name, rule, names) ,
112+ Rule :: Seq ( elements) | Rule :: Choice ( elements) => elements
113+ . iter ( )
114+ . map ( |e| validate ( rule_name, e, names) )
115+ . collect ( ) ,
116+ Rule :: Metadata { rule, params } => {
117+ if let Precedence :: Name ( n) = & params. precedence {
118+ if !names. contains ( n) {
119+ return Err ( Error :: new ( format ! (
120+ "Undeclared precedence '{}' in rule '{}'" ,
121+ n, rule_name
122+ ) ) ) ;
123+ }
124+ }
125+ validate ( rule_name, rule, names) ?;
126+ Ok ( ( ) )
127+ }
128+ _ => Ok ( ( ) ) ,
129+ }
130+ }
131+
132+ let precedence_names = grammar
133+ . precedence_orderings
134+ . iter ( )
135+ . flat_map ( |l| l. iter ( ) )
136+ . collect :: < HashSet < & String > > ( ) ;
137+ for variable in & grammar. variables {
138+ validate ( & variable. name , & variable. rule , & precedence_names) ?;
139+ }
140+
104141 Ok ( ( ) )
105142}
143+
144+ #[ cfg( test) ]
145+ mod tests {
146+ use super :: * ;
147+ use crate :: generate:: grammars:: { InputGrammar , Variable , VariableType } ;
148+
149+ #[ test]
150+ fn test_validate_named_precedences_with_undeclared_precedence ( ) {
151+ let grammar = InputGrammar {
152+ name : String :: new ( ) ,
153+ word_token : None ,
154+ extra_symbols : vec ! [ ] ,
155+ external_tokens : vec ! [ ] ,
156+ supertype_symbols : vec ! [ ] ,
157+ expected_conflicts : vec ! [ ] ,
158+ variables_to_inline : vec ! [ ] ,
159+ precedence_orderings : vec ! [
160+ vec![ "a" . to_string( ) , "b" . to_string( ) ] ,
161+ vec![ "b" . to_string( ) , "c" . to_string( ) , "d" . to_string( ) ] ,
162+ ] ,
163+ variables : vec ! [
164+ Variable {
165+ name: "v1" . to_string( ) ,
166+ kind: VariableType :: Named ,
167+ rule: Rule :: Seq ( vec![
168+ Rule :: prec_left( Precedence :: Name ( "b" . to_string( ) ) , Rule :: string( "w" ) ) ,
169+ Rule :: prec( Precedence :: Name ( "c" . to_string( ) ) , Rule :: string( "x" ) ) ,
170+ ] ) ,
171+ } ,
172+ Variable {
173+ name: "v2" . to_string( ) ,
174+ kind: VariableType :: Named ,
175+ rule: Rule :: repeat( Rule :: Choice ( vec![
176+ Rule :: prec_left( Precedence :: Name ( "omg" . to_string( ) ) , Rule :: string( "y" ) ) ,
177+ Rule :: prec( Precedence :: Name ( "c" . to_string( ) ) , Rule :: string( "z" ) ) ,
178+ ] ) ) ,
179+ } ,
180+ ] ,
181+ } ;
182+
183+ let result = validate_named_precedences ( & grammar) ;
184+ assert_eq ! (
185+ result. unwrap_err( ) . message( ) ,
186+ "Undeclared precedence 'omg' in rule 'v2'" ,
187+ ) ;
188+ }
189+
190+ #[ test]
191+ fn test_validate_named_precedences_with_conflicting_order ( ) {
192+ let grammar = InputGrammar {
193+ name : String :: new ( ) ,
194+ word_token : None ,
195+ extra_symbols : vec ! [ ] ,
196+ external_tokens : vec ! [ ] ,
197+ supertype_symbols : vec ! [ ] ,
198+ expected_conflicts : vec ! [ ] ,
199+ variables_to_inline : vec ! [ ] ,
200+ precedence_orderings : vec ! [
201+ vec![ "a" . to_string( ) , "b" . to_string( ) ] ,
202+ vec![ "b" . to_string( ) , "c" . to_string( ) , "a" . to_string( ) ] ,
203+ ] ,
204+ variables : vec ! [
205+ Variable {
206+ name: "v1" . to_string( ) ,
207+ kind: VariableType :: Named ,
208+ rule: Rule :: Seq ( vec![
209+ Rule :: prec_left( Precedence :: Name ( "b" . to_string( ) ) , Rule :: string( "w" ) ) ,
210+ Rule :: prec( Precedence :: Name ( "c" . to_string( ) ) , Rule :: string( "x" ) ) ,
211+ ] ) ,
212+ } ,
213+ Variable {
214+ name: "v2" . to_string( ) ,
215+ kind: VariableType :: Named ,
216+ rule: Rule :: repeat( Rule :: Choice ( vec![
217+ Rule :: prec_left( Precedence :: Name ( "a" . to_string( ) ) , Rule :: string( "y" ) ) ,
218+ Rule :: prec( Precedence :: Name ( "c" . to_string( ) ) , Rule :: string( "z" ) ) ,
219+ ] ) ) ,
220+ } ,
221+ ] ,
222+ } ;
223+
224+ let result = validate_named_precedences ( & grammar) ;
225+ assert_eq ! (
226+ result. unwrap_err( ) . message( ) ,
227+ "Conflicting orderings for precedences 'a' and 'b'" ,
228+ ) ;
229+ }
230+ }
0 commit comments