Rust · 65778 bytes Raw Blame History
1 //! Declaration parser.
2 //!
3 //! Parses type declarations, USE statements, IMPLICIT, derived type
4 //! definitions, and legacy declaration forms (COMMON, DATA, etc.).
5
6 use super::{ParseError, Parser};
7 use crate::ast::decl::*;
8 use crate::ast::Spanned;
9 use crate::lexer::TokenKind;
10
11 impl<'a> Parser<'a> {
12 // ---- Type specifier parsing ----
13
14 /// Try to parse a type specifier. Returns None if current token isn't a type keyword.
15 pub fn try_parse_type_spec(&mut self) -> Option<Result<TypeSpec, ParseError>> {
16 let text = self.peek_text().to_lowercase();
17 match text.as_str() {
18 "integer" => {
19 self.advance();
20 Some(self.parse_kind_selector().map(TypeSpec::Integer))
21 }
22 "real" => {
23 self.advance();
24 Some(self.parse_kind_selector().map(TypeSpec::Real))
25 }
26 "doubleprecision" | "double" => {
27 self.advance();
28 // Handle "double precision" / "double complex" as two tokens.
29 if self.peek_text().eq_ignore_ascii_case("precision") {
30 self.advance();
31 Some(Ok(TypeSpec::DoublePrecision))
32 } else if self.peek_text().eq_ignore_ascii_case("complex") {
33 self.advance();
34 Some(Ok(TypeSpec::DoubleComplex))
35 } else {
36 Some(Ok(TypeSpec::DoublePrecision))
37 }
38 }
39 "complex" => {
40 self.advance();
41 Some(self.parse_kind_selector().map(TypeSpec::Complex))
42 }
43 "doublecomplex" => {
44 self.advance();
45 Some(Ok(TypeSpec::DoubleComplex))
46 }
47 "logical" => {
48 self.advance();
49 Some(self.parse_kind_selector().map(TypeSpec::Logical))
50 }
51 "character" => {
52 self.advance();
53 Some(self.parse_char_selector().map(TypeSpec::Character))
54 }
55 "type" => {
56 // type(name) is a type specifier, but type :: name is a derived type definition.
57 // Only consume if followed by (.
58 let next_pos = self.pos + 1;
59 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::LParen {
60 self.advance();
61 Some(self.parse_type_or_class_spec(false))
62 } else {
63 None // Not a type specifier — could be a derived type def.
64 }
65 }
66 "class" => {
67 self.advance();
68 Some(self.parse_type_or_class_spec(true))
69 }
70 _ => None,
71 }
72 }
73
74 /// Parse a type specifier for IMPLICIT — without consuming a kind selector,
75 /// since the parenthesized part is the letter range, not a kind.
76 fn parse_implicit_type_spec(&mut self) -> Option<Result<TypeSpec, ParseError>> {
77 let text = self.peek_text().to_lowercase();
78 match text.as_str() {
79 "integer" => {
80 self.advance();
81 Some(Ok(TypeSpec::Integer(None)))
82 }
83 "real" => {
84 self.advance();
85 Some(Ok(TypeSpec::Real(None)))
86 }
87 "doubleprecision" | "double" => {
88 self.advance();
89 if self.peek_text().eq_ignore_ascii_case("precision") {
90 self.advance();
91 Some(Ok(TypeSpec::DoublePrecision))
92 } else if self.peek_text().eq_ignore_ascii_case("complex") {
93 self.advance();
94 Some(Ok(TypeSpec::DoubleComplex))
95 } else {
96 Some(Ok(TypeSpec::DoublePrecision))
97 }
98 }
99 "complex" => {
100 self.advance();
101 Some(Ok(TypeSpec::Complex(None)))
102 }
103 "logical" => {
104 self.advance();
105 Some(Ok(TypeSpec::Logical(None)))
106 }
107 "character" => {
108 self.advance();
109 Some(Ok(TypeSpec::Character(None)))
110 }
111 _ => None,
112 }
113 }
114
115 fn parse_kind_selector(&mut self) -> Result<Option<KindSelector>, ParseError> {
116 // Check for *N (old-style)
117 if self.eat(&TokenKind::Star) {
118 let expr = self.parse_expr()?;
119 return Ok(Some(KindSelector::Star(expr)));
120 }
121 // Check for (kind=N) or (N)
122 if self.peek() != &TokenKind::LParen {
123 return Ok(None);
124 }
125 self.advance(); // (
126 // Check for kind= keyword
127 if self.peek_text().eq_ignore_ascii_case("kind") {
128 let next_pos = self.pos + 1;
129 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::Assign {
130 self.advance(); // kind
131 self.advance(); // =
132 }
133 }
134 let expr = self.parse_expr()?;
135 self.expect(&TokenKind::RParen)?;
136 Ok(Some(KindSelector::Expr(expr)))
137 }
138
139 fn parse_char_selector(&mut self) -> Result<Option<CharSelector>, ParseError> {
140 // Check for *N (old-style)
141 if self.eat(&TokenKind::Star) {
142 let len = self.parse_len_spec()?;
143 return Ok(Some(CharSelector {
144 len: Some(len),
145 kind: None,
146 }));
147 }
148 if self.peek() != &TokenKind::LParen {
149 return Ok(None);
150 }
151 self.advance(); // (
152
153 let mut len = None;
154 let mut kind = None;
155
156 // Parse len and/or kind parameters.
157 if self.peek_text().eq_ignore_ascii_case("len") {
158 let next_pos = self.pos + 1;
159 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::Assign {
160 self.advance(); // len
161 self.advance(); // =
162 len = Some(self.parse_len_spec()?);
163 } else {
164 // Just a number — treat as len.
165 len = Some(self.parse_len_spec()?);
166 }
167 } else if self.peek_text().eq_ignore_ascii_case("kind") {
168 let next_pos = self.pos + 1;
169 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::Assign {
170 self.advance(); // kind
171 self.advance(); // =
172 kind = Some(self.parse_expr()?);
173 }
174 } else {
175 // Bare number or expression — treat as len.
176 len = Some(self.parse_len_spec()?);
177 }
178
179 // Check for comma and second parameter.
180 if self.eat(&TokenKind::Comma) {
181 if self.peek_text().eq_ignore_ascii_case("kind") {
182 let next_pos = self.pos + 1;
183 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::Assign {
184 self.advance(); // kind
185 self.advance(); // =
186 }
187 } else if self.peek_text().eq_ignore_ascii_case("len") {
188 let next_pos = self.pos + 1;
189 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::Assign {
190 self.advance(); // len
191 self.advance(); // =
192 len = Some(self.parse_len_spec()?);
193 self.expect(&TokenKind::RParen)?;
194 return Ok(Some(CharSelector { len, kind }));
195 }
196 }
197 if kind.is_none() {
198 kind = Some(self.parse_expr()?);
199 } else {
200 len = Some(self.parse_len_spec()?);
201 }
202 }
203
204 self.expect(&TokenKind::RParen)?;
205 Ok(Some(CharSelector { len, kind }))
206 }
207
208 fn parse_len_spec(&mut self) -> Result<LenSpec, ParseError> {
209 if self.eat(&TokenKind::Star) {
210 return Ok(LenSpec::Star);
211 }
212 if self.peek() == &TokenKind::Colon {
213 self.advance();
214 return Ok(LenSpec::Colon);
215 }
216 // F77 entity-decl character-length form: `name*(*)`, `name*(:)`,
217 // `name*(N)`. The leading `*` is consumed by the caller; here we
218 // see the parenthesized inner form. Per F2018 §C.6.1 these are
219 // the type-param-value alternatives `*`, `:`, and a scalar
220 // int-expr. The plain `parse_expr` can't accept the bare `*` or
221 // `:` form, so unwrap one level of parens before delegating.
222 if self.peek() == &TokenKind::LParen {
223 self.advance();
224 let inner = if self.eat(&TokenKind::Star) {
225 LenSpec::Star
226 } else if self.eat(&TokenKind::Colon) {
227 LenSpec::Colon
228 } else {
229 LenSpec::Expr(self.parse_expr()?)
230 };
231 self.expect(&TokenKind::RParen)?;
232 return Ok(inner);
233 }
234 let expr = self.parse_expr()?;
235 Ok(LenSpec::Expr(expr))
236 }
237
238 fn parse_type_or_class_spec(&mut self, is_class: bool) -> Result<TypeSpec, ParseError> {
239 self.expect(&TokenKind::LParen)?;
240 if self.eat(&TokenKind::Star) {
241 self.expect(&TokenKind::RParen)?;
242 return Ok(if is_class {
243 TypeSpec::ClassStar
244 } else {
245 TypeSpec::TypeStar
246 });
247 }
248 if self.peek() != &TokenKind::Identifier {
249 return Err(self.error(format!("expected type name, got {}", self.peek())));
250 }
251 let name_tok = self.advance().clone();
252 let name = name_tok.text;
253 self.expect(&TokenKind::RParen)?;
254 Ok(if is_class {
255 TypeSpec::Class(name)
256 } else {
257 TypeSpec::Type(name)
258 })
259 }
260
261 // ---- Attribute parsing ----
262
263 /// Try to parse a declaration attribute after a comma.
264 pub fn try_parse_attribute(&mut self) -> Option<Result<Attribute, ParseError>> {
265 let text = self.peek_text().to_lowercase();
266 match text.as_str() {
267 "allocatable" => {
268 self.advance();
269 Some(Ok(Attribute::Allocatable))
270 }
271 "pointer" => {
272 self.advance();
273 Some(Ok(Attribute::Pointer))
274 }
275 "target" => {
276 self.advance();
277 Some(Ok(Attribute::Target))
278 }
279 "optional" => {
280 self.advance();
281 Some(Ok(Attribute::Optional))
282 }
283 "save" => {
284 self.advance();
285 Some(Ok(Attribute::Save))
286 }
287 "parameter" => {
288 self.advance();
289 Some(Ok(Attribute::Parameter))
290 }
291 "value" => {
292 self.advance();
293 Some(Ok(Attribute::Value))
294 }
295 "volatile" => {
296 self.advance();
297 Some(Ok(Attribute::Volatile))
298 }
299 "asynchronous" => {
300 self.advance();
301 Some(Ok(Attribute::Asynchronous))
302 }
303 "protected" => {
304 self.advance();
305 Some(Ok(Attribute::Protected))
306 }
307 "contiguous" => {
308 self.advance();
309 Some(Ok(Attribute::Contiguous))
310 }
311 "external" => {
312 self.advance();
313 Some(Ok(Attribute::External))
314 }
315 "intrinsic" => {
316 self.advance();
317 Some(Ok(Attribute::Intrinsic))
318 }
319 "public" => {
320 self.advance();
321 Some(Ok(Attribute::Public))
322 }
323 "private" => {
324 self.advance();
325 Some(Ok(Attribute::Private))
326 }
327 "dimension" => {
328 self.advance();
329 Some(self.parse_dimension_spec().map(Attribute::Dimension))
330 }
331 "intent" => {
332 self.advance();
333 Some(self.parse_intent_spec().map(Attribute::Intent))
334 }
335 "bind" => {
336 self.advance();
337 Some(self.parse_bind_spec().map(Attribute::Bind))
338 }
339 _ => None,
340 }
341 }
342
343 fn parse_dimension_spec(&mut self) -> Result<Vec<ArraySpec>, ParseError> {
344 self.expect(&TokenKind::LParen)?;
345 let specs = self.parse_array_spec_list()?;
346 self.expect(&TokenKind::RParen)?;
347 Ok(specs)
348 }
349
350 fn parse_array_spec_list(&mut self) -> Result<Vec<ArraySpec>, ParseError> {
351 let mut specs = Vec::new();
352 loop {
353 specs.push(self.parse_one_array_spec()?);
354 if !self.eat(&TokenKind::Comma) {
355 break;
356 }
357 }
358 Ok(specs)
359 }
360
361 fn parse_one_array_spec(&mut self) -> Result<ArraySpec, ParseError> {
362 // Assumed rank: (..) — F2018. The lexer produces two Dot tokens or
363 // a DotOp. We check for consecutive dots.
364 if self.peek_text() == ".." || self.peek_text() == "." {
365 // Try consuming .. as assumed rank.
366 let save = self.pos;
367 if self.peek_text() == ".." {
368 self.advance();
369 return Ok(ArraySpec::AssumedRank);
370 }
371 self.pos = save;
372 }
373
374 // Bare colon (:) — either deferred shape (allocatable/pointer) or
375 // assumed shape (dummy argument). The distinction depends on context
376 // (allocatable attribute), so we emit Deferred and let sema reclassify
377 // to AssumedShape if needed.
378 if self.peek() == &TokenKind::Colon {
379 self.advance();
380 return Ok(ArraySpec::Deferred);
381 }
382
383 // Assumed size: (*)
384 if self.peek() == &TokenKind::Star {
385 self.advance();
386 return Ok(ArraySpec::AssumedSize { lower: None });
387 }
388
389 // Explicit or lower:upper
390 let first = self.parse_expr()?;
391 if self.eat(&TokenKind::Colon) {
392 // Could be lower:upper, lower:*, or lower:
393 if self.peek() == &TokenKind::Star {
394 self.advance();
395 return Ok(ArraySpec::AssumedSize { lower: Some(first) });
396 }
397 if matches!(self.peek(), TokenKind::Comma | TokenKind::RParen) {
398 return Ok(ArraySpec::AssumedShape { lower: Some(first) });
399 }
400 let upper = self.parse_expr()?;
401 return Ok(ArraySpec::Explicit {
402 lower: Some(first),
403 upper,
404 });
405 }
406
407 // Just an upper bound (lower is 1 implicitly).
408 Ok(ArraySpec::Explicit {
409 lower: None,
410 upper: first,
411 })
412 }
413
414 fn parse_intent_spec(&mut self) -> Result<Intent, ParseError> {
415 self.expect(&TokenKind::LParen)?;
416 let text = self.peek_text().to_lowercase();
417 let intent = match text.as_str() {
418 "in" => {
419 self.advance();
420 if self.peek_text().eq_ignore_ascii_case("out") {
421 self.advance();
422 Intent::InOut
423 } else {
424 Intent::In
425 }
426 }
427 "out" => {
428 self.advance();
429 Intent::Out
430 }
431 "inout" => {
432 self.advance();
433 Intent::InOut
434 }
435 _ => {
436 return Err(self.error(format!(
437 "expected intent specifier, got {}",
438 self.peek_text()
439 )))
440 }
441 };
442 self.expect(&TokenKind::RParen)?;
443 Ok(intent)
444 }
445
446 fn parse_bind_spec(&mut self) -> Result<Option<String>, ParseError> {
447 self.expect(&TokenKind::LParen)?;
448 self.expect_ident_kw("c")?;
449 let name = if self.eat(&TokenKind::Comma) {
450 if self.peek_text().eq_ignore_ascii_case("name") {
451 self.advance();
452 self.expect(&TokenKind::Assign)?;
453 let name_tok = self.advance().clone();
454 Some(name_tok.text)
455 } else {
456 None
457 }
458 } else {
459 None
460 };
461 self.expect(&TokenKind::RParen)?;
462 Ok(name)
463 }
464
465 fn expect_ident_kw(&mut self, name: &str) -> Result<(), ParseError> {
466 if self.peek_text().eq_ignore_ascii_case(name) {
467 self.advance();
468 Ok(())
469 } else {
470 Err(self.error(format!("expected '{}', got '{}'", name, self.peek_text())))
471 }
472 }
473
474 // ---- Type declaration parsing ----
475
476 /// Parse a type declaration statement:
477 /// `type-spec [, attr-list] :: entity-list`
478 /// or `type-spec entity-list` (old-style, no ::)
479 pub fn parse_type_decl(&mut self, type_spec: TypeSpec) -> Result<SpannedDecl, ParseError> {
480 let start = self.current_span();
481
482 // Parse optional attributes (comma-separated before ::).
483 let mut attrs = Vec::new();
484 while self.eat(&TokenKind::Comma) {
485 if let Some(attr_result) = self.try_parse_attribute() {
486 attrs.push(attr_result?);
487 } else {
488 break;
489 }
490 }
491
492 // Optional :: separator.
493 let _has_double_colon = self.eat(&TokenKind::ColonColon);
494
495 // Parse entity list.
496 let entities = self.parse_entity_list()?;
497
498 let span = crate::parser::expr::span_from_to(start, self.prev_span());
499 Ok(Spanned::new(
500 Decl::TypeDecl {
501 type_spec,
502 attrs,
503 entities,
504 },
505 span,
506 ))
507 }
508
509 fn parse_entity_list(&mut self) -> Result<Vec<EntityDecl>, ParseError> {
510 let mut entities = Vec::new();
511 loop {
512 entities.push(self.parse_entity_decl()?);
513 if !self.eat(&TokenKind::Comma) {
514 break;
515 }
516 }
517 Ok(entities)
518 }
519
520 fn parse_entity_decl(&mut self) -> Result<EntityDecl, ParseError> {
521 if self.peek() != &TokenKind::Identifier {
522 return Err(self.error(format!("expected entity name, got {}", self.peek())));
523 }
524 let name_tok = self.advance().clone();
525 let name = name_tok.text;
526
527 // Optional array spec on the entity: x(10), x(:,:)
528 let array_spec = if self.peek() == &TokenKind::LParen {
529 self.advance();
530 let specs = self.parse_array_spec_list()?;
531 self.expect(&TokenKind::RParen)?;
532 Some(specs)
533 } else {
534 None
535 };
536
537 if self.peek() == &TokenKind::LBracket {
538 return Err(
539 self.error("coarray declarations are recognized but not yet implemented".into())
540 );
541 }
542
543 // Optional character length: character :: name*20
544 let char_len = if self.eat(&TokenKind::Star) {
545 Some(self.parse_len_spec()?)
546 } else {
547 None
548 };
549
550 // Initialization: = expr
551 let init = if self.eat(&TokenKind::Assign) {
552 Some(self.parse_expr()?)
553 } else {
554 None
555 };
556
557 // Pointer initialization: => expr
558 let ptr_init = if self.eat(&TokenKind::Arrow) {
559 Some(self.parse_expr()?)
560 } else {
561 None
562 };
563
564 Ok(EntityDecl {
565 name,
566 array_spec,
567 char_len,
568 init,
569 ptr_init,
570 })
571 }
572
573 // ---- USE statement ----
574
575 pub fn parse_use_stmt(&mut self) -> Result<SpannedDecl, ParseError> {
576 let start = self.current_span();
577 // Already consumed 'use'.
578
579 // Handle: use :: mod, use, intrinsic :: mod, use, non_intrinsic :: mod
580 let mut nature = UseNature::Normal;
581
582 // F2003: use :: module_name (optional :: without nature)
583 if self.eat(&TokenKind::ColonColon) {
584 // Just :: with no nature — normal use with explicit ::
585 } else if self.eat(&TokenKind::Comma) {
586 let text = self.peek_text().to_lowercase();
587 if text == "intrinsic" {
588 self.advance();
589 nature = UseNature::Intrinsic;
590 self.expect(&TokenKind::ColonColon)?;
591 } else if text == "non_intrinsic" {
592 self.advance();
593 nature = UseNature::NonIntrinsic;
594 self.expect(&TokenKind::ColonColon)?;
595 }
596 }
597
598 let module = self.advance().clone().text;
599
600 let mut renames = Vec::new();
601 let mut only = None;
602
603 if self.eat(&TokenKind::Comma) {
604 if self.peek_text().eq_ignore_ascii_case("only") {
605 self.advance();
606 self.expect(&TokenKind::Colon)?;
607 only = Some(self.parse_only_list()?);
608 } else {
609 // Rename list: local => remote
610 renames = self.parse_rename_list()?;
611 }
612 }
613
614 let span = crate::parser::expr::span_from_to(start, self.prev_span());
615 Ok(Spanned::new(
616 Decl::UseStmt {
617 module,
618 nature,
619 renames,
620 only,
621 },
622 span,
623 ))
624 }
625
626 fn parse_only_list(&mut self) -> Result<Vec<OnlyItem>, ParseError> {
627 let mut items = Vec::new();
628 if self.at_stmt_end() {
629 return Ok(items);
630 }
631 loop {
632 let mut name = self.advance().clone().text;
633 let mut is_generic_spec = false;
634 if name.eq_ignore_ascii_case("operator") || name.eq_ignore_ascii_case("assignment") {
635 self.expect(&TokenKind::LParen)?;
636 let op = self.advance().clone().text;
637 self.expect(&TokenKind::RParen)?;
638 name = format!("{}({})", name, op);
639 is_generic_spec = true;
640 } else if (name.eq_ignore_ascii_case("read") || name.eq_ignore_ascii_case("write"))
641 && self.peek() == &TokenKind::LParen
642 {
643 // F2018 §12.6.4.8: defined-IO generic-spec — `read (formatted)`,
644 // `read (unformatted)`, `write (formatted)`, `write (unformatted)`.
645 // Stored as a single OnlyItem::Generic so module resolution can
646 // import the corresponding INTERFACE READ/WRITE binding.
647 self.advance();
648 let kind = self.advance().clone().text;
649 self.expect(&TokenKind::RParen)?;
650 name = format!(
651 "{}({})",
652 name.to_ascii_lowercase(),
653 kind.to_ascii_lowercase()
654 );
655 is_generic_spec = true;
656 }
657 if self.eat(&TokenKind::Arrow) {
658 let remote = self.advance().clone().text;
659 items.push(OnlyItem::Rename(Rename {
660 local: name,
661 remote,
662 }));
663 } else if is_generic_spec
664 || name.eq_ignore_ascii_case("operator(+)")
665 || name.eq_ignore_ascii_case("operator(-)")
666 || name.eq_ignore_ascii_case("operator(*)")
667 || name.eq_ignore_ascii_case("operator(/)")
668 || name.eq_ignore_ascii_case("operator(**)")
669 || name.eq_ignore_ascii_case("operator(//)")
670 || name.eq_ignore_ascii_case("operator(==)")
671 || name.eq_ignore_ascii_case("operator(/=)")
672 || name.eq_ignore_ascii_case("operator(<)")
673 || name.eq_ignore_ascii_case("operator(<=)")
674 || name.eq_ignore_ascii_case("operator(>)")
675 || name.eq_ignore_ascii_case("operator(>=)")
676 || name.eq_ignore_ascii_case("assignment(=)")
677 {
678 items.push(OnlyItem::Generic(name));
679 } else {
680 items.push(OnlyItem::Name(name));
681 }
682 if !self.eat(&TokenKind::Comma) {
683 break;
684 }
685 }
686 Ok(items)
687 }
688
689 fn parse_rename_list(&mut self) -> Result<Vec<Rename>, ParseError> {
690 let mut renames = Vec::new();
691 loop {
692 let local = self.advance().clone().text;
693 self.expect(&TokenKind::Arrow)?;
694 let remote = self.advance().clone().text;
695 renames.push(Rename { local, remote });
696 if !self.eat(&TokenKind::Comma) {
697 break;
698 }
699 }
700 Ok(renames)
701 }
702
703 // ---- IMPLICIT ----
704
705 pub fn parse_implicit(&mut self) -> Result<SpannedDecl, ParseError> {
706 let start = self.current_span();
707 // Already consumed 'implicit'.
708
709 if self.peek_text().eq_ignore_ascii_case("none") {
710 self.advance();
711 // Check for (type) or (external) or (type, external)
712 let mut type_ = true;
713 let mut external = false;
714 if self.peek() == &TokenKind::LParen {
715 self.advance();
716 type_ = false;
717 loop {
718 let spec = self.peek_text().to_lowercase();
719 match spec.as_str() {
720 "type" => {
721 self.advance();
722 type_ = true;
723 }
724 "external" => {
725 self.advance();
726 external = true;
727 }
728 _ => break,
729 }
730 if !self.eat(&TokenKind::Comma) {
731 break;
732 }
733 }
734 self.expect(&TokenKind::RParen)?;
735 }
736 let span = crate::parser::expr::span_from_to(start, self.prev_span());
737 return Ok(Spanned::new(Decl::ImplicitNone { external, type_ }, span));
738 }
739
740 // IMPLICIT type-spec (letter-range-list)
741 // Note: we parse the type keyword WITHOUT its kind selector, because
742 // the parenthesized part after the type keyword is the letter range,
743 // not a kind selector. E.g., "implicit integer (i-n)" — the (i-n)
744 // is a letter range, not kind=i-n.
745 let mut specs = Vec::new();
746 loop {
747 let type_spec = self
748 .parse_implicit_type_spec()
749 .ok_or_else(|| self.error("expected type specifier in IMPLICIT".into()))??;
750 self.expect(&TokenKind::LParen)?;
751 let mut ranges = Vec::new();
752 loop {
753 let start_letter = self.advance().clone().text.chars().next().unwrap_or('a');
754 self.expect(&TokenKind::Minus)?;
755 let end_letter = self.advance().clone().text.chars().next().unwrap_or('z');
756 ranges.push((start_letter, end_letter));
757 if !self.eat(&TokenKind::Comma) {
758 break;
759 }
760 }
761 self.expect(&TokenKind::RParen)?;
762 specs.push(ImplicitSpec { type_spec, ranges });
763 if !self.eat(&TokenKind::Comma) {
764 break;
765 }
766 }
767
768 let span = crate::parser::expr::span_from_to(start, self.prev_span());
769 Ok(Spanned::new(Decl::ImplicitStmt { specs }, span))
770 }
771
772 // ---- Derived type definition ----
773
774 pub fn parse_derived_type_def(&mut self) -> Result<SpannedDecl, ParseError> {
775 let start = self.current_span();
776 // Already consumed 'type'. Next could be :: or , attrs.
777
778 let mut attrs = Vec::new();
779
780 // Parse type attributes: abstract, bind(c), extends(parent), public, private
781 while self.eat(&TokenKind::Comma) {
782 let text = self.peek_text().to_lowercase();
783 match text.as_str() {
784 "abstract" => {
785 self.advance();
786 attrs.push(TypeAttr::Abstract);
787 }
788 "public" => {
789 self.advance();
790 attrs.push(TypeAttr::Public);
791 }
792 "private" => {
793 self.advance();
794 attrs.push(TypeAttr::Private);
795 }
796 "bind" => {
797 self.advance();
798 let name = self.parse_bind_spec()?;
799 attrs.push(TypeAttr::Bind(name));
800 }
801 "extends" => {
802 self.advance();
803 self.expect(&TokenKind::LParen)?;
804 let parent = self.advance().clone().text;
805 self.expect(&TokenKind::RParen)?;
806 attrs.push(TypeAttr::Extends(parent));
807 }
808 _ => break,
809 }
810 }
811
812 self.eat(&TokenKind::ColonColon);
813 let name = self.advance().clone().text;
814 self.skip_newlines();
815
816 // Parse components until 'contains' or 'end type'.
817 let mut components = Vec::new();
818 let mut type_bound_procs = Vec::new();
819 let mut final_procs = Vec::new();
820
821 loop {
822 self.skip_newlines();
823 let text = self.peek_text().to_lowercase();
824
825 if text == "contains" {
826 self.advance();
827 self.skip_newlines();
828 // Parse type-bound procedures until 'end type'.
829 loop {
830 self.skip_newlines();
831 let proc_text = self.peek_text().to_lowercase();
832 if proc_text == "end" {
833 break;
834 }
835 if proc_text == "endtype" {
836 break;
837 }
838
839 if proc_text == "procedure" {
840 self.advance();
841 let tbp = self.parse_type_bound_proc()?;
842 type_bound_procs.push(tbp);
843 } else if proc_text == "generic" {
844 self.advance();
845 let tbp = self.parse_type_bound_proc_generic()?;
846 type_bound_procs.push(tbp);
847 } else if proc_text == "final" {
848 self.advance();
849 self.eat(&TokenKind::ColonColon);
850 let name = self.advance().clone().text;
851 final_procs.push(name);
852 } else {
853 // Skip unknown lines in contains section.
854 while !self.at_stmt_end() {
855 self.advance();
856 }
857 }
858 self.skip_newlines();
859 }
860 break;
861 }
862
863 if text == "end" || text == "endtype" {
864 break;
865 }
866
867 // PROCEDURE(interface_name) [, attrs] :: name [=> null()]
868 // Procedure pointer components inside a derived type.
869 if text == "procedure" {
870 let next_pos = self.pos + 1;
871 if next_pos < self.tokens.len() && self.tokens[next_pos].kind == TokenKind::LParen {
872 let comp_start = self.current_span();
873 self.advance(); // consume 'procedure'
874 self.advance(); // consume '('
875 let iface_name = if self.peek() == &TokenKind::Identifier {
876 self.advance().clone().text
877 } else {
878 String::new()
879 };
880 self.expect(&TokenKind::RParen)?;
881
882 let mut comp_attrs = Vec::new();
883 while self.eat(&TokenKind::Comma) {
884 let attr_text = self.peek_text().to_lowercase();
885 match attr_text.as_str() {
886 "pointer" => {
887 self.advance();
888 comp_attrs.push(crate::ast::decl::Attribute::Pointer);
889 }
890 "nopass" | "pass" | "deferred" | "non_overridable" => {
891 self.advance();
892 }
893 _ => {
894 self.advance();
895 }
896 }
897 }
898
899 if self.peek() == &TokenKind::ColonColon {
900 self.advance();
901 }
902
903 let mut entities = Vec::new();
904 loop {
905 let entity_name = if self.peek() == &TokenKind::Identifier {
906 self.advance().clone().text
907 } else {
908 String::new()
909 };
910
911 // F2008 §4.5.4.5: a procedure pointer
912 // component may carry a default initial
913 // association `=> proc_name` or `=> null()`.
914 // Without capturing the right-hand side the
915 // pointer field stays uninitialized — calling
916 // `instance%fn(args)` then jumps through
917 // garbage memory. stdlib_hashmaps's
918 // `procedure(hasher_fun), pointer, nopass ::
919 // hasher => default_hasher` motivated this fix.
920 let mut ptr_init: Option<crate::ast::expr::SpannedExpr> = None;
921 if self.eat(&TokenKind::Arrow) {
922 let init_start = self.current_span();
923 if self.peek_text().eq_ignore_ascii_case("null") {
924 self.advance();
925 if self.peek() == &TokenKind::LParen {
926 self.advance();
927 let _ = self.expect(&TokenKind::RParen);
928 }
929 // Leave ptr_init as None for `=> null()`
930 // (matches the legacy behaviour, where
931 // the field is zero-initialised).
932 } else if self.peek() == &TokenKind::Identifier {
933 let target_name = self.advance().clone().text;
934 let span =
935 crate::parser::expr::span_from_to(init_start, self.prev_span());
936 ptr_init = Some(crate::ast::Spanned::new(
937 crate::ast::expr::Expr::Name { name: target_name },
938 span,
939 ));
940 }
941 }
942
943 entities.push(crate::ast::decl::EntityDecl {
944 name: entity_name,
945 array_spec: None,
946 init: None,
947 char_len: None,
948 ptr_init,
949 });
950
951 if !self.eat(&TokenKind::Comma) {
952 break;
953 }
954 }
955
956 comp_attrs.push(crate::ast::decl::Attribute::External);
957 let span = crate::parser::expr::span_from_to(comp_start, self.prev_span());
958 components.push(crate::ast::Spanned::new(
959 crate::ast::decl::Decl::TypeDecl {
960 type_spec: crate::ast::decl::TypeSpec::Type(iface_name),
961 attrs: comp_attrs,
962 entities,
963 },
964 span,
965 ));
966 continue;
967 }
968 }
969
970 // Try to parse a component declaration.
971 if let Some(ts_result) = self.try_parse_type_spec() {
972 let ts = ts_result?;
973 let comp = self.parse_type_decl(ts)?;
974 components.push(comp);
975 } else {
976 // Skip unrecognized lines.
977 while !self.at_stmt_end() {
978 self.advance();
979 }
980 self.skip_newlines();
981 }
982 }
983
984 // Consume 'end type [name]'.
985 if self.peek_text().eq_ignore_ascii_case("endtype") {
986 self.advance();
987 } else if self.peek_text().eq_ignore_ascii_case("end") {
988 self.advance();
989 self.eat_ident("type");
990 }
991 // Optional name after end type.
992 if self.peek() == &TokenKind::Identifier {
993 self.advance();
994 }
995
996 let extends = attrs.iter().find_map(|a| {
997 if let TypeAttr::Extends(ref p) = a {
998 Some(p.clone())
999 } else {
1000 None
1001 }
1002 });
1003
1004 let span = crate::parser::expr::span_from_to(start, self.prev_span());
1005 Ok(Spanned::new(
1006 Decl::DerivedTypeDef {
1007 name,
1008 extends,
1009 attrs,
1010 components,
1011 type_bound_procs,
1012 final_procs,
1013 },
1014 span,
1015 ))
1016 }
1017
1018 fn parse_type_bound_proc(&mut self) -> Result<TypeBoundProc, ParseError> {
1019 // procedure [(iface)] [, attrs] :: name [=> binding]
1020 let interface = if self.eat(&TokenKind::LParen) {
1021 let iface = self.advance().clone().text;
1022 self.expect(&TokenKind::RParen)?;
1023 Some(iface)
1024 } else {
1025 None
1026 };
1027 let mut proc_attrs = Vec::new();
1028 while self.eat(&TokenKind::Comma) {
1029 let text = self.peek_text().to_lowercase();
1030 match text.as_str() {
1031 "pass" => {
1032 proc_attrs.push(self.advance().clone().text);
1033 // Optional argument: pass(arg). Consume balanced parens
1034 // — we don't track which arg the pass is on; F2018 says
1035 // it defaults to the first dummy if not specified.
1036 if self.peek() == &TokenKind::LParen {
1037 let mut depth = 0;
1038 loop {
1039 match self.peek() {
1040 TokenKind::LParen => {
1041 self.advance();
1042 depth += 1;
1043 }
1044 TokenKind::RParen => {
1045 self.advance();
1046 depth -= 1;
1047 if depth == 0 {
1048 break;
1049 }
1050 }
1051 TokenKind::Eof => break,
1052 _ => {
1053 self.advance();
1054 }
1055 }
1056 }
1057 }
1058 }
1059 "nopass" | "deferred" | "non_overridable" | "public" | "private" => {
1060 proc_attrs.push(self.advance().clone().text);
1061 }
1062 _ => break,
1063 }
1064 }
1065 self.eat(&TokenKind::ColonColon);
1066 let name = self.advance().clone().text;
1067 let binding = if self.eat(&TokenKind::Arrow) {
1068 Some(self.advance().clone().text)
1069 } else {
1070 None
1071 };
1072 let bindings = binding.iter().cloned().collect();
1073 Ok(TypeBoundProc {
1074 name,
1075 interface,
1076 binding,
1077 bindings,
1078 attrs: proc_attrs,
1079 is_generic: false,
1080 })
1081 }
1082
1083 fn parse_type_bound_proc_generic(&mut self) -> Result<TypeBoundProc, ParseError> {
1084 // generic [, attrs] :: name => specific_name [, specific_name ...]
1085 // or generic [, attrs] :: operator(+) => specific_name
1086 // F2018 §4.5.5: access-spec (PUBLIC/PRIVATE) may appear after the
1087 // GENERIC keyword as a comma-separated attribute. The non-generic
1088 // parser already handles this for ordinary procedure bindings; the
1089 // generic parser was skipping it, so `generic, public :: name => ...`
1090 // mis-parsed name as `,` and dropped every binding.
1091 let mut proc_attrs = Vec::new();
1092 while self.eat(&TokenKind::Comma) {
1093 let text = self.peek_text().to_lowercase();
1094 match text.as_str() {
1095 "public" | "private" => {
1096 proc_attrs.push(self.advance().clone().text);
1097 }
1098 _ => break,
1099 }
1100 }
1101 self.eat(&TokenKind::ColonColon);
1102 let mut name = self.advance().clone().text;
1103 // Handle operator(...) form.
1104 if name.eq_ignore_ascii_case("operator") || name.eq_ignore_ascii_case("assignment") {
1105 self.expect(&TokenKind::LParen)?;
1106 let op = self.advance().clone().text;
1107 self.expect(&TokenKind::RParen)?;
1108 name = format!("{}({})", name, op);
1109 }
1110 let mut bindings = Vec::new();
1111 if self.eat(&TokenKind::Arrow) {
1112 bindings.push(self.advance().clone().text);
1113 while self.eat(&TokenKind::Comma) {
1114 bindings.push(self.advance().clone().text);
1115 }
1116 }
1117 let binding = bindings.first().cloned();
1118 Ok(TypeBoundProc {
1119 name,
1120 interface: None,
1121 binding,
1122 bindings,
1123 attrs: proc_attrs,
1124 is_generic: true,
1125 })
1126 }
1127
1128 // ---- PARAMETER, COMMON, EQUIVALENCE, DATA ----
1129
1130 pub fn parse_parameter_stmt(&mut self) -> Result<SpannedDecl, ParseError> {
1131 let start = self.current_span();
1132 // Already consumed 'parameter'. Expect (name=expr, ...)
1133 self.expect(&TokenKind::LParen)?;
1134 let mut pairs = Vec::new();
1135 loop {
1136 let name = self.advance().clone().text;
1137 self.expect(&TokenKind::Assign)?;
1138 let value = self.parse_expr()?;
1139 pairs.push((name, value));
1140 if !self.eat(&TokenKind::Comma) {
1141 break;
1142 }
1143 }
1144 self.expect(&TokenKind::RParen)?;
1145 let span = crate::parser::expr::span_from_to(start, self.prev_span());
1146 Ok(Spanned::new(Decl::ParameterStmt { pairs }, span))
1147 }
1148
1149 pub fn parse_common_block(&mut self) -> Result<SpannedDecl, ParseError> {
1150 let start = self.current_span();
1151 // Already consumed 'common'. Expect /name/ var-list.
1152 let name = if self.eat(&TokenKind::Slash) {
1153 let n = self.advance().clone().text;
1154 self.expect(&TokenKind::Slash)?;
1155 Some(n)
1156 } else {
1157 None
1158 };
1159 let mut vars = Vec::new();
1160 loop {
1161 vars.push(self.advance().clone().text);
1162 if !self.eat(&TokenKind::Comma) {
1163 break;
1164 }
1165 }
1166 let span = crate::parser::expr::span_from_to(start, self.prev_span());
1167 Ok(Spanned::new(Decl::CommonBlock { name, vars }, span))
1168 }
1169
1170 pub fn parse_data_stmt(&mut self) -> Result<SpannedDecl, ParseError> {
1171 use crate::parser::expr::BP_MUL;
1172 let start = self.current_span();
1173 // Already consumed 'data'. Format: obj-list /value-list/ [, obj-list /value-list/]
1174 // Note: / delimiters conflict with division operator. We parse expressions
1175 // at a binding power that excludes * and / to prevent consuming the delimiter.
1176 let mut sets = Vec::new();
1177 loop {
1178 let mut objects = Vec::new();
1179 while self.peek() != &TokenKind::Slash {
1180 objects.push(self.parse_expr_bp(BP_MUL.right)?);
1181 if !self.eat(&TokenKind::Comma) {
1182 break;
1183 }
1184 }
1185 self.expect(&TokenKind::Slash)?;
1186 let mut values = Vec::new();
1187 while self.peek() != &TokenKind::Slash {
1188 values.push(self.parse_expr_bp(BP_MUL.right)?);
1189 if !self.eat(&TokenKind::Comma) {
1190 break;
1191 }
1192 }
1193 self.expect(&TokenKind::Slash)?;
1194 sets.push(DataSet { objects, values });
1195 if !self.eat(&TokenKind::Comma) {
1196 break;
1197 }
1198 // Check if next batch starts or if we're at end of statement.
1199 if self.at_stmt_end() {
1200 break;
1201 }
1202 }
1203 let span = crate::parser::expr::span_from_to(start, self.prev_span());
1204 Ok(Spanned::new(Decl::DataStmt { sets }, span))
1205 }
1206
1207 pub fn parse_equivalence_stmt(&mut self) -> Result<SpannedDecl, ParseError> {
1208 let start = self.current_span();
1209 // Already consumed 'equivalence'. Format: (var-list), (var-list), ...
1210 let mut groups = Vec::new();
1211 loop {
1212 self.expect(&TokenKind::LParen)?;
1213 let mut group = Vec::new();
1214 loop {
1215 group.push(self.parse_expr()?);
1216 if !self.eat(&TokenKind::Comma) {
1217 break;
1218 }
1219 }
1220 self.expect(&TokenKind::RParen)?;
1221 groups.push(group);
1222 if !self.eat(&TokenKind::Comma) {
1223 break;
1224 }
1225 }
1226 let span = crate::parser::expr::span_from_to(start, self.prev_span());
1227 Ok(Spanned::new(Decl::EquivalenceStmt { groups }, span))
1228 }
1229
1230 pub fn parse_enum_def(&mut self) -> Result<SpannedDecl, ParseError> {
1231 let start = self.current_span();
1232 self.advance(); // consume ENUM
1233
1234 if self.eat(&TokenKind::Comma) {
1235 if !self.eat_ident("bind") {
1236 return Err(self.error("expected BIND(C) after ENUM,".into()));
1237 }
1238 self.expect(&TokenKind::LParen)?;
1239 if !self.eat_ident("c") {
1240 return Err(self.error("expected BIND(C) after ENUM,".into()));
1241 }
1242 self.expect(&TokenKind::RParen)?;
1243 }
1244
1245 self.skip_newlines();
1246 let mut enumerators = Vec::new();
1247 loop {
1248 self.skip_newlines();
1249 let text = self.peek_text().to_lowercase();
1250 if text == "end" || text == "endenum" {
1251 break;
1252 }
1253 if text != "enumerator" {
1254 return Err(self.error(format!(
1255 "expected ENUMERATOR or end enum, got '{}'",
1256 self.peek_text()
1257 )));
1258 }
1259 self.advance(); // consume ENUMERATOR
1260 self.eat(&TokenKind::ColonColon);
1261 loop {
1262 if self.peek() != &TokenKind::Identifier {
1263 return Err(self.error("expected enumerator name".into()));
1264 }
1265 let name = self.advance().clone().text;
1266 let value = if self.eat(&TokenKind::Assign) {
1267 Some(self.parse_expr()?)
1268 } else {
1269 None
1270 };
1271 enumerators.push((name, value));
1272 if !self.eat(&TokenKind::Comma) {
1273 break;
1274 }
1275 }
1276 self.skip_newlines();
1277 }
1278
1279 self.consume_end("enum")?;
1280 let span = crate::parser::expr::span_from_to(start, self.prev_span());
1281 Ok(Spanned::new(Decl::EnumDef { enumerators }, span))
1282 }
1283 }
1284
1285 #[cfg(test)]
1286 mod tests {
1287 use super::*;
1288 use crate::lexer::Lexer;
1289
1290 fn parse_decl(src: &str) -> SpannedDecl {
1291 let tokens = Lexer::tokenize(src, 0).unwrap();
1292 let mut parser = Parser::new(&tokens);
1293
1294 // Try type specifier first.
1295 if let Some(ts_result) = parser.try_parse_type_spec() {
1296 let ts = ts_result.unwrap();
1297 return parser.parse_type_decl(ts).unwrap();
1298 }
1299
1300 // Try USE.
1301 if parser.peek_text().eq_ignore_ascii_case("use") {
1302 parser.advance();
1303 return parser.parse_use_stmt().unwrap();
1304 }
1305
1306 // Try IMPLICIT.
1307 if parser.peek_text().eq_ignore_ascii_case("implicit") {
1308 parser.advance();
1309 return parser.parse_implicit().unwrap();
1310 }
1311
1312 // Try PARAMETER.
1313 if parser.peek_text().eq_ignore_ascii_case("parameter") {
1314 parser.advance();
1315 return parser.parse_parameter_stmt().unwrap();
1316 }
1317
1318 // Try COMMON.
1319 if parser.peek_text().eq_ignore_ascii_case("common") {
1320 parser.advance();
1321 return parser.parse_common_block().unwrap();
1322 }
1323
1324 // Try DATA.
1325 if parser.peek_text().eq_ignore_ascii_case("data") {
1326 parser.advance();
1327 return parser.parse_data_stmt().unwrap();
1328 }
1329
1330 // Try EQUIVALENCE.
1331 if parser.peek_text().eq_ignore_ascii_case("equivalence") {
1332 parser.advance();
1333 return parser.parse_equivalence_stmt().unwrap();
1334 }
1335
1336 // Try ENUM.
1337 if parser.peek_text().eq_ignore_ascii_case("enum") {
1338 return parser.parse_enum_def().unwrap();
1339 }
1340
1341 panic!("could not parse as declaration: {}", src);
1342 }
1343
1344 // ---- Type declarations ----
1345
1346 #[test]
1347 fn integer_simple() {
1348 let d = parse_decl("integer :: x, y, z");
1349 if let Decl::TypeDecl {
1350 type_spec,
1351 entities,
1352 ..
1353 } = &d.node
1354 {
1355 assert!(matches!(type_spec, TypeSpec::Integer(None)));
1356 assert_eq!(entities.len(), 3);
1357 assert_eq!(entities[0].name, "x");
1358 } else {
1359 panic!("not TypeDecl");
1360 }
1361 }
1362
1363 #[test]
1364 fn integer_with_init() {
1365 let d = parse_decl("integer :: x = 0, y = 1");
1366 if let Decl::TypeDecl { entities, .. } = &d.node {
1367 assert!(entities[0].init.is_some());
1368 assert!(entities[1].init.is_some());
1369 } else {
1370 panic!("not TypeDecl");
1371 }
1372 }
1373
1374 #[test]
1375 fn integer_with_kind() {
1376 let d = parse_decl("integer(8) :: x");
1377 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1378 assert!(matches!(type_spec, TypeSpec::Integer(Some(_))));
1379 } else {
1380 panic!("not TypeDecl");
1381 }
1382 }
1383
1384 #[test]
1385 fn type_bound_proc_with_interface_spec_preserves_method_name() {
1386 let tokens = Lexer::tokenize("procedure(push_iface), deferred :: push", 0).unwrap();
1387 let mut parser = Parser::new(&tokens);
1388 parser.advance();
1389 let tbp = parser.parse_type_bound_proc().unwrap();
1390 assert_eq!(tbp.name, "push");
1391 assert_eq!(tbp.interface.as_deref(), Some("push_iface"));
1392 assert!(tbp.binding.is_none());
1393 assert!(tbp.bindings.is_empty());
1394 assert_eq!(tbp.attrs, vec!["deferred"]);
1395 }
1396
1397 #[test]
1398 fn generic_type_bound_proc_preserves_all_specific_bindings() {
1399 let tokens =
1400 Lexer::tokenize("generic :: set => set_float, set_integer, set_datetime", 0).unwrap();
1401 let mut parser = Parser::new(&tokens);
1402 parser.advance();
1403 let tbp = parser.parse_type_bound_proc_generic().unwrap();
1404 assert_eq!(tbp.name, "set");
1405 assert_eq!(tbp.binding.as_deref(), Some("set_float"));
1406 assert_eq!(
1407 tbp.bindings,
1408 vec![
1409 "set_float".to_string(),
1410 "set_integer".to_string(),
1411 "set_datetime".to_string()
1412 ]
1413 );
1414 assert!(tbp.is_generic);
1415 }
1416
1417 #[test]
1418 fn real_allocatable() {
1419 let d = parse_decl("real(8), allocatable :: matrix(:,:)");
1420 if let Decl::TypeDecl {
1421 type_spec,
1422 attrs,
1423 entities,
1424 } = &d.node
1425 {
1426 assert!(matches!(type_spec, TypeSpec::Real(Some(_))));
1427 assert!(attrs.contains(&Attribute::Allocatable));
1428 assert!(entities[0].array_spec.is_some());
1429 } else {
1430 panic!("not TypeDecl");
1431 }
1432 }
1433
1434 #[test]
1435 fn character_deferred_length() {
1436 let d = parse_decl("character(len=:), allocatable :: name");
1437 if let Decl::TypeDecl {
1438 type_spec, attrs, ..
1439 } = &d.node
1440 {
1441 if let TypeSpec::Character(Some(cs)) = type_spec {
1442 assert!(matches!(cs.len, Some(LenSpec::Colon)));
1443 } else {
1444 panic!("not character type");
1445 }
1446 assert!(attrs.contains(&Attribute::Allocatable));
1447 } else {
1448 panic!("not TypeDecl");
1449 }
1450 }
1451
1452 #[test]
1453 fn character_assumed_length() {
1454 let d = parse_decl("character(len=*), intent(in) :: input");
1455 if let Decl::TypeDecl {
1456 type_spec, attrs, ..
1457 } = &d.node
1458 {
1459 if let TypeSpec::Character(Some(cs)) = type_spec {
1460 assert!(matches!(cs.len, Some(LenSpec::Star)));
1461 } else {
1462 panic!("not character type");
1463 }
1464 assert!(attrs
1465 .iter()
1466 .any(|a| matches!(a, Attribute::Intent(Intent::In))));
1467 } else {
1468 panic!("not TypeDecl");
1469 }
1470 }
1471
1472 #[test]
1473 fn type_derived() {
1474 let d = parse_decl("type(my_type) :: obj");
1475 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1476 assert!(matches!(type_spec, TypeSpec::Type(ref n) if n == "my_type"));
1477 } else {
1478 panic!("not TypeDecl");
1479 }
1480 }
1481
1482 #[test]
1483 fn class_star() {
1484 let d = parse_decl("class(*) :: poly");
1485 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1486 assert!(matches!(type_spec, TypeSpec::ClassStar));
1487 } else {
1488 panic!("not TypeDecl");
1489 }
1490 }
1491
1492 #[test]
1493 fn pointer_init() {
1494 let d = parse_decl("type(node), pointer :: ptr => null()");
1495 if let Decl::TypeDecl { entities, .. } = &d.node {
1496 assert!(entities[0].ptr_init.is_some());
1497 } else {
1498 panic!("not TypeDecl");
1499 }
1500 }
1501
1502 #[test]
1503 fn intent_inout() {
1504 let d = parse_decl("real, intent(inout) :: x");
1505 if let Decl::TypeDecl { attrs, .. } = &d.node {
1506 assert!(attrs
1507 .iter()
1508 .any(|a| matches!(a, Attribute::Intent(Intent::InOut))));
1509 } else {
1510 panic!("not TypeDecl");
1511 }
1512 }
1513
1514 #[test]
1515 fn intent_in_out_two_words() {
1516 let d = parse_decl("real, intent(in out) :: x");
1517 if let Decl::TypeDecl { attrs, .. } = &d.node {
1518 assert!(attrs
1519 .iter()
1520 .any(|a| matches!(a, Attribute::Intent(Intent::InOut))));
1521 } else {
1522 panic!("not TypeDecl");
1523 }
1524 }
1525
1526 #[test]
1527 fn multiple_attributes() {
1528 let d = parse_decl("real(8), dimension(:,:), allocatable, intent(inout) :: matrix");
1529 if let Decl::TypeDecl { attrs, .. } = &d.node {
1530 assert!(attrs.iter().any(|a| matches!(a, Attribute::Dimension(_))));
1531 assert!(attrs.contains(&Attribute::Allocatable));
1532 assert!(attrs
1533 .iter()
1534 .any(|a| matches!(a, Attribute::Intent(Intent::InOut))));
1535 } else {
1536 panic!("not TypeDecl");
1537 }
1538 }
1539
1540 #[test]
1541 fn old_style_no_double_colon() {
1542 let d = parse_decl("integer x, y");
1543 if let Decl::TypeDecl { entities, .. } = &d.node {
1544 assert_eq!(entities.len(), 2);
1545 assert_eq!(entities[0].name, "x");
1546 } else {
1547 panic!("not TypeDecl");
1548 }
1549 }
1550
1551 #[test]
1552 fn double_precision() {
1553 let d = parse_decl("double precision :: x");
1554 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1555 assert!(matches!(type_spec, TypeSpec::DoublePrecision));
1556 } else {
1557 panic!("not TypeDecl");
1558 }
1559 }
1560
1561 #[test]
1562 fn bind_c() {
1563 let d = parse_decl("integer, bind(c) :: x");
1564 if let Decl::TypeDecl { attrs, .. } = &d.node {
1565 assert!(attrs.iter().any(|a| matches!(a, Attribute::Bind(None))));
1566 } else {
1567 panic!("not TypeDecl");
1568 }
1569 }
1570
1571 #[test]
1572 fn enum_bind_c() {
1573 let d = parse_decl("enum, bind(c)\n enumerator :: red = 1, blue = 2\nend enum\n");
1574 if let Decl::EnumDef { enumerators } = &d.node {
1575 assert_eq!(enumerators.len(), 2);
1576 assert_eq!(enumerators[0].0, "red");
1577 assert_eq!(enumerators[1].0, "blue");
1578 } else {
1579 panic!("not EnumDef");
1580 }
1581 }
1582
1583 // ---- USE statements ----
1584
1585 #[test]
1586 fn use_simple() {
1587 let d = parse_decl("use my_module");
1588 if let Decl::UseStmt { module, nature, .. } = &d.node {
1589 assert_eq!(module, "my_module");
1590 assert_eq!(*nature, UseNature::Normal);
1591 } else {
1592 panic!("not UseStmt");
1593 }
1594 }
1595
1596 #[test]
1597 fn use_only() {
1598 let d = parse_decl("use my_module, only: foo, bar");
1599 if let Decl::UseStmt { only, .. } = &d.node {
1600 let items = only.as_ref().unwrap();
1601 assert_eq!(items.len(), 2);
1602 } else {
1603 panic!("not UseStmt");
1604 }
1605 }
1606
1607 #[test]
1608 fn use_intrinsic() {
1609 let d = parse_decl("use, intrinsic :: iso_c_binding");
1610 if let Decl::UseStmt { module, nature, .. } = &d.node {
1611 assert_eq!(module, "iso_c_binding");
1612 assert_eq!(*nature, UseNature::Intrinsic);
1613 } else {
1614 panic!("not UseStmt");
1615 }
1616 }
1617
1618 #[test]
1619 fn use_only_with_rename() {
1620 let d = parse_decl("use my_module, only: local => remote");
1621 if let Decl::UseStmt { only, .. } = &d.node {
1622 let items = only.as_ref().unwrap();
1623 assert!(matches!(&items[0], OnlyItem::Rename(_)));
1624 } else {
1625 panic!("not UseStmt");
1626 }
1627 }
1628
1629 #[test]
1630 fn use_only_generic_specs() {
1631 let d = parse_decl("use my_module, only: operator(+), operator(//), assignment(=)");
1632 if let Decl::UseStmt { only, .. } = &d.node {
1633 let items = only.as_ref().unwrap();
1634 assert_eq!(items.len(), 3);
1635 assert!(matches!(&items[0], OnlyItem::Generic(name) if name == "operator(+)"));
1636 assert!(matches!(&items[1], OnlyItem::Generic(name) if name == "operator(//)"));
1637 assert!(matches!(&items[2], OnlyItem::Generic(name) if name == "assignment(=)"));
1638 } else {
1639 panic!("not UseStmt");
1640 }
1641 }
1642
1643 // ---- IMPLICIT ----
1644
1645 #[test]
1646 fn implicit_none() {
1647 let d = parse_decl("implicit none");
1648 assert!(matches!(
1649 d.node,
1650 Decl::ImplicitNone {
1651 type_: true,
1652 external: false
1653 }
1654 ));
1655 }
1656
1657 #[test]
1658 fn implicit_none_type_external() {
1659 let d = parse_decl("implicit none(type, external)");
1660 assert!(matches!(
1661 d.node,
1662 Decl::ImplicitNone {
1663 type_: true,
1664 external: true
1665 }
1666 ));
1667 }
1668
1669 #[test]
1670 fn implicit_double_precision() {
1671 let d = parse_decl("implicit double precision (a-h, o-z)");
1672 if let Decl::ImplicitStmt { specs } = &d.node {
1673 assert_eq!(specs.len(), 1);
1674 assert!(matches!(specs[0].type_spec, TypeSpec::DoublePrecision));
1675 assert_eq!(specs[0].ranges.len(), 2);
1676 } else {
1677 panic!("not ImplicitStmt");
1678 }
1679 }
1680
1681 // ---- PARAMETER, COMMON, DATA, EQUIVALENCE ----
1682
1683 #[test]
1684 fn parameter_stmt() {
1685 let d = parse_decl("parameter (pi = 3.14159, e = 2.71828)");
1686 if let Decl::ParameterStmt { pairs } = &d.node {
1687 assert_eq!(pairs.len(), 2);
1688 assert_eq!(pairs[0].0, "pi");
1689 assert_eq!(pairs[1].0, "e");
1690 } else {
1691 panic!("not ParameterStmt");
1692 }
1693 }
1694
1695 #[test]
1696 fn common_block() {
1697 let d = parse_decl("common /block1/ x, y, z");
1698 if let Decl::CommonBlock { name, vars } = &d.node {
1699 assert_eq!(name.as_deref(), Some("block1"));
1700 assert_eq!(vars.len(), 3);
1701 } else {
1702 panic!("not CommonBlock");
1703 }
1704 }
1705
1706 #[test]
1707 fn data_stmt() {
1708 let d = parse_decl("data x /1.0/, y /2.0/");
1709 if let Decl::DataStmt { sets } = &d.node {
1710 assert_eq!(sets.len(), 2);
1711 } else {
1712 panic!("not DataStmt");
1713 }
1714 }
1715
1716 #[test]
1717 fn equivalence_stmt() {
1718 let d = parse_decl("equivalence (a, b), (c, d)");
1719 if let Decl::EquivalenceStmt { groups } = &d.node {
1720 assert_eq!(groups.len(), 2);
1721 assert_eq!(groups[0].len(), 2);
1722 } else {
1723 panic!("not EquivalenceStmt");
1724 }
1725 }
1726
1727 // ---- Audit test gap coverage ----
1728
1729 #[test]
1730 fn real_star8_old_style() {
1731 let d = parse_decl("real*8 :: x");
1732 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1733 assert!(matches!(
1734 type_spec,
1735 TypeSpec::Real(Some(KindSelector::Star(_)))
1736 ));
1737 } else {
1738 panic!("not TypeDecl");
1739 }
1740 }
1741
1742 #[test]
1743 fn character_bare_length() {
1744 let d = parse_decl("character(10) :: s");
1745 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1746 if let TypeSpec::Character(Some(cs)) = type_spec {
1747 assert!(matches!(cs.len, Some(LenSpec::Expr(_))));
1748 } else {
1749 panic!("not character type");
1750 }
1751 } else {
1752 panic!("not TypeDecl");
1753 }
1754 }
1755
1756 #[test]
1757 fn integer_kind_keyword() {
1758 let d = parse_decl("integer(kind=4) :: x");
1759 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1760 assert!(matches!(type_spec, TypeSpec::Integer(Some(_))));
1761 } else {
1762 panic!("not TypeDecl");
1763 }
1764 }
1765
1766 #[test]
1767 fn class_derived_type() {
1768 let d = parse_decl("class(my_type) :: x");
1769 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1770 assert!(matches!(type_spec, TypeSpec::Class(ref n) if n == "my_type"));
1771 } else {
1772 panic!("not TypeDecl");
1773 }
1774 }
1775
1776 #[test]
1777 fn type_star_assumed() {
1778 let d = parse_decl("type(*) :: x");
1779 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1780 assert!(matches!(type_spec, TypeSpec::TypeStar));
1781 } else {
1782 panic!("not TypeDecl");
1783 }
1784 }
1785
1786 #[test]
1787 fn entity_array_spec() {
1788 let d = parse_decl("integer :: a(10), b(20,30)");
1789 if let Decl::TypeDecl { entities, .. } = &d.node {
1790 assert!(entities[0].array_spec.is_some());
1791 assert_eq!(entities[0].array_spec.as_ref().unwrap().len(), 1);
1792 assert!(entities[1].array_spec.is_some());
1793 assert_eq!(entities[1].array_spec.as_ref().unwrap().len(), 2);
1794 } else {
1795 panic!("not TypeDecl");
1796 }
1797 }
1798
1799 #[test]
1800 fn coarray_entity_decl_reports_not_implemented() {
1801 let tokens = Lexer::tokenize("integer :: x[*]\n", 0).unwrap();
1802 let mut parser = Parser::new(&tokens);
1803 let type_spec = parser
1804 .try_parse_type_spec()
1805 .expect("expected type specifier")
1806 .unwrap();
1807 let err = parser
1808 .parse_type_decl(type_spec)
1809 .expect_err("coarray declaration should not parse yet");
1810 assert!(err
1811 .msg
1812 .contains("coarray declarations are recognized but not yet implemented"));
1813 }
1814
1815 #[test]
1816 fn logical_type() {
1817 let d = parse_decl("logical :: flag");
1818 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1819 assert!(matches!(type_spec, TypeSpec::Logical(None)));
1820 } else {
1821 panic!("not TypeDecl");
1822 }
1823 }
1824
1825 #[test]
1826 fn complex_type() {
1827 let d = parse_decl("complex :: z");
1828 if let Decl::TypeDecl { type_spec, .. } = &d.node {
1829 assert!(matches!(type_spec, TypeSpec::Complex(None)));
1830 } else {
1831 panic!("not TypeDecl");
1832 }
1833 }
1834
1835 #[test]
1836 fn implicit_integer() {
1837 let d = parse_decl("implicit integer (i-n)");
1838 if let Decl::ImplicitStmt { specs } = &d.node {
1839 assert!(matches!(specs[0].type_spec, TypeSpec::Integer(_)));
1840 } else {
1841 panic!("not ImplicitStmt");
1842 }
1843 }
1844
1845 #[test]
1846 fn use_double_colon() {
1847 let d = parse_decl("use :: my_module");
1848 if let Decl::UseStmt { module, .. } = &d.node {
1849 assert_eq!(module, "my_module");
1850 } else {
1851 panic!("not UseStmt");
1852 }
1853 }
1854
1855 #[test]
1856 fn bind_with_name() {
1857 let d = parse_decl("integer, bind(c, name='cfunc') :: x");
1858 if let Decl::TypeDecl { attrs, .. } = &d.node {
1859 assert!(attrs.iter().any(|a| matches!(a, Attribute::Bind(Some(_)))));
1860 } else {
1861 panic!("not TypeDecl");
1862 }
1863 }
1864
1865 #[test]
1866 fn save_attribute() {
1867 let d = parse_decl("integer, save :: x");
1868 if let Decl::TypeDecl { attrs, .. } = &d.node {
1869 assert!(attrs.contains(&Attribute::Save));
1870 } else {
1871 panic!("not TypeDecl");
1872 }
1873 }
1874
1875 #[test]
1876 fn value_attribute() {
1877 let d = parse_decl("integer, value :: x");
1878 if let Decl::TypeDecl { attrs, .. } = &d.node {
1879 assert!(attrs.contains(&Attribute::Value));
1880 } else {
1881 panic!("not TypeDecl");
1882 }
1883 }
1884 }
1885