96 use,
intrinsic :: iso_c_binding, only: c_funptr, c_f_procpointer
104 implicit none;
private
118 integer :: nlines = 0
119 type(string),
allocatable :: lines(:)
124 integer,
parameter :: BODY_BUFFER = 50
125 type(body) :: bodies(MAX_FOR_DEPTH)
126 type(macro),
allocatable :: fmacros(:)
162 type(
context),
intent(in) :: ctx
163 type(
macro),
allocatable,
intent(inout) :: macros(:)
164 character(*),
intent(in) :: token
166 character(:),
allocatable :: val, name, temp
167 integer :: pos, paren_start, paren_end, i, npar, imacro
171 if (depth > max_for_depth)
then
173 message=
'Loop nesting too deep', &
174 source=
trim(ctx%path)), &
175 ctx%content, ctx%line))
180 temp =
trim(adjustl(ctx%content(pos + 1:)))
182 if (
index(temp,
' in ') == 0)
then
184 message=
'Syntax error', &
185 label=
label_type(
'Missing " in " keyword', pos + 1, 4), &
187 trim(ctx%content), ctx%line))
190 name =
trim(adjustl(temp(:
index(temp,
' in '))))
191 if (
global%undef .contains. name)
return
193 if (name ==
'defined')
then
195 message=
'Reserved macro name', &
196 label=
label_type(
'"defined" cannot be used as a macro name', paren_start + 1,
len(name)), &
198 trim(ctx%content), ctx%line))
202 pos =
index(temp,
' in ') +
len(
' in ')
205 paren_start =
index(temp,
'[')
206 if (paren_start == 0)
then
208 message=
'Syntax error', &
209 label=
label_type(
'Missing opening square bracket in #for expression', 1, 1), &
211 trim(ctx%content), ctx%line))
215 paren_end =
index(temp,
']', back=.true.)
216 if (paren_end == 0)
then
218 message=
'Syntax error', &
219 label=
label_type(
'Missing closing square bracket in #for expression',
len_trim(ctx%content) + 1, 1), &
221 trim(ctx%content), ctx%line))
224 temp = temp(paren_start + 1:paren_end - 1)
228 if (temp(pos:pos) ==
',')
then
233 if (
len_trim(temp) > 0) npar = npar + 1
235 if (.not.
allocated(fmacros))
allocate(fmacros(0))
236 if (.not.
is_defined(name, fmacros, imacro))
then
237 call add(fmacros, name,
'')
240 fmacros(imacro) =
macro(name,
'')
243 fmacros(imacro)%active = .false.
244 fmacros(imacro)%is_variadic = .false.
245 if (
allocated(fmacros(imacro)%params))
deallocate(fmacros(imacro)%params)
246 allocate(fmacros(imacro)%params(npar))
249 do while (pos <=
len_trim(temp) .and. i <= npar)
250 do while (pos <=
len_trim(temp) .and. temp(pos:pos) ==
' ')
255 do while (pos <=
len_trim(temp) .and. temp(pos:pos) /=
',' .and. temp(pos:pos) /=
' ')
259 fmacros(imacro)%params(i) = temp(paren_start:pos - 1)
262 if (temp(pos:pos) ==
',') pos = pos + 1
290 type(
context),
intent(inout) :: ctx
291 integer,
intent(in) :: ounit
292 type(c_funptr),
intent(in) :: p
293 type(
macro),
intent(in) :: macros(:)
294 character(*),
intent(in) :: token
297 character(:),
allocatable :: rst, tmp
299 type(
string),
allocatable :: params(:)
300 type(
macro),
allocatable :: ms(:)
303 call c_f_procpointer(p, preprocess)
308 if (depth + 1 <=
size_of(fmacros))
then
309 if (
allocated(fmacros(depth + 1)%params)) params = fmacros(depth + 1)%params
310 if (
allocated(fmacros(depth + 1)%params))
deallocate(fmacros(depth + 1)%params)
313 fmacros(depth + 1)%value = params(i)
314 fmacros(depth + 1)%active = .true.
315 ms = [fmacros(depth + 1), macros]
317 do j = 1, bodies(depth + 1)%nlines
318 if (
head(bodies(depth + 1)%lines(j)%chars) ==
'#')
then
319 if (
len(bodies(depth + 1)%lines(j)%chars) == 1)
then
322 rst = adjustl(
expand_macros(bodies(depth + 1)%lines(j)%chars, ms, stitch, &
323 global%implicit_continuation,
global%support_dollar_insert, ctx))
324 tmp = preprocess(rst, ounit, ctx%path, ctx%line, ms, stitch)
327 rst = adjustl(
expand_macros(bodies(depth + 1)%lines(j)%chars, ms, stitch,
global%implicit_continuation, &
328 global%support_dollar_insert, ctx))
329 tmp = preprocess(rst, ounit, ctx%path, ctx%line, ms, stitch)
334 call addline(bodies(depth),
string(tmp))
339 tmp = preprocess(rst, ounit, ctx%path, ctx%line, ms, stitch)
342 write(ounit,
'(A)') rst
346 call addline(bodies(depth),
string(
''))
348 write(ounit,
'(A)')
''
351 bodies(depth + 1)%nlines = 0
352 if (
allocated(bodies(depth + 1)%lines))
deallocate(bodies(depth + 1)%lines)
355 if (
allocated(params))
deallocate(params)
356 if (
allocated(ms))
deallocate(ms)
361 message=
'Unbalanced #for expression. Missing #for or #endfor directive.', &
368 if (
allocated(fmacros))
deallocate(fmacros)
369 do i = 1, max_for_depth
370 if (
allocated(bodies(i)%lines))
deallocate(bodies(i)%lines)
384 character(*),
intent(in) :: line
386 call addline(bodies(depth),
string(line))
401 subroutine addline(b, line)
402 type(body),
intent(inout) :: b
403 type(
string),
intent(in) :: line
405 type(
string),
allocatable :: tmp(:)
408 if (.not.
allocated(b%lines))
then
412 b%nlines = b%nlines + 1
414 if (b%nlines <= n)
then
415 b%lines(b%nlines) = line
417 allocate(tmp(n + body_buffer))
418 tmp(1:n) = b%lines(1:n)
420 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 innermost 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. This routine is typically used by the main pr...
type(global_settings), public global
Global preprocessor configuration instance.
character(:) function, allocatable, public expand_macros(line, macros, stitch, implicit_conti, dollar_insert, ctx)
Recursively expand user-defined macros.
logical function, public is_defined(name, macros, idx)
Determine whether a macro is currently defined.
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 character of the trimmed string.
Generic renderer for diagnostics and source excerpts.
Append macros to a macro table.
Abstract interface for line preprocessing callbacks.
Return the number of stored macro definitions.
Locate the position of a substring.
Return the trimmed length of a string object.
Return the length of a string object.
Remove trailing blanks from a string object.
Snapshot of a source location within the preprocessing stream.
Structured compiler diagnostic.
Diagnostic label identifying a region of source text.
Representation of a preprocessor macro.
Represents text as a sequence of ASCII code units. The derived type wraps an allocatable character ar...