74 use,
intrinsic :: iso_c_binding, only: c_funptr, c_f_procpointer
82 implicit none;
private
91 type(string),
allocatable :: lines(:)
95 integer,
parameter :: BODY_BUFFER = 50
96 type(body) :: bodies(MAX_FOR_DEPTH)
97 type(macro),
allocatable :: fmacros(:)
129 type(
context),
intent(in) :: ctx
130 type(
macro),
allocatable,
intent(inout) :: macros(:)
131 character(*),
intent(in) :: token
133 character(:),
allocatable :: val, name, temp
134 integer :: pos, paren_start, paren_end, i, npar, imacro
138 if (depth > max_for_depth)
then
140 message=
'Loop nesting too deep', &
141 source=
trim(ctx%path)), &
142 ctx%content, ctx%line))
147 temp =
trim(adjustl(ctx%content(pos + 1:)))
149 if (
index(temp,
' in ') == 0)
then
151 message=
'Syntax error', &
152 label=
label_type(
'Missing " in " keyword', pos + 1, 4), &
154 trim(ctx%content), ctx%line))
157 name =
trim(adjustl(temp(:
index(temp,
' in '))))
158 if (
global%undef .contains. name)
return
160 if (name ==
'defined')
then
162 message=
'Reserved macro name', &
163 label=
label_type(
'"defined" cannot be used as a macro name', paren_start + 1,
len(name)), &
165 trim(ctx%content), ctx%line))
169 pos =
index(temp,
' in ') +
len(
' in ')
172 paren_start =
index(temp,
'[')
173 if (paren_start == 0)
then
175 message=
'Syntax error', &
176 label=
label_type(
'Missing opening square bracket in #for expression', 1, 1), &
178 trim(ctx%content), ctx%line))
182 paren_end =
index(temp,
']', back=.true.)
183 if (paren_end == 0)
then
185 message=
'Syntax error', &
186 label=
label_type(
'Missing closing square bracket in #for expression',
len_trim(ctx%content) + 1, 1), &
188 trim(ctx%content), ctx%line))
191 temp = temp(paren_start + 1:paren_end - 1)
195 if (temp(pos:pos) ==
',')
then
200 if (
len_trim(temp) > 0) npar = npar + 1
202 if (.not.
allocated(fmacros))
allocate(fmacros(0))
203 if (.not.
is_defined(name, fmacros, imacro))
then
204 call add(fmacros, name,
'')
207 fmacros(imacro) =
macro(name,
'')
210 fmacros(imacro)%active = .false.
211 fmacros(imacro)%is_variadic = .false.
212 if (
allocated(fmacros(imacro)%params))
deallocate(fmacros(imacro)%params)
213 allocate(fmacros(imacro)%params(npar))
216 do while (pos <=
len_trim(temp) .and. i <= npar)
217 do while (pos <=
len_trim(temp) .and. temp(pos:pos) ==
' ')
222 do while (pos <=
len_trim(temp) .and. temp(pos:pos) /=
',' .and. temp(pos:pos) /=
' ')
226 fmacros(imacro)%params(i) = temp(paren_start:pos - 1)
229 if (temp(pos:pos) ==
',') pos = pos + 1
252 type(
context),
intent(inout) :: ctx
253 integer,
intent(in) :: ounit
254 type(c_funptr),
intent(in) :: p
255 type(
macro),
intent(in) :: macros(:)
256 character(*),
intent(in) :: token
259 character(:),
allocatable :: rst, tmp
261 type(
string),
allocatable :: params(:)
262 type(
macro),
allocatable :: ms(:)
265 call c_f_procpointer(p, preprocess)
270 if (depth + 1 <=
size_of(fmacros))
then
271 if (
allocated(fmacros(depth + 1)%params)) params = fmacros(depth + 1)%params
272 if (
allocated(fmacros(depth + 1)%params))
deallocate(fmacros(depth + 1)%params)
275 fmacros(depth + 1)%value = params(i)
276 fmacros(depth + 1)%active = .true.
277 ms = [fmacros(depth + 1), macros]
279 do j = 1, bodies(depth + 1)%nlines
280 if (
head(bodies(depth + 1)%lines(j)%chars) ==
'#')
then
281 if (
len(bodies(depth + 1)%lines(j)%chars) == 1)
then
284 rst = adjustl(
expand_macros(bodies(depth + 1)%lines(j)%chars, ms, stitch, &
285 global%implicit_continuation,
global%support_dollar_insert, ctx))
286 tmp = preprocess(rst, ounit, ctx%path, ctx%line, ms, stitch)
289 rst = adjustl(
expand_macros(bodies(depth + 1)%lines(j)%chars, ms, stitch,
global%implicit_continuation, &
290 global%support_dollar_insert, ctx))
291 tmp = preprocess(rst, ounit, ctx%path, ctx%line, ms, stitch)
296 call addline(bodies(depth),
string(tmp))
301 tmp = preprocess(rst, ounit, ctx%path, ctx%line, ms, stitch)
304 write(ounit,
'(A)') rst
308 call addline(bodies(depth),
string(
''))
310 write(ounit,
'(A)')
''
313 bodies(depth + 1)%nlines = 0
314 if (
allocated(bodies(depth + 1)%lines))
deallocate(bodies(depth + 1)%lines)
317 if (
allocated(params))
deallocate(params)
318 if (
allocated(ms))
deallocate(ms)
323 message=
'Unbalanced #for expression. Missing #for or #endfor directive.', &
330 if (
allocated(fmacros))
deallocate(fmacros)
331 do i = 1, max_for_depth
332 if (
allocated(bodies(i)%lines))
deallocate(bodies(i)%lines)
347 character(*),
intent(in) :: line
349 call addline(bodies(depth),
string(line))
363 subroutine addline(b, line)
364 type(body),
intent(inout) :: b
365 type(
string),
intent(in) :: line
367 type(
string),
allocatable :: tmp(:)
370 if (.not.
allocated(b%lines))
then
374 b%nlines = b%nlines + 1
376 if (b%nlines <= n)
then
377 b%lines(b%nlines) = line
379 allocate(tmp(n + body_buffer))
380 tmp(1:n) = b%lines(1:n)
382 call move_alloc(from=tmp, to=b%lines)
subroutine, public handle_for(ctx, macros, token)
Process a #for directive and initialize a new loop context.
subroutine, public add_to_loop(line)
Append a source line to the currently active loop body.
subroutine, public handle_endfor(ctx, ounit, p, macros, token)
Finalize a loop and emit all expanded iterations.
logical function, public is_in_forloop()
Query whether parsing is currently inside a #for block.
type(global_settings), public global
The single global instance used throughout fpx Initialized automatically with sensible defaults value...
character(:) function, allocatable, public expand_macros(line, macros, stitch, implicit_conti, dollar_insert, ctx)
Core recursive macro expander (handles function-like, variadic, #, ##).
logical function, public is_defined(name, macros, idx)
Check if a macro with given name exists in table.
pure character(len_trim(str)) function, public lowercase(str)
Convert string to lower case (respects contents of quotes).
character function, public head(str)
Returns the first non-blank character of a string.
Interface to render diagnostic messages and labels.
Add one or more macros to a dynamic table.
Return current number of stored macros.
Return the trimmed length of a string.
Return the length of a string.
Return the trimmed string.
Source location and content snapshot for precise diagnostics Instances of this type are created for e...
Definition of diagnostic message.
Represents text as a sequence of ASCII code units. The derived type wraps an allocatable character ar...
Derived type representing a single preprocessor macro Extends string with macro-specific fields: rep...
Represents text as a sequence of ASCII code units. The derived type wraps an allocatable character ar...