diff options
Diffstat (limited to 'crates/ra_parser')
-rw-r--r-- | crates/ra_parser/src/grammar.rs | 11 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/expressions.rs | 42 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/expressions/atom.rs | 74 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/items.rs | 2 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/type_args.rs | 2 | ||||
-rw-r--r-- | crates/ra_parser/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_parser/src/parser.rs | 2 | ||||
-rw-r--r-- | crates/ra_parser/src/syntax_kind/generated.rs | 2 |
8 files changed, 73 insertions, 66 deletions
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index c2a6e82e9..293baecf6 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs | |||
@@ -18,9 +18,10 @@ | |||
18 | //! // fn foo() {} | 18 | //! // fn foo() {} |
19 | //! ``` | 19 | //! ``` |
20 | //! | 20 | //! |
21 | //! After adding a new inline-test, run `cargo collect-tests` to extract | 21 | //! After adding a new inline-test, run `cargo xtask codegen` to |
22 | //! it as a standalone text-fixture into `tests/data/parser/inline`, and | 22 | //! extract it as a standalone text-fixture into |
23 | //! run `cargo test` once to create the "gold" value. | 23 | //! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to |
24 | //! create the "gold" value. | ||
24 | //! | 25 | //! |
25 | //! Coding convention: rules like `where_clause` always produce either a | 26 | //! Coding convention: rules like `where_clause` always produce either a |
26 | //! node or an error, rules like `opt_where_clause` may produce nothing. | 27 | //! node or an error, rules like `opt_where_clause` may produce nothing. |
@@ -54,7 +55,7 @@ pub(crate) mod fragments { | |||
54 | use super::*; | 55 | use super::*; |
55 | 56 | ||
56 | pub(crate) use super::{ | 57 | pub(crate) use super::{ |
57 | expressions::block, paths::type_path as path, patterns::pattern, types::type_, | 58 | expressions::block_expr, paths::type_path as path, patterns::pattern, types::type_, |
58 | }; | 59 | }; |
59 | 60 | ||
60 | pub(crate) fn expr(p: &mut Parser) { | 61 | pub(crate) fn expr(p: &mut Parser) { |
@@ -143,7 +144,7 @@ pub(crate) fn reparser( | |||
143 | parent: Option<SyntaxKind>, | 144 | parent: Option<SyntaxKind>, |
144 | ) -> Option<fn(&mut Parser)> { | 145 | ) -> Option<fn(&mut Parser)> { |
145 | let res = match node { | 146 | let res = match node { |
146 | BLOCK => expressions::naked_block, | 147 | BLOCK_EXPR => expressions::block_expr, |
147 | RECORD_FIELD_DEF_LIST => items::record_field_def_list, | 148 | RECORD_FIELD_DEF_LIST => items::record_field_def_list, |
148 | RECORD_FIELD_LIST => items::record_field_list, | 149 | RECORD_FIELD_LIST => items::record_field_list, |
149 | ENUM_VARIANT_LIST => items::enum_variant_list, | 150 | ENUM_VARIANT_LIST => items::enum_variant_list, |
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index cb30b25a8..d6e8df32a 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | mod atom; | 3 | mod atom; |
4 | 4 | ||
5 | pub(crate) use self::atom::match_arm_list; | 5 | pub(crate) use self::atom::{block_expr, match_arm_list}; |
6 | pub(super) use self::atom::{literal, LITERAL_FIRST}; | 6 | pub(super) use self::atom::{literal, LITERAL_FIRST}; |
7 | use super::*; | 7 | use super::*; |
8 | 8 | ||
@@ -49,28 +49,6 @@ fn expr_no_struct(p: &mut Parser) { | |||
49 | expr_bp(p, r, 1); | 49 | expr_bp(p, r, 1); |
50 | } | 50 | } |
51 | 51 | ||
52 | // test block | ||
53 | // fn a() {} | ||
54 | // fn b() { let _ = 1; } | ||
55 | // fn c() { 1; 2; } | ||
56 | // fn d() { 1; 2 } | ||
57 | pub(crate) fn block(p: &mut Parser) { | ||
58 | if !p.at(T!['{']) { | ||
59 | p.error("expected a block"); | ||
60 | return; | ||
61 | } | ||
62 | atom::block_expr(p, None); | ||
63 | } | ||
64 | |||
65 | pub(crate) fn naked_block(p: &mut Parser) { | ||
66 | assert!(p.at(T!['{'])); | ||
67 | let m = p.start(); | ||
68 | p.bump(T!['{']); | ||
69 | expr_block_contents(p); | ||
70 | p.expect(T!['}']); | ||
71 | m.complete(p, BLOCK); | ||
72 | } | ||
73 | |||
74 | fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool { | 52 | fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool { |
75 | match kind { | 53 | match kind { |
76 | BIN_EXPR | RANGE_EXPR | IF_EXPR => false, | 54 | BIN_EXPR | RANGE_EXPR | IF_EXPR => false, |
@@ -197,7 +175,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { | |||
197 | } | 175 | } |
198 | } | 176 | } |
199 | 177 | ||
200 | pub(crate) fn expr_block_contents(p: &mut Parser) { | 178 | pub(super) fn expr_block_contents(p: &mut Parser) { |
201 | // This is checked by a validator | 179 | // This is checked by a validator |
202 | attributes::inner_attributes(p); | 180 | attributes::inner_attributes(p); |
203 | 181 | ||
@@ -347,13 +325,27 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> | |||
347 | let kind = match p.current() { | 325 | let kind = match p.current() { |
348 | // test ref_expr | 326 | // test ref_expr |
349 | // fn foo() { | 327 | // fn foo() { |
328 | // // reference operator | ||
350 | // let _ = &1; | 329 | // let _ = &1; |
351 | // let _ = &mut &f(); | 330 | // let _ = &mut &f(); |
331 | // let _ = &raw; | ||
332 | // let _ = &raw.0; | ||
333 | // // raw reference operator | ||
334 | // let _ = &raw mut foo; | ||
335 | // let _ = &raw const foo; | ||
352 | // } | 336 | // } |
353 | T![&] => { | 337 | T![&] => { |
354 | m = p.start(); | 338 | m = p.start(); |
355 | p.bump(T![&]); | 339 | p.bump(T![&]); |
356 | p.eat(T![mut]); | 340 | if p.at(IDENT) |
341 | && p.at_contextual_kw("raw") | ||
342 | && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const])) | ||
343 | { | ||
344 | p.bump_remap(T![raw]); | ||
345 | p.bump_any(); | ||
346 | } else { | ||
347 | p.eat(T![mut]); | ||
348 | } | ||
357 | REF_EXPR | 349 | REF_EXPR |
358 | } | 350 | } |
359 | // test unary_expr | 351 | // test unary_expr |
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index 76aa601cb..706a2f796 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs | |||
@@ -84,7 +84,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
84 | T![box] => box_expr(p, None), | 84 | T![box] => box_expr(p, None), |
85 | T![for] => for_expr(p, None), | 85 | T![for] => for_expr(p, None), |
86 | T![while] => while_expr(p, None), | 86 | T![while] => while_expr(p, None), |
87 | T![try] => try_expr(p, None), | 87 | T![try] => try_block_expr(p, None), |
88 | LIFETIME if la == T![:] => { | 88 | LIFETIME if la == T![:] => { |
89 | let m = p.start(); | 89 | let m = p.start(); |
90 | label(p); | 90 | label(p); |
@@ -92,7 +92,12 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
92 | T![loop] => loop_expr(p, Some(m)), | 92 | T![loop] => loop_expr(p, Some(m)), |
93 | T![for] => for_expr(p, Some(m)), | 93 | T![for] => for_expr(p, Some(m)), |
94 | T![while] => while_expr(p, Some(m)), | 94 | T![while] => while_expr(p, Some(m)), |
95 | T!['{'] => block_expr(p, Some(m)), | 95 | // test labeled_block |
96 | // fn f() { 'label: {}; } | ||
97 | T!['{'] => { | ||
98 | block_expr(p); | ||
99 | m.complete(p, EFFECT_EXPR) | ||
100 | } | ||
96 | _ => { | 101 | _ => { |
97 | // test_err misplaced_label_err | 102 | // test_err misplaced_label_err |
98 | // fn main() { | 103 | // fn main() { |
@@ -108,13 +113,17 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
108 | let m = p.start(); | 113 | let m = p.start(); |
109 | p.bump(T![async]); | 114 | p.bump(T![async]); |
110 | p.eat(T![move]); | 115 | p.eat(T![move]); |
111 | block_expr(p, Some(m)) | 116 | block_expr(p); |
117 | m.complete(p, EFFECT_EXPR) | ||
112 | } | 118 | } |
113 | T![match] => match_expr(p), | 119 | T![match] => match_expr(p), |
120 | // test unsafe_block | ||
121 | // fn f() { unsafe { } } | ||
114 | T![unsafe] if la == T!['{'] => { | 122 | T![unsafe] if la == T!['{'] => { |
115 | let m = p.start(); | 123 | let m = p.start(); |
116 | p.bump(T![unsafe]); | 124 | p.bump(T![unsafe]); |
117 | block_expr(p, Some(m)) | 125 | block_expr(p); |
126 | m.complete(p, EFFECT_EXPR) | ||
118 | } | 127 | } |
119 | T!['{'] => { | 128 | T!['{'] => { |
120 | // test for_range_from | 129 | // test for_range_from |
@@ -123,7 +132,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
123 | // break; | 132 | // break; |
124 | // } | 133 | // } |
125 | // } | 134 | // } |
126 | block_expr(p, None) | 135 | block_expr_unchecked(p) |
127 | } | 136 | } |
128 | T![return] => return_expr(p), | 137 | T![return] => return_expr(p), |
129 | T![continue] => continue_expr(p), | 138 | T![continue] => continue_expr(p), |
@@ -134,7 +143,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
134 | } | 143 | } |
135 | }; | 144 | }; |
136 | let blocklike = match done.kind() { | 145 | let blocklike = match done.kind() { |
137 | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR | TRY_EXPR => { | 146 | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR | EFFECT_EXPR => { |
138 | BlockLike::Block | 147 | BlockLike::Block |
139 | } | 148 | } |
140 | _ => BlockLike::NotBlock, | 149 | _ => BlockLike::NotBlock, |
@@ -231,13 +240,9 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker { | |||
231 | p.eat(T![move]); | 240 | p.eat(T![move]); |
232 | params::param_list_closure(p); | 241 | params::param_list_closure(p); |
233 | if opt_fn_ret_type(p) { | 242 | if opt_fn_ret_type(p) { |
234 | if p.at(T!['{']) { | 243 | // test lambda_ret_block |
235 | // test lambda_ret_block | 244 | // fn main() { || -> i32 { 92 }(); } |
236 | // fn main() { || -> i32 { 92 }(); } | 245 | block_expr(p); |
237 | block_expr(p, None); | ||
238 | } else { | ||
239 | p.error("expected `{`"); | ||
240 | } | ||
241 | } else { | 246 | } else { |
242 | if p.at_ts(EXPR_FIRST) { | 247 | if p.at_ts(EXPR_FIRST) { |
243 | expr(p); | 248 | expr(p); |
@@ -261,13 +266,13 @@ fn if_expr(p: &mut Parser) -> CompletedMarker { | |||
261 | let m = p.start(); | 266 | let m = p.start(); |
262 | p.bump(T![if]); | 267 | p.bump(T![if]); |
263 | cond(p); | 268 | cond(p); |
264 | block(p); | 269 | block_expr(p); |
265 | if p.at(T![else]) { | 270 | if p.at(T![else]) { |
266 | p.bump(T![else]); | 271 | p.bump(T![else]); |
267 | if p.at(T![if]) { | 272 | if p.at(T![if]) { |
268 | if_expr(p); | 273 | if_expr(p); |
269 | } else { | 274 | } else { |
270 | block(p); | 275 | block_expr(p); |
271 | } | 276 | } |
272 | } | 277 | } |
273 | m.complete(p, IF_EXPR) | 278 | m.complete(p, IF_EXPR) |
@@ -295,7 +300,7 @@ fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | |||
295 | assert!(p.at(T![loop])); | 300 | assert!(p.at(T![loop])); |
296 | let m = m.unwrap_or_else(|| p.start()); | 301 | let m = m.unwrap_or_else(|| p.start()); |
297 | p.bump(T![loop]); | 302 | p.bump(T![loop]); |
298 | block(p); | 303 | block_expr(p); |
299 | m.complete(p, LOOP_EXPR) | 304 | m.complete(p, LOOP_EXPR) |
300 | } | 305 | } |
301 | 306 | ||
@@ -310,7 +315,7 @@ fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | |||
310 | let m = m.unwrap_or_else(|| p.start()); | 315 | let m = m.unwrap_or_else(|| p.start()); |
311 | p.bump(T![while]); | 316 | p.bump(T![while]); |
312 | cond(p); | 317 | cond(p); |
313 | block(p); | 318 | block_expr(p); |
314 | m.complete(p, WHILE_EXPR) | 319 | m.complete(p, WHILE_EXPR) |
315 | } | 320 | } |
316 | 321 | ||
@@ -325,7 +330,7 @@ fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | |||
325 | patterns::pattern(p); | 330 | patterns::pattern(p); |
326 | p.expect(T![in]); | 331 | p.expect(T![in]); |
327 | expr_no_struct(p); | 332 | expr_no_struct(p); |
328 | block(p); | 333 | block_expr(p); |
329 | m.complete(p, FOR_EXPR) | 334 | m.complete(p, FOR_EXPR) |
330 | } | 335 | } |
331 | 336 | ||
@@ -458,16 +463,25 @@ fn match_guard(p: &mut Parser) -> CompletedMarker { | |||
458 | m.complete(p, MATCH_GUARD) | 463 | m.complete(p, MATCH_GUARD) |
459 | } | 464 | } |
460 | 465 | ||
461 | // test block_expr | 466 | // test block |
462 | // fn foo() { | 467 | // fn a() {} |
463 | // {}; | 468 | // fn b() { let _ = 1; } |
464 | // unsafe {}; | 469 | // fn c() { 1; 2; } |
465 | // 'label: {}; | 470 | // fn d() { 1; 2 } |
466 | // } | 471 | pub(crate) fn block_expr(p: &mut Parser) { |
467 | pub(super) fn block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | 472 | if !p.at(T!['{']) { |
473 | p.error("expected a block"); | ||
474 | return; | ||
475 | } | ||
476 | block_expr_unchecked(p); | ||
477 | } | ||
478 | |||
479 | fn block_expr_unchecked(p: &mut Parser) -> CompletedMarker { | ||
468 | assert!(p.at(T!['{'])); | 480 | assert!(p.at(T!['{'])); |
469 | let m = m.unwrap_or_else(|| p.start()); | 481 | let m = p.start(); |
470 | naked_block(p); | 482 | p.bump(T!['{']); |
483 | expr_block_contents(p); | ||
484 | p.expect(T!['}']); | ||
471 | m.complete(p, BLOCK_EXPR) | 485 | m.complete(p, BLOCK_EXPR) |
472 | } | 486 | } |
473 | 487 | ||
@@ -532,7 +546,7 @@ fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker { | |||
532 | // fn foo() { | 546 | // fn foo() { |
533 | // let _ = try {}; | 547 | // let _ = try {}; |
534 | // } | 548 | // } |
535 | fn try_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | 549 | fn try_block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { |
536 | assert!(p.at(T![try])); | 550 | assert!(p.at(T![try])); |
537 | let m = m.unwrap_or_else(|| p.start()); | 551 | let m = m.unwrap_or_else(|| p.start()); |
538 | // Special-case `try!` as macro. | 552 | // Special-case `try!` as macro. |
@@ -552,8 +566,8 @@ fn try_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | |||
552 | } | 566 | } |
553 | 567 | ||
554 | p.bump(T![try]); | 568 | p.bump(T![try]); |
555 | block(p); | 569 | block_expr(p); |
556 | m.complete(p, TRY_EXPR) | 570 | m.complete(p, EFFECT_EXPR) |
557 | } | 571 | } |
558 | 572 | ||
559 | // test box_expr | 573 | // test box_expr |
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 1503a8730..67a924de5 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs | |||
@@ -329,7 +329,7 @@ fn fn_def(p: &mut Parser) { | |||
329 | if p.at(T![;]) { | 329 | if p.at(T![;]) { |
330 | p.bump(T![;]); | 330 | p.bump(T![;]); |
331 | } else { | 331 | } else { |
332 | expressions::block(p) | 332 | expressions::block_expr(p) |
333 | } | 333 | } |
334 | } | 334 | } |
335 | 335 | ||
diff --git a/crates/ra_parser/src/grammar/type_args.rs b/crates/ra_parser/src/grammar/type_args.rs index 33d9973e9..2d61f9d80 100644 --- a/crates/ra_parser/src/grammar/type_args.rs +++ b/crates/ra_parser/src/grammar/type_args.rs | |||
@@ -48,7 +48,7 @@ fn type_arg(p: &mut Parser) { | |||
48 | m.complete(p, ASSOC_TYPE_ARG); | 48 | m.complete(p, ASSOC_TYPE_ARG); |
49 | } | 49 | } |
50 | T!['{'] => { | 50 | T!['{'] => { |
51 | expressions::block(p); | 51 | expressions::block_expr(p); |
52 | m.complete(p, CONST_ARG); | 52 | m.complete(p, CONST_ARG); |
53 | } | 53 | } |
54 | k if k.is_literal() => { | 54 | k if k.is_literal() => { |
diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index 652492c1e..eeb8ad66b 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs | |||
@@ -25,7 +25,7 @@ pub(crate) use token_set::TokenSet; | |||
25 | pub use syntax_kind::SyntaxKind; | 25 | pub use syntax_kind::SyntaxKind; |
26 | 26 | ||
27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
28 | pub struct ParseError(pub String); | 28 | pub struct ParseError(pub Box<String>); |
29 | 29 | ||
30 | /// `TokenSource` abstracts the source of the tokens parser operates on. | 30 | /// `TokenSource` abstracts the source of the tokens parser operates on. |
31 | /// | 31 | /// |
@@ -112,7 +112,7 @@ pub fn parse_fragment( | |||
112 | FragmentKind::Type => grammar::fragments::type_, | 112 | FragmentKind::Type => grammar::fragments::type_, |
113 | FragmentKind::Pattern => grammar::fragments::pattern, | 113 | FragmentKind::Pattern => grammar::fragments::pattern, |
114 | FragmentKind::Item => grammar::fragments::item, | 114 | FragmentKind::Item => grammar::fragments::item, |
115 | FragmentKind::Block => grammar::fragments::block, | 115 | FragmentKind::Block => grammar::fragments::block_expr, |
116 | FragmentKind::Visibility => grammar::fragments::opt_visibility, | 116 | FragmentKind::Visibility => grammar::fragments::opt_visibility, |
117 | FragmentKind::MetaItem => grammar::fragments::meta_item, | 117 | FragmentKind::MetaItem => grammar::fragments::meta_item, |
118 | FragmentKind::Statement => grammar::fragments::stmt, | 118 | FragmentKind::Statement => grammar::fragments::stmt, |
diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs index faa63d53f..4f59b0a23 100644 --- a/crates/ra_parser/src/parser.rs +++ b/crates/ra_parser/src/parser.rs | |||
@@ -192,7 +192,7 @@ impl<'t> Parser<'t> { | |||
192 | /// structured errors with spans and notes, like rustc | 192 | /// structured errors with spans and notes, like rustc |
193 | /// does. | 193 | /// does. |
194 | pub(crate) fn error<T: Into<String>>(&mut self, message: T) { | 194 | pub(crate) fn error<T: Into<String>>(&mut self, message: T) { |
195 | let msg = ParseError(message.into()); | 195 | let msg = ParseError(Box::new(message.into())); |
196 | self.push_event(Event::Error { msg }) | 196 | self.push_event(Event::Error { msg }) |
197 | } | 197 | } |
198 | 198 | ||
diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index ab727ed7e..e7404492a 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs | |||
@@ -191,6 +191,7 @@ pub enum SyntaxKind { | |||
191 | RECORD_LIT, | 191 | RECORD_LIT, |
192 | RECORD_FIELD_LIST, | 192 | RECORD_FIELD_LIST, |
193 | RECORD_FIELD, | 193 | RECORD_FIELD, |
194 | EFFECT_EXPR, | ||
194 | BOX_EXPR, | 195 | BOX_EXPR, |
195 | CALL_EXPR, | 196 | CALL_EXPR, |
196 | INDEX_EXPR, | 197 | INDEX_EXPR, |
@@ -203,7 +204,6 @@ pub enum SyntaxKind { | |||
203 | PREFIX_EXPR, | 204 | PREFIX_EXPR, |
204 | RANGE_EXPR, | 205 | RANGE_EXPR, |
205 | BIN_EXPR, | 206 | BIN_EXPR, |
206 | BLOCK, | ||
207 | EXTERN_BLOCK, | 207 | EXTERN_BLOCK, |
208 | EXTERN_ITEM_LIST, | 208 | EXTERN_ITEM_LIST, |
209 | ENUM_VARIANT, | 209 | ENUM_VARIANT, |