343 function expand_macros(line, macros, stitch, implicit_conti, dollar_insert, ctx)
result(expanded)
344 character(*),
intent(in) :: line
345 type(
macro),
allocatable,
intent(inout) :: macros(:)
346 logical,
intent(out) :: stitch
347 logical,
intent(in) :: implicit_conti
348 logical,
intent(in) :: dollar_insert
349 type(context),
intent(in) :: ctx
350 character(:),
allocatable :: expanded
352 integer :: imacro, paren_level
353 type(digraph) :: graph
355 imacro = 0; paren_level = 0
356 graph = digraph(
size(macros))
359 expanded = expand_macros_internal(line, imacro, macros)
361 if (implicit_conti)
then
362 stitch = (tail(expanded) ==
'&') .or. paren_level > 0
364 stitch = (tail(expanded) ==
'&') .and. paren_level > 0
368 recursive function expand_macros_internal(line, imacro, macros)
result(expanded)
369 character(*),
intent(in) :: line
370 integer,
intent(in) :: imacro
371 type(
macro),
allocatable,
intent(inout) :: macros(:)
372 character(:),
allocatable :: expanded
374 character(:),
allocatable :: args_str, temp, va_args
375 character(:),
allocatable :: token1, token2, prefix, suffix
376 type(string) :: arg_values(max_params)
377 integer :: c, i, j, k, n, pos, start, arg_start, nargs
378 integer :: m_start, m_end, token1_start, token2_stop
379 logical :: isopened, found
381 integer,
allocatable :: indexes(:)
382 logical :: exists, ok, hasfunc
385 if (
size(macros) == 0)
return
386 isopened = .false.; hasfunc = .false.
388 do i = 1,
size(macros)
389 n = len_trim(macros(i));
if (n == 0) cycle
391 do while (c < len_trim(expanded))
393 if (expanded(c:c) ==
'"' .or. expanded(c:c) ==
"'")
then
394 if (.not. isopened)
then
396 quote = expanded(c:c)
398 if (expanded(c:c) == quote) isopened = .false.
402 if (c + n - 1 > len_trim(expanded))
exit
404 if (.not. hasfunc)
then
405 call update_func_macro(expanded, macros)
410 if (dollar_insert)
then
411 if (expanded(c:c) ==
'$')
then
412 if (c < len_trim(expanded))
then
413 if (expanded(c + 1:c + 1) ==
'{')
then
415 do while (j <= len_trim(expanded))
416 if (expanded(j:j) ==
'}')
exit
420 if (j <= len_trim(expanded))
then
421 token1 = trim(expanded(c + 2:j - 1))
423 temp = macros(k)%value
424 if (len(temp) == 0 .and. .not. macros(k)%active)
then
427 expanded = expanded(:c - 1) // temp // expanded(j + 1:)
428 if (len(temp) /= 0)
then
429 c = c + len_trim(temp) - 1
441 if (expanded(c:c + n - 1) == macros(i))
then
443 if (len_trim(expanded(c:)) > n)
then
444 found = verify(expanded(c + n:c + n),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
446 if (found .and. c > 1)
then
447 found = verify(expanded(c - 1:c - 1),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
456 ok =
allocated(macros(i)%params);
if (ok) ok =
size(macros(i)%params) > 0
457 if (ok .or. macros(i)%is_variadic)
then
458 if (start <= len(expanded))
then
459 if (expanded(start:start) ==
'(')
then
461 arg_start = start + 1
464 do while (j <= len(expanded) .and. paren_level > 0)
465 if (expanded(j:j) ==
'(') paren_level = paren_level + 1
466 if (expanded(j:j) ==
')') paren_level = paren_level - 1
467 if (paren_level == 1 .and. expanded(j:j) ==
',' .or. paren_level == 0)
then
468 if (nargs < max_params)
then
470 arg_values(nargs) = trim(adjustl(expanded(arg_start:j - 1)))
477 args_str = expanded(start:m_end)
478 temp = trim(macros(i)%value)
480 if (macros(i)%is_variadic)
then
481 if (nargs <
size(macros(i)%params))
then
482 call printf(render(diagnostic_report(level_error, &
483 message=
'Variadic macro issue', &
484 label=label_type(
'Too few arguments for macro ' // macros(i), start, m_end - &
486 source=trim(ctx%path)), &
491 do j =
size(macros(i)%params) + 1, nargs
492 if (j >
size(macros(i)%params) + 1) va_args = va_args //
', '
493 va_args = va_args // arg_values(j)
495 else if (nargs /=
size(macros(i)%params))
then
496 call printf(render(diagnostic_report(level_error, &
497 message=
'Function-like macro issue', &
498 label=label_type(
'Incorrect number of arguments for macro ' // macros(i), start, &
500 source=trim(ctx%path)), &
511 jloop:
do j = 1,
size(macros(i)%params)
513 wloop:
do while (c1 < len_trim(temp))
515 if (temp(c1:c1) ==
'"') opened = .not. opened
516 if (opened) cycle wloop
517 if (c1 + len_trim(macros(i)%params(j)) - 1 > len(temp)) cycle wloop
519 if (temp(c1:c1 + len_trim(macros(i)%params(j)) - 1) == trim(macros(i)%params(j))) &
524 cend = c1 + len_trim(macros(i)%params(j))
526 if (c1 == 1 .and. cend == l + 1)
then
528 else if (c1 > 1 .and. l == cend - 1)
then
529 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) &
531 else if (c1 <= 1 .and. cend <= l)
then
532 if (verify(temp(cend:cend),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) cycle &
535 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0 &
536 .or. verify(temp(cend:cend),
' #()[]<>$&;.,!/*-+\="' //
"'") /=&
541 c1 = c1 + len_trim(macros(i)%params(j)) - 1
542 start = pos + len_trim(macros(i)%params(j))
544 if (temp(pos - 1:pos - 1) ==
'#')
then
545 temp = trim(temp(:pos - 2) //
'"' // arg_values(j) //
'"' // trim(temp(&
548 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
550 elseif (pos > 2)
then
552 if (previous(temp, h1) ==
'#')
then
554 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // trim(&
557 if (temp(h1 - 1:h1 - 1) /=
'#')
then
558 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // &
561 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:&
566 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
569 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
580 pos = index(temp,
'##')
585 call printf(render(diagnostic_report(level_error, &
586 message=
'Syntax error', &
587 label=label_type(
'No token before ##', pos, 2), &
588 source=trim(ctx%path)), &
593 token1 = adjustr(temp(:k))
595 token1_start = index(token1,
' ')
596 if (token1_start > 0)
then
597 prefix = token1(:token1_start)
598 token1 = token1(token1_start + 1:)
603 if (k > len(temp))
then
604 call printf(render(diagnostic_report(level_error, &
605 message=
'Syntax error', &
606 label=label_type(
'No token after ##', pos, 2), &
607 source=trim(ctx%path)), &
613 token2 = adjustl(temp(k:))
614 token2_stop = index(token2,
' ')
615 if (token2_stop > 0)
then
616 suffix = token2(token2_stop:)
617 token2 = token2(:token2_stop - 1)
622 token1 = expand_macros_internal(token1, imacro, macros)
624 token2 = expand_macros_internal(token2, imacro, macros)
626 temp = trim(prefix // trim(token1) // trim(token2) // suffix)
633 if (macros(i)%is_variadic)
then
636 pos = index(temp,
'__VA_ARGS__')
638 start = pos + len(
'__VA_ARGS__') - 1
639 if (start < len(temp) .and. temp(start:start) ==
'_' &
640 .and. temp(start + 1:start + 1) ==
')')
then
641 temp = trim(temp(:pos - 1) // trim(va_args) //
')')
643 temp = trim(temp(:pos - 1) // trim(va_args) // trim(temp(start + 1:)))
647 pos = index(temp,
'__VA_OPT__')
649 start = pos + index(temp(pos:),
')') - 1
650 if (len_trim(va_args) > 0)
then
651 temp = trim(temp(:pos - 1)) // temp(pos + index(temp(pos:),
'('):start &
652 - 1) // trim(temp(start + 1:))
654 temp = trim(temp(:pos - 1)) // trim(temp(start + 1:))
662 call graph%add_edge(imacro, i)
663 if (.not. graph%is_circular(i))
then
664 temp = expand_macros_internal(temp, i, macros)
666 call printf(render(diagnostic_report(level_error, &
667 message=
'Failed macro expansion', &
668 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i)))&
670 source=trim(ctx%path)), &
674 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
678 temp = trim(macros(i)%value)
680 call graph%add_edge(imacro, i)
681 if ((.not. graph%is_circular(i)) .and. (.not. macros(i)%is_cyclic))
then
682 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
683 expanded = expand_macros_internal(expanded, imacro, macros)
685 call printf(render(diagnostic_report(level_error, &
686 message=
'Failed macro expansion', &
687 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i))), &
688 source=trim(ctx%path)), &
696 pos = index(expanded,
'&')
697 if (index(expanded,
'!') > pos .and. pos > 0) expanded = expanded(:pos + 1)