458 function expand_macros(line, macros, stitch, implicit_conti, dollar_insert, ctx)
result(expanded)
459 character(*),
intent(in) :: line
460 type(
macro),
allocatable,
intent(inout) :: macros(:)
461 logical,
intent(out) :: stitch
462 logical,
intent(in) :: implicit_conti
463 logical,
intent(in) :: dollar_insert
464 type(context),
intent(in) :: ctx
465 character(:),
allocatable :: expanded
467 integer :: imacro, paren_level
468 type(digraph) :: graph
470 imacro = 0; paren_level = 0
471 graph = digraph(
size(macros))
474 expanded = expand_macros_internal(line, imacro, macros)
476 if (implicit_conti)
then
477 stitch = (tail(expanded) ==
'&') .or. paren_level > 0
479 stitch = (tail(expanded) ==
'&') .and. paren_level > 0
483 recursive function expand_macros_internal(line, imacro, macros)
result(expanded)
484 character(*),
intent(in) :: line
485 integer,
intent(in) :: imacro
486 type(
macro),
allocatable,
intent(inout) :: macros(:)
487 character(:),
allocatable :: expanded
489 character(:),
allocatable :: args_str, temp, va_args
490 character(:),
allocatable :: token1, token2, prefix, suffix
491 type(string) :: arg_values(max_params)
492 integer :: c, i, j, k, n, pos, start, arg_start, nargs
493 integer :: m_start, m_end, token1_start, token2_stop
494 logical :: isopened, found
496 integer,
allocatable :: indexes(:)
497 logical :: exists, ok, hasfunc
500 if (
size(macros) == 0)
return
501 isopened = .false.; hasfunc = .false.
503 do i = 1,
size(macros)
504 n = len_trim(macros(i));
if (n == 0) cycle
506 do while (c < len_trim(expanded))
508 if (expanded(c:c) ==
'"' .or. expanded(c:c) ==
"'")
then
509 if (.not. isopened)
then
511 quote = expanded(c:c)
513 if (expanded(c:c) == quote) isopened = .false.
517 if (c + n - 1 > len_trim(expanded))
exit
519 if (.not. hasfunc)
then
520 call update_func_macro(expanded, macros)
525 if (dollar_insert)
then
526 if (expanded(c:c) ==
'$')
then
527 if (c < len_trim(expanded))
then
528 if (expanded(c + 1:c + 1) ==
'{')
then
530 do while (j <= len_trim(expanded))
531 if (expanded(j:j) ==
'}')
exit
535 if (j <= len_trim(expanded))
then
536 token1 = trim(expanded(c + 2:j - 1))
538 temp = macros(k)%value
539 if (len(temp) == 0 .and. .not. macros(k)%active)
then
542 expanded = expanded(:c - 1) // temp // expanded(j + 1:)
543 if (len(temp) /= 0)
then
544 c = c + len_trim(temp) - 1
556 if (expanded(c:c + n - 1) == macros(i))
then
558 if (len_trim(expanded(c:)) > n)
then
559 found = verify(expanded(c + n:c + n),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
561 if (found .and. c > 1)
then
562 found = verify(expanded(c - 1:c - 1),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
571 ok =
allocated(macros(i)%params);
if (ok) ok =
size(macros(i)%params) > 0
572 if (ok .or. macros(i)%is_variadic)
then
573 if (start <= len(expanded))
then
574 if (expanded(start:start) ==
'(')
then
576 arg_start = start + 1
579 do while (j <= len(expanded) .and. paren_level > 0)
580 if (expanded(j:j) ==
'(') paren_level = paren_level + 1
581 if (expanded(j:j) ==
')') paren_level = paren_level - 1
582 if (paren_level == 1 .and. expanded(j:j) ==
',' .or. paren_level == 0)
then
583 if (nargs < max_params)
then
585 arg_values(nargs) = trim(adjustl(expanded(arg_start:j - 1)))
592 args_str = expanded(start:m_end)
593 temp = trim(macros(i)%value)
595 if (macros(i)%is_variadic)
then
596 if (nargs <
size(macros(i)%params))
then
597 call printf(render(diagnostic_report(level_error, &
598 message=
'Variadic macro issue', &
599 label=label_type(
'Too few arguments for macro ' // macros(i), start, m_end - &
601 source=trim(ctx%path)), &
606 do j =
size(macros(i)%params) + 1, nargs
607 if (j >
size(macros(i)%params) + 1) va_args = va_args //
', '
608 va_args = va_args // arg_values(j)
610 else if (nargs /=
size(macros(i)%params))
then
611 call printf(render(diagnostic_report(level_error, &
612 message=
'Function-like macro issue', &
613 label=label_type(
'Incorrect number of arguments for macro ' // macros(i), start, &
615 source=trim(ctx%path)), &
626 jloop:
do j = 1,
size(macros(i)%params)
628 wloop:
do while (c1 < len_trim(temp))
630 if (temp(c1:c1) ==
'"') opened = .not. opened
631 if (opened) cycle wloop
632 if (c1 + len_trim(macros(i)%params(j)) - 1 > len(temp)) cycle wloop
634 if (temp(c1:c1 + len_trim(macros(i)%params(j)) - 1) == trim(macros(i)%params(j))) &
639 cend = c1 + len_trim(macros(i)%params(j))
641 if (c1 == 1 .and. cend == l + 1)
then
643 else if (c1 > 1 .and. l == cend - 1)
then
644 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) &
646 else if (c1 <= 1 .and. cend <= l)
then
647 if (verify(temp(cend:cend),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) cycle &
650 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0 &
651 .or. verify(temp(cend:cend),
' #()[]<>$&;.,!/*-+\="' //
"'") /=&
656 c1 = c1 + len_trim(macros(i)%params(j)) - 1
657 start = pos + len_trim(macros(i)%params(j))
659 if (temp(pos - 1:pos - 1) ==
'#')
then
660 temp = trim(temp(:pos - 2) //
'"' // arg_values(j) //
'"' // trim(temp(&
663 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
665 elseif (pos > 2)
then
667 if (previous(temp, h1) ==
'#')
then
669 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // trim(&
672 if (temp(h1 - 1:h1 - 1) /=
'#')
then
673 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // &
676 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:&
681 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
684 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
695 pos = index(temp,
'##')
700 call printf(render(diagnostic_report(level_error, &
701 message=
'Syntax error', &
702 label=label_type(
'No token before ##', pos, 2), &
703 source=trim(ctx%path)), &
708 token1 = adjustr(temp(:k))
710 token1_start = index(token1,
' ')
711 if (token1_start > 0)
then
712 prefix = token1(:token1_start)
713 token1 = token1(token1_start + 1:)
718 if (k > len(temp))
then
719 call printf(render(diagnostic_report(level_error, &
720 message=
'Syntax error', &
721 label=label_type(
'No token after ##', pos, 2), &
722 source=trim(ctx%path)), &
728 token2 = adjustl(temp(k:))
729 token2_stop = index(token2,
' ')
730 if (token2_stop > 0)
then
731 suffix = token2(token2_stop:)
732 token2 = token2(:token2_stop - 1)
737 token1 = expand_macros_internal(token1, imacro, macros)
739 token2 = expand_macros_internal(token2, imacro, macros)
741 temp = trim(prefix // trim(token1) // trim(token2) // suffix)
748 if (macros(i)%is_variadic)
then
751 pos = index(temp,
'__VA_ARGS__')
753 start = pos + len(
'__VA_ARGS__') - 1
754 if (start < len(temp) .and. temp(start:start) ==
'_' &
755 .and. temp(start + 1:start + 1) ==
')')
then
756 temp = trim(temp(:pos - 1) // trim(va_args) //
')')
758 temp = trim(temp(:pos - 1) // trim(va_args) // trim(temp(start + 1:)))
762 pos = index(temp,
'__VA_OPT__')
764 start = pos + index(temp(pos:),
')') - 1
765 if (len_trim(va_args) > 0)
then
766 temp = trim(temp(:pos - 1)) // temp(pos + index(temp(pos:),
'('):start &
767 - 1) // trim(temp(start + 1:))
769 temp = trim(temp(:pos - 1)) // trim(temp(start + 1:))
777 call graph%add_edge(imacro, i)
778 if (.not. graph%is_circular(i))
then
779 temp = expand_macros_internal(temp, i, macros)
781 call printf(render(diagnostic_report(level_error, &
782 message=
'Failed macro expansion', &
783 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i)))&
785 source=trim(ctx%path)), &
789 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
793 temp = trim(macros(i)%value)
795 call graph%add_edge(imacro, i)
796 if ((.not. graph%is_circular(i)) .and. (.not. macros(i)%is_cyclic))
then
797 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
798 expanded = expand_macros_internal(expanded, imacro, macros)
800 call printf(render(diagnostic_report(level_error, &
801 message=
'Failed macro expansion', &
802 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i))), &
803 source=trim(ctx%path)), &
811 pos = index(expanded,
'&')
812 if (index(expanded,
'!') > pos .and. pos > 0) expanded = expanded(:pos + 1)