299 function expand_macros(line, macros, stitch, implicit_conti, ctx)
result(expanded)
300 character(*),
intent(in) :: line
301 type(
macro),
intent(in) :: macros(:)
302 logical,
intent(out) :: stitch
303 logical,
intent(in) :: implicit_conti
304 type(context),
intent(in) :: ctx
305 character(:),
allocatable :: expanded
307 integer :: imacro, paren_level
308 type(digraph) :: graph
310 imacro = 0; paren_level = 0
311 graph = digraph(
size(macros))
314 expanded = expand_macros_internal(line, imacro, macros)
316 if (implicit_conti)
then
317 stitch = (tail(expanded) ==
'&') .or. paren_level > 0
319 stitch = (tail(expanded) ==
'&') .and. paren_level > 0
323 recursive function expand_macros_internal(line, imacro, macros)
result(expanded)
324 character(*),
intent(in) :: line
325 integer,
intent(in) :: imacro
326 type(
macro),
intent(in) :: macros(:)
327 character(:),
allocatable :: expanded
329 character(:),
allocatable :: args_str, temp, va_args
330 character(:),
allocatable :: token1, token2, prefix, suffix
331 type(string) :: arg_values(max_params)
332 integer :: c, i, j, k, n, pos, start, arg_start, nargs
333 integer :: m_start, m_end, token1_start, token2_stop
334 logical :: isopened, found
336 integer,
allocatable :: indexes(:)
340 if (
size(macros) == 0)
return
343 do i = 1,
size(macros)
344 n = len_trim(macros(i));
if (n == 0) cycle
346 do while (c < len_trim(expanded))
348 if (expanded(c:c) ==
'"' .or. expanded(c:c) ==
"'")
then
349 if (.not. isopened)
then
351 quote = expanded(c:c)
353 if (expanded(c:c) == quote) isopened = .false.
357 if (c + n - 1 > len_trim(expanded))
exit
360 if (expanded(c:c + n - 1) == macros(i))
then
362 if (len_trim(expanded(c:)) > n)
then
363 found = verify(expanded(c + n:c + n),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
365 if (found .and. c > 1)
then
366 found = verify(expanded(c - 1:c - 1),
' ()[]<>&;.,^~!/*-+\="' //
"'") == 0
375 if (
size(macros(i)%params) > 0 .or. macros(i)%is_variadic)
then
376 if (start <= len(expanded))
then
377 if (expanded(start:start) ==
'(')
then
379 arg_start = start + 1
382 do while (j <= len(expanded) .and. paren_level > 0)
383 if (expanded(j:j) ==
'(') paren_level = paren_level + 1
384 if (expanded(j:j) ==
')') paren_level = paren_level - 1
385 if (paren_level == 1 .and. expanded(j:j) ==
',' .or. paren_level == 0)
then
386 if (nargs < max_params)
then
388 arg_values(nargs) = trim(adjustl(expanded(arg_start:j - 1)))
395 args_str = expanded(start:m_end)
396 temp = trim(macros(i)%value)
398 if (macros(i)%is_variadic)
then
399 if (nargs <
size(macros(i)%params))
then
400 call printf(render(diagnostic_report(level_error, &
401 message=
'Variadic macro issue', &
402 label=label_type(
'Too few arguments for macro ' // macros(i), start, m_end - start), &
403 source=trim(ctx%path)), &
408 do j =
size(macros(i)%params) + 1, nargs
409 if (j >
size(macros(i)%params) + 1) va_args = va_args //
', '
410 va_args = va_args // arg_values(j)
412 else if (nargs /=
size(macros(i)%params))
then
413 call printf(render(diagnostic_report(level_error, &
414 message=
'Function-like macro issue', &
415 label=label_type(
'Incorrect number of arguments for macro ' // macros(i), start, m_end - start), &
416 source=trim(ctx%path)), &
427 jloop:
do j = 1,
size(macros(i)%params)
429 wloop:
do while (c1 < len_trim(temp))
431 if (temp(c1:c1) ==
'"') opened = .not. opened
432 if (opened) cycle wloop
433 if (c1 + len_trim(macros(i)%params(j)) - 1 > len(temp)) cycle wloop
435 if (temp(c1:c1 + len_trim(macros(i)%params(j)) - 1) == trim(macros(i)%params(j))) &
440 cend = c1 + len_trim(macros(i)%params(j))
442 if (c1 == 1 .and. cend == l + 1)
then
444 else if (c1 > 1 .and. l == cend - 1)
then
445 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) &
447 else if (c1 <= 1 .and. cend <= l)
then
448 if (verify(temp(cend:cend),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0) cycle &
451 if (verify(temp(c1 - 1:c1 - 1),
' #()[]<>&;.,!/*-+\="' //
"'") /= 0 &
452 .or. verify(temp(cend:cend),
' #()[]<>$&;.,!/*-+\="' //
"'") /=&
457 c1 = c1 + len_trim(macros(i)%params(j)) - 1
458 start = pos + len_trim(macros(i)%params(j))
460 if (temp(pos - 1:pos - 1) ==
'#')
then
461 temp = trim(temp(:pos - 2) //
'"' // arg_values(j) //
'"' // trim(temp(&
464 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
466 elseif (pos > 2)
then
468 if (previous(temp, h1) ==
'#')
then
470 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // trim(&
473 if (temp(h1 - 1:h1 - 1) /=
'#')
then
474 temp = trim(temp(:h1 - 1) //
'"' // arg_values(j) //
'"' // &
477 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:&
482 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
485 temp = trim(temp(:pos - 1) // arg_values(j) // trim(temp(start:)))
496 pos = index(temp,
'##')
501 call printf(render(diagnostic_report(level_error, &
502 message=
'Synthax error', &
503 label=label_type(
'No token before ##', pos, 2), &
504 source=trim(ctx%path)), &
509 token1 = adjustr(temp(:k))
511 token1_start = index(token1,
' ')
512 if (token1_start > 0)
then
513 prefix = token1(:token1_start)
514 token1 = token1(token1_start + 1:)
519 if (k > len(temp))
then
520 call printf(render(diagnostic_report(level_error, &
521 message=
'Synthax error', &
522 label=label_type(
'No token after ##', pos, 2), &
523 source=trim(ctx%path)), &
529 token2 = adjustl(temp(k:))
530 token2_stop = index(token2,
' ')
531 if (token2_stop > 0)
then
532 suffix = token2(token2_stop:)
533 token2 = token2(:token2_stop - 1)
538 token1 = expand_macros_internal(token1, imacro, macros)
540 token2 = expand_macros_internal(token2, imacro, macros)
542 temp = trim(prefix // trim(token1) // trim(token2) // suffix)
549 if (macros(i)%is_variadic)
then
552 pos = index(temp,
'__VA_ARGS__')
554 start = pos + len(
'__VA_ARGS__') - 1
555 if (start < len(temp) .and. temp(start:start) ==
'_' &
556 .and. temp(start + 1:start + 1) ==
')')
then
557 temp = trim(temp(:pos - 1) // trim(va_args) //
')')
559 temp = trim(temp(:pos - 1) // trim(va_args) // trim(temp(start + 1:)))
563 pos = index(temp,
'__VA_OPT__')
565 start = pos + index(temp(pos:),
')') - 1
566 if (len_trim(va_args) > 0)
then
567 temp = trim(temp(:pos - 1)) // temp(pos + index(temp(pos:),
'('):start &
568 - 1) // trim(temp(start + 1:))
570 temp = trim(temp(:pos - 1)) // trim(temp(start + 1:))
578 call graph%add_edge(imacro, i)
579 if (.not. graph%is_circular(i))
then
580 temp = expand_macros_internal(temp, i, macros)
582 call printf(render(diagnostic_report(level_error, &
583 message=
'Failed macro expansion', &
584 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i))), &
585 source=trim(ctx%path)), &
589 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
593 temp = trim(macros(i)%value)
595 call graph%add_edge(imacro, i)
596 if ((.not. graph%is_circular(i)) .and. (.not. macros(i)%is_cyclic))
then
597 expanded = trim(expanded(:m_start - 1) // trim(temp) // expanded(m_end + 1:))
598 expanded = expand_macros_internal(expanded, imacro, macros)
600 call printf(render(diagnostic_report(level_error, &
601 message=
'Failed macro expansion', &
602 label=label_type(
'Circular macro detected', index(temp, macros(i)), len(macros(i))), &
603 source=trim(ctx%path)), &
611 pos = index(expanded,
'&')
612 if (index(expanded,
'!') > pos .and. pos > 0) expanded = expanded(:pos + 1)