camfort / fortran-src

Fortran parsing and static analysis infrastructure
https://hackage.haskell.org/package/fortran-src
Other
44 stars 20 forks source link

F90 parsing failure for multiple comments in case constructs #267

Open RaoulHC opened 1 year ago

RaoulHC commented 1 year ago

The following code fails to parse in the f90 and above parsers

program main
integer x
select case(x)
! comment uno
! comment dos
case (2):
    print *, "foo"
end select
end program main

Outputting:

ProgramFilefortran-src: case-comments.f90, 5:14: case-comments.f90: parsing failed. 
Last parsed token: TComment (5:1)-(5:13) " comment dos".
CallStack (from HasCallStack):
  error, called at app/Main.hs:108:36 in main:Main

It appears the MAYBE_COMMENT grammar used in quite a few places, seems to only accept a single comment, but is structured such that getting it to accept multiple comments causes other grammars to fail. Having a single comment (or oddly an inline comment after select case (x) and another line comment), parses correctly.

dorchard commented 1 year ago

(For info on trying to solve this, single comments were enabled by this commit: https://github.com/camfort/fortran-src/commit/401fd71679e3c1fcc564f28b35d4eaa790377523)

raehik commented 1 year ago

Nice catch. The relevant parser snippet in F90 is here:


-- We store line comments as statements, but this raises an issue: we have
-- nowhere to place comments after a SELECT CASE but before a CASE. So we drop
-- them. The inner CASES_ rule does /not/ use this, because comments can always
-- be parsed as belonging to to the above CASE block.
CASES  :: { ([(AList Index A0, [Block A0])], Maybe [Block A0], SrcSpan, Maybe (Expression A0)) }
: COMMENT_BLOCK CASES_ { $2 }
|               CASES_ { $1 }

CASES_ :: { ([(AList Index A0, [Block A0])], Maybe [Block A0], SrcSpan, Maybe (Expression A0)) }
: maybe(INTEGER_LITERAL) case '(' INDICIES ')' MAYBE_COMMENT NEWLINE BLOCKS CASES_
  { let (clauses, defaultCase, endSpan, endLabel) = $9
    in  ((fromReverseList $4, reverse $8) : clauses, defaultCase, endSpan, endLabel) }
| maybe(INTEGER_LITERAL) case default          MAYBE_COMMENT NEWLINE BLOCKS END_SELECT
  { let (endSpan, endLabel) = $7
    in ([], Just $6, endSpan, endLabel) }
| END_SELECT
  { let (endSpan, endLabel) = $1
    in ([], Nothing, endSpan, endLabel) }

-- ...

MAYBE_COMMENT :: { Maybe Token }
: comment     { Just $1 }
| {- EMPTY -} { Nothing }

Previously, we would fail on parsing any comments there. Maybe we could change from MAYBE_COMMENT (0 or 1) to COMMENTS (0-n).