452 function expand_macros(line, macros, stitch, implicit_conti, dollar_insert, ctx)
result(expanded)
453 character(*),
intent(in) :: line
454 type(
macro),
allocatable,
intent(inout) :: macros(:)
455 logical,
intent(out) :: stitch
456 logical,
intent(in) :: implicit_conti
457 logical,
intent(in) :: dollar_insert
458 type(context),
intent(in) :: ctx
459 character(:),
allocatable :: expanded
461 integer :: imacro, paren_level
462 type(digraph) :: graph
464 imacro = 0; paren_level = 0
465 graph = digraph(
size(macros))
468 expanded = expand_macros_internal(line, imacro, macros)
470 if (implicit_conti)
then
471 stitch = (tail(expanded) ==
'&') .or. paren_level > 0
473 stitch = (tail(expanded) ==
'&') .and. paren_level > 0
477 recursive function expand_macros_internal(line, imacro, macros)
result(expanded)
478 character(*),
intent(in) :: line
479 integer,
intent(in) :: imacro
480 type(
macro),
allocatable,
intent(inout) :: macros(:)
481 character(:),
allocatable :: expanded
483 character(:),
allocatable :: args_str, temp, va_args
484 character(:),
allocatable :: token1, token2, prefix, suffix
485 type(string) :: arg_values(max_params)
486 integer :: c, i, j, k, n, pos, start, arg_start, nargs
487 integer :: m_start, m_end, token1_start, token2_stop
488 logical :: isopened, found
490 integer,
allocatable :: indexes(:)
491 logical :: exists, ok, hasfunc
494 if (
size(macros) == 0)
return
495 isopened = .false.; hasfunc = .false.
497 do i = 1,
size(macros)
498 n = len_trim(macros(i));
if (n == 0) cycle
500 do while (c < len_trim(expanded))
502 if (expanded(c:c) ==
'"' .or. expanded(c:c) ==
"'")
then
503 if (.not. isopened)
then
505 quote = expanded(c:c)
507 if (expanded(c:c) == quote) isopened = .false.
511 if (c + n - 1 > len_trim(expanded))
exit
513 if (.not. hasfunc)
then
514 call update_func_macro(expanded, macros)
519 if (dollar_insert)
then
520 if (expanded(c:c) ==
'$')
then
521 if (c < len_trim(expanded))
then
522 if (expanded(c + 1:c + 1) ==
'{')
then
524 do while (j <= len_trim(expanded))
525 if (expanded(j:j) ==
'}')
exit
529 if (j <= len_trim(expanded))
then
530 token1 = trim(expanded(c + 2:j - 1))
532 temp = macros(k)%value
533 if (len(temp) == 0 .and. .not. macros(k)%active)
then
536 expanded = expanded(:c - 1) // temp // expanded(j + 1:)
537 if (len(temp) /= 0)
then
538 c = c + len_trim(temp) - 1
550 if (expanded(c:c + n - 1) == macros(i))
then
552 if (len_trim(expanded(c:)) > n)
then
553 found = verify(expanded(c + n:c + n),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
555 if (found .and. c > 1)
then
556 found = verify(expanded(c - 1:c - 1),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
565 ok =
allocated(macros(i)%params);
if (ok) ok =
size(macros(i)%params) > 0
566 if (ok .or. macros(i)%is_variadic)
then
567 if (start <= len(expanded))
then
568 if (expanded(start:start) ==
'(')
then
570 arg_start = start + 1
573 do while (j <= len(expanded) .and. paren_level > 0)
574 if (expanded(j:j) ==
'(') paren_level = paren_level + 1
575 if (expanded(j:j) ==
')') paren_level = paren_level - 1
576 if (paren_level == 1 .and. expanded(j:j) ==
',' .or. paren_level == 0)
then
577 if (nargs < max_params)
then
579 arg_values(nargs) = trim(adjustl(expanded(arg_start:j - 1)))
586 args_str = expanded(start:m_end)
587 temp = trim(macros(i)%value)
589 if (macros(i)%is_variadic)
then
590 if (nargs <
size(macros(i)%params))
then
591 call printf(render(diagnostic_report(level_error, &
592 message=
'Variadic macro issue', &
593 label=label_type(
'Too few arguments for macro ' // macros(i), start, m_end - &
595 source=trim(ctx%path)), &
600 do j =
size(macros(i)%params) + 1, nargs
601 if (j >
size(macros(i)%params) + 1) va_args = va_args //
', '
602 va_args = va_args // arg_values(j)
604 else if (nargs /=
size(macros(i)%params))
then
605 call printf(render(diagnostic_report(level_error, &
606 message=
'Function-like macro issue', &
607 label=label_type(
'Incorrect number of arguments for macro ' // macros(i), start, &
609 source=trim(ctx%path)), &
620 jloop:
do j = 1,
size(macros(i)%params)
622 wloop:
do while (c1 < len_trim(temp))
624 if (temp(c1:c1) ==
'"') opened = .not. opened
625 if (opened) cycle wloop
626 if (c1 + len_trim(macros(i)%params(j)) - 1 > len(temp)) cycle wloop
628 if (temp(c1:c1 + len_trim(macros(i)%params(j)) - 1) == trim(macros(i)%params(j))) &
633 cend = c1 + len_trim(macros(i)%params(j))
635 if (c1 == 1 .and. cend == l + 1)
then
637 else if (c1 > 1 .and. l == cend - 1)
then
638 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) &
640 else if (c1 <= 1 .and. cend <= l)
then
641 if (verify(temp(cend:cend),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) cycle &
644 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0 &
645 .or. verify(temp(cend:cend),
' #()[]<>$&;.,!/*-+\="' //
"'") /=&
650 c1 = c1 + len_trim(macros(i)%params(j)) - 1
651 start = pos + len_trim(macros(i)%params(j))
653 if (temp(pos - 1:pos - 1) ==
'#')
then
654 temp = trim(temp(:pos - 2) //
'"' // arg_values(j) //
'"' // trim(temp(&
657 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
659 elseif (pos > 2)
then
661 if (previous(temp, h1) ==
'#')
then
663 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // trim(&
666 if (temp(h1 - 1:h1 - 1) /=
'#')
then
667 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // &
670 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:&
675 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
678 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
689 pos = index(temp,
'##')
694 call printf(render(diagnostic_report(level_error, &
695 message=
'Syntax error', &
696 label=label_type(
'No token before ##', pos, 2), &
697 source=trim(ctx%path)), &
702 token1 = adjustr(temp(:k))
704 token1_start = index(token1,
' ')
705 if (token1_start > 0)
then
706 prefix = token1(:token1_start)
707 token1 = token1(token1_start + 1:)
712 if (k > len(temp))
then
713 call printf(render(diagnostic_report(level_error, &
714 message=
'Syntax error', &
715 label=label_type(
'No token after ##', pos, 2), &
716 source=trim(ctx%path)), &
722 token2 = adjustl(temp(k:))
723 token2_stop = index(token2,
' ')
724 if (token2_stop > 0)
then
725 suffix = token2(token2_stop:)
726 token2 = token2(:token2_stop - 1)
731 token1 = expand_macros_internal(token1, imacro, macros)
733 token2 = expand_macros_internal(token2, imacro, macros)
735 temp = trim(prefix // trim(token1) // trim(token2) // suffix)
742 if (macros(i)%is_variadic)
then
745 pos = index(temp,
'__VA_ARGS__')
747 start = pos + len(
'__VA_ARGS__') - 1
748 if (start < len(temp) .and. temp(start:start) ==
'_' &
749 .and. temp(start + 1:start + 1) ==
')')
then
750 temp = trim(temp(:pos - 1) // trim(va_args) //
')')
752 temp = trim(temp(:pos - 1) // trim(va_args) // trim(temp(start + 1:)))
756 pos = index(temp,
'__VA_OPT__')
758 start = pos + index(temp(pos:),
')') - 1
759 if (len_trim(va_args) > 0)
then
760 temp = trim(temp(:pos - 1)) // temp(pos + index(temp(pos:),
'('):start &
761 - 1) // trim(temp(start + 1:))
763 temp = trim(temp(:pos - 1)) // trim(temp(start + 1:))
771 call graph%add_edge(imacro, i)
772 if (.not. graph%is_circular(i))
then
773 temp = expand_macros_internal(temp, i, macros)
775 call printf(render(diagnostic_report(level_error, &
776 message=
'Failed macro expansion', &
777 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i)))&
779 source=trim(ctx%path)), &
783 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
787 temp = trim(macros(i)%value)
789 call graph%add_edge(imacro, i)
790 if ((.not. graph%is_circular(i)) .and. (.not. macros(i)%is_cyclic))
then
791 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
792 expanded = expand_macros_internal(expanded, imacro, macros)
794 call printf(render(diagnostic_report(level_error, &
795 message=
'Failed macro expansion', &
796 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i))), &
797 source=trim(ctx%path)), &
805 pos = index(expanded,
'&')
806 if (index(expanded,
'!') > pos .and. pos > 0) expanded = expanded(:pos + 1)