Toggle main menu visibility
Loading...
Searching...
No Matches
line.f90
Go to the documentation of this file.
1
!> @file
2
!! @defgroup group_line Line
3
!! Standard-compliant handling of the `#line` directive.
4
!!
5
!! This module implements support for the ISO C preprocessor `#line`
6
!! directive, allowing the logical source location used by the preprocessor
7
!! to be modified during preprocessing.
8
!!
9
!! The directive affects the information stored in the active
10
!! @link fpx_context::context context @endlink object and therefore influences:
11
!!
12
!! - Diagnostic messages and source locations,
13
!! - Expansions of predefined macros such as `__LINE__`,
14
!! - Expansions of `__FILE__` and `__FILENAME__`,
15
!! - The apparent origin of generated or transformed source code.
16
!!
17
!! This functionality is particularly useful for:
18
!!
19
!! - Source-to-source translators,
20
!! - Code generators,
21
!! - Literate programming systems,
22
!! - Template engines,
23
!! - Tools that emit Fortran intended to preserve original source locations.
24
!!
25
!! The following standard forms are supported:
26
!!
27
!! - `#line <number>`
28
!! - `#line <number> "<filename>"`
29
!!
30
!! When encountered, the directive immediately updates the logical
31
!! source position used for all subsequent processing.
32
!!
33
!! @note
34
!! The specified line number refers to the line immediately following
35
!! the directive itself, matching the behaviour of the ISO C preprocessor.
36
!!
37
!! @note
38
!! Malformed directives generate warnings and are ignored.
39
!!
40
!! @section line_examples Examples
41
!!
42
!! 1. Reset the logical line number:
43
!! @code{.f90}
44
!! #line 100
45
!! print *, "Reported as line 100"
46
!! ...
47
!! @endcode
48
!!
49
!! 2. Change both line number and filename:
50
!! @code{.f90}
51
!! #line 42 "generated.f90"
52
!! integer :: x
53
!!
54
!! ! Diagnostics now refer to generated.f90:42
55
!! ...
56
!! @endcode
57
!!
58
!! 3. Improve diagnostics in generated code:
59
!! @code{.f90}
60
!! #line 215 "input_template.f90"
61
!! call generated_procedure()
62
!! ...
63
!! @endcode
64
module
fpx_line
65
use
fpx_path
66
use
fpx_logging
67
use
fpx_context
68
69
implicit none
;
private
70
71
public
::
handle_line
72
73
contains
74
75
!> Process a `#line` directive.
76
!!
77
!! Parses the directive arguments, validates the requested logical
78
!! line number, and updates the active @ref context object.
79
!!
80
!! Supported forms are:
81
!!
82
!! @code{.txt}
83
!! #line <number>
84
!! #line <number> "<filename>"
85
!! @endcode
86
!!
87
!! The specified line number becomes the number associated with the
88
!! source line immediately following the directive.
89
!!
90
!! If a filename is supplied, subsequent diagnostics and predefined
91
!! file-related macros use the new filename.
92
!!
93
!! Invalid directives produce warnings and leave the current context
94
!! unchanged.
95
!!
96
!! @param[inout] ctx
97
!! Current source context. Its logical line number and optional
98
!! filename are updated in place.
99
!! @param[in] token
100
!! Directive keyword used to identify the `#line` directive,
101
!! typically `"line"`.
102
!!
103
!! @ingroup group_line
104
subroutine
handle_line
(ctx, token)
105
type
(
context
),
intent(inout)
:: ctx
106
character(*)
,
intent(in)
:: token
107
!private
108
character(:)
,
allocatable
:: temp, num_str, fname
109
integer
:: pos, iostat, new_line, closing
110
logical
:: has_filename
111
112
! Skip #line keyword
113
pos = index(lowercase(ctx%content), token) + len(token)
114
temp = trim(adjustl(ctx%content(pos:)))
115
116
if
(len_trim(temp) == 0)
then
117
call
printf(
render
(
diagnostic_report
(level_warning, &
118
message=
'Syntax error'
, &
119
label=
label_type
(
'#line directive with no arguments'
, index(token, lowercase(ctx%content)) + len(token) + 1, 1)&
120
, &
121
source=ctx%path), &
122
trim(ctx%content), ctx%line))
123
return
124
end if
125
126
! Extract line number
127
pos = index(temp,
' '
)
128
if
(pos > 0)
then
129
num_str = temp(:pos - 1)
130
fname = trim(adjustl(temp(pos:)))
131
has_filename = .true.
132
else
133
num_str = trim(temp)
134
has_filename = .false.
135
end if
136
137
! Parse line number
138
read
(num_str, *, iostat=iostat) new_line
139
if
(iostat /= 0 .or. new_line < 1)
then
140
call
printf(
render
(
diagnostic_report
(level_warning, &
141
message=
'Syntax error'
, &
142
label=
label_type
(
'Invalid line number in #line directive'
, index(token, lowercase(ctx%content)) + len(token) + &
143
1, len(num_str)), &
144
source=ctx%path), &
145
trim(ctx%content), ctx%line))
146
return
147
end if
148
149
! Update current line number (subtract 1 because the next line will be +1)
150
ctx%line = new_line - 1
151
152
! Update filename if provided (strip quotes)
153
if
(has_filename)
then
154
if
(fname(1:1) ==
'"'
.and. len(fname) > 1)
then
155
closing = index(fname(2:),
'"'
)
156
if
(closing == 0)
then
157
call
printf(
render
(
diagnostic_report
(level_error, &
158
message=
'Malformed #line directive'
, &
159
label=
label_type
(
'Missing closing quotation mark'
, &
160
index(ctx%content,
'"'
),1), &
161
source=trim(ctx%path)), &
162
ctx%content, ctx%line))
163
end if
164
fname = fname(2:closing)
165
end if
166
if
(len_trim(fname) > 0)
then
167
ctx%path = trim(fname)
168
end if
169
end if
170
end subroutine
171
end module
fpx_line::handle_line
subroutine, public handle_line(ctx, token)
Process a #line directive.
Definition
line.f90:105
fpx_logging::render
Generic renderer for diagnostics and source excerpts.
Definition
logging.f90:208
fpx_context::context
Snapshot of a source location within the preprocessing stream.
Definition
context.f90:114
fpx_logging::diagnostic_report
Structured compiler diagnostic.
Definition
logging.f90:337
fpx_logging::label_type
Diagnostic label identifying a region of source text.
Definition
logging.f90:304
© 2026 davidpfister. All Rights Reserved.