194 function expand_all(line, macros, filepath, iline, stitch, has_extra)
result(expanded)
195 character(*),
intent(in) :: line
196 type(
macro),
intent(in) :: macros(:)
197 character(*),
intent(in) :: filepath
198 integer,
intent(in) :: iline
199 logical,
intent(in) :: has_extra
200 logical,
intent(out) :: stitch
201 character(:),
allocatable :: expanded
203 integer :: pos, start, sep, dot
204 type(datetime) :: date
212 pos = index(expanded,
'__FILENAME__')
214 start = pos + len(
'__FILENAME__')
215 expanded = trim(expanded(:pos - 1) //
'"' // filename(filepath, .true.) //
'"' // trim(expanded(start:)))
216 if (verbose) print *,
"Substituted __FILENAME__ with '", trim(filepath),
"', expanded: '", trim(expanded),
"'"
224 pos = index(expanded,
'__FILE__')
226 start = pos + len(
'__FILE__')
227 expanded = trim(expanded(:pos - 1) //
'"' // trim(filepath) //
'"' // trim(expanded(start:)))
228 if (verbose) print *,
"Substituted __FILE__ with '", trim(filepath),
"', expanded: '", trim(expanded),
"'"
235 pos = index(expanded,
'__LINE__')
238 start = pos + len(
'__LINE__')
239 expanded = trim(expanded(:pos - 1) //
tostring(iline) // trim(expanded(start:)))
240 if (verbose) print *,
"Substituted __LINE__ with '", iline,
"', expanded: '", trim(expanded),
"'"
248 pos = index(expanded,
'__DATE__')
251 start = pos + len(
'__DATE__')
252 expanded = trim(expanded(:pos - 1) //
'"' // date%to_string(
'MMM-dd-yyyy') //
'"' // trim(expanded(start:)))
253 if (verbose) print *,
"Substituted __DATE__ with '", date%to_string(
'MMM-dd-yyyy'),
"', expanded: '", trim(&
262 pos = index(expanded,
'__TIME__')
265 start = pos + len(
'__TIME__')
266 expanded = trim(expanded(:pos - 1) //
'"' // date%to_string(
'HH:mm:ss') //
'"' // trim(expanded(start:)))
267 if (verbose) print *,
"Substituted __TIME__ with '", date%to_string(
'HH:mm:ss'),
"', expanded: '", trim(&
277 pos = index(expanded,
'__TIMESTAMP__')
280 start = pos + len(
'__TIMESTAMP__')
281 expanded = trim(expanded(:pos - 1) //
'"' // date%to_string(
'ddd MM yyyy') //
' ' // date%to_string(
'HH:mm:ss'&
282 &) //
'"' // trim(expanded(start:)))
283 if (verbose) print *,
"Substituted __TIMESTAMP__ with '", date%to_string(
'ddd MM yyyy') //
' ' // date%&
284 to_string(
'HH:mm:ss'),
"', expanded: '", trim(expanded),
"'"
309 character(*),
intent(in) :: line
310 type(
macro),
intent(in) :: macros(:)
311 logical,
intent(out) :: stitch
312 character(:),
allocatable :: expanded
314 integer :: imacro, paren_level
315 type(digraph) :: graph
317 imacro = 0; paren_level = 0
318 graph = digraph(
size(macros))
321 expanded = expand_macros_internal(line, imacro, macros)
323 stitch = stitch .or. paren_level > 0
326 recursive function expand_macros_internal(line, imacro, macros)
result(expanded)
327 character(*),
intent(in) :: line
328 integer,
intent(in) :: imacro
329 type(
macro),
intent(in) :: macros(:)
330 character(:),
allocatable :: expanded
332 character(:),
allocatable :: args_str, temp, va_args
333 character(:),
allocatable :: token1, token2, prefix, suffix
334 type(string) :: arg_values(max_params)
335 integer :: c, i, j, k, n, pos, start, arg_start, nargs
336 integer :: m_start, m_end, token1_start, token2_stop
337 logical :: isopened, found
339 integer,
allocatable :: indexes(:)
343 if (
size(macros) == 0)
return
345 if (verbose) print *,
"Initial expanded: '", trim(expanded),
"'"
347 do i = 1,
size(macros)
348 n = len_trim(macros(i))
351 do while (c < len_trim(expanded))
353 if (expanded(c:c) ==
'"' .or. expanded(c:c) ==
"'")
then
354 if (.not. isopened)
then
356 quote = expanded(c:c)
358 if (expanded(c:c) == quote) isopened = .false.
362 if (c + n - 1 > len_trim(expanded))
exit
365 if (expanded(c:c + n - 1) == macros(i))
then
367 if (len_trim(expanded(c:)) > n)
then
368 found = verify(expanded(c + n:c + n),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
370 if (found .and. c > 1)
then
371 found = verify(expanded(c - 1:c - 1),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
380 if (
size(macros(i)%params) > 0 .or. macros(i)%is_variadic)
then
381 if (start <= len(expanded))
then
382 if (expanded(start:start) ==
'(')
then
384 arg_start = start + 1
387 do while (j <= len(expanded) .and. paren_level > 0)
388 if (expanded(j:j) ==
'(') paren_level = paren_level + 1
389 if (expanded(j:j) ==
')') paren_level = paren_level - 1
390 if (paren_level == 1 .and. expanded(j:j) ==
',' .or. paren_level == 0)
then
391 if (nargs < max_params)
then
393 arg_values(nargs) = trim(adjustl(expanded(arg_start:j - 1)))
400 args_str = expanded(start:m_end)
401 if (verbose) print *,
"Expanding macro: ", macros(i),
", args: ", trim(args_str)
402 temp = trim(macros(i)%value)
404 if (macros(i)%is_variadic)
then
405 if (nargs <
size(macros(i)%params))
then
406 if (verbose) print *,
"Error: Too few arguments for macro ", macros(i)
410 do j =
size(macros(i)%params) + 1, nargs
411 if (j >
size(macros(i)%params) + 1) va_args = va_args //
', '
412 va_args = va_args // arg_values(j)
414 if (verbose) print *,
"__VA_ARGS__: '", trim(va_args),
"'"
415 else if (nargs /=
size(macros(i)%params))
then
416 if (verbose) print *,
"Error: Incorrect number of arguments for macro ", macros(i)
426 jloop:
do j = 1,
size(macros(i)%params)
428 wloop:
do while (c1 < len_trim(temp))
430 if (temp(c1:c1) ==
'"') opened = .not. opened
431 if (opened) cycle wloop
432 if (c1 + len_trim(macros(i)%params(j)) - 1 > len(temp)) cycle wloop
434 if (temp(c1:c1 + len_trim(macros(i)%params(j)) - 1) == trim(macros(i)%params(j))) &
439 cend = c1 + len_trim(macros(i)%params(j))
441 if (c1 == 1 .and. cend == l + 1)
then
443 else if (c1 > 1 .and. l == cend - 1)
then
444 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) &
446 else if (c1 <= 1 .and. cend <= l)
then
447 if (verify(temp(cend:cend),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) cycle &
450 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0 &
451 .or. verify(temp(cend:cend),
' #()[]<>$&;.,!/*-+\="' //
"'") /=&
456 c1 = c1 + len_trim(macros(i)%params(j)) - 1
457 start = pos + len_trim(macros(i)%params(j))
459 if (temp(pos - 1:pos - 1) ==
'#')
then
460 temp = trim(temp(:pos - 2) //
'"' // arg_values(j) //
'"' // trim(temp(&
463 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
465 elseif (pos > 2)
then
467 if (previous(temp, h1) ==
'#')
then
469 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // trim(&
472 if (temp(h1 - 1:h1 - 1) /=
'#')
then
473 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // &
476 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:&
481 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
484 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
486 if (verbose) print *,
"Substituted param ", j,
": '", macros(i)%params(j), &
488 arg_values(j),
"', temp: '", trim(temp),
"'"
498 pos = index(temp,
'##')
503 if (verbose) print *,
"Error: No token before ##"
507 token1 = adjustr(temp(:k))
509 token1_start = index(token1,
' ')
510 if (token1_start > 0)
then
511 prefix = token1(:token1_start)
512 token1 = token1(token1_start + 1:)
517 if (k > len(temp))
then
518 if (verbose) print *,
"Error: No token after ##"
523 token2 = adjustl(temp(k:))
524 token2_stop = index(token2,
' ')
525 if (token2_stop > 0)
then
526 suffix = token2(token2_stop:)
527 token2 = token2(:token2_stop - 1)
531 temp = trim(prefix // trim(token1) // trim(token2) // suffix)
532 if (verbose) print *,
"Concatenated '", trim(token1),
"' and '", trim(token2), &
533 "' to '", trim(token1) // trim(token2),
"', temp: '", trim(temp),
"'"
540 if (macros(i)%is_variadic)
then
543 pos = index(temp,
'__VA_ARGS__')
545 start = pos + len(
'__VA_ARGS__') - 1
546 if (start < len(temp) .and. temp(start:start) ==
'_' &
547 .and. temp(start + 1:start + 1) ==
')')
then
548 temp = trim(temp(:pos - 1) // trim(va_args) //
')')
550 temp = trim(temp(:pos - 1) // trim(va_args) // trim(temp(start + 1:)))
552 if (verbose) print *,
"Substituted __VA_ARGS__ with '", trim(va_args), &
553 "', temp: '", trim(temp),
"'"
555 pos = index(temp,
'__VA_OPT__')
557 start = pos + index(temp(pos:),
')') - 1
558 if (len_trim(va_args) > 0)
then
559 temp = trim(temp(:pos - 1)) // temp(pos + index(temp(pos:),
'('):start &
560 - 1) // trim(temp(start + 1:))
562 temp = trim(temp(:pos - 1)) // trim(temp(start + 1:))
570 if (verbose) print *,
"Before recursive call, temp: '", trim(temp),
"'"
571 call graph%add_edge(imacro, i)
572 if (.not. graph%is_circular(i))
then
573 temp = expand_macros_internal(temp, i, macros)
575 if (verbose) print *,
"Circular macro detected: '", macros(i),
"'"
578 if (verbose) print *,
"After recursive call, temp: '", trim(temp),
"'"
579 if (verbose) print *,
"Prefix: '", trim(expanded(:m_start - 1)),
"'"
580 if (verbose) print *,
"Temp: '", trim(temp),
"'"
581 if (verbose) print *,
"Suffix: '", trim(expanded(m_end + 1:)),
"'"
582 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
583 if (verbose) print *,
"After substitution, expanded: '", trim(expanded),
"'"
587 temp = trim(macros(i)%value)
589 call graph%add_edge(imacro, i)
590 if ((.not. graph%is_circular(i)) .and. (.not. macros(i)%is_cyclic))
then
591 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
592 expanded = expand_macros_internal(expanded, imacro, macros)
594 if (verbose) print *,
"Circular macro detected: '", macros(i),
"'"
597 if (verbose) print *,
"Simple macro expanded: '", trim(expanded),
"'"
602 pos = index(expanded,
'&')
603 if (index(expanded,
'!') > pos .and. pos > 0) expanded = expanded(:pos + 1)
604 stitch = tail(expanded) ==
'&'