104 implicit none;
private
131 character(1),
parameter :: nl = new_line(
'a')
132 character(1),
parameter :: escape = achar(27)
133 character(2),
parameter :: code_start = escape //
'['
134 character(1),
parameter :: code_end =
'm'
135 character(4),
parameter :: code_clear = code_start //
'0' // code_end
137 character(17),
parameter :: styles(1:2, 1:16) = reshape([ &
139 'ITALICS_ON ',
'3 ', &
140 'UNDERLINE_ON ',
'4 ', &
141 'INVERSE_ON ',
'7 ', &
142 'STRIKETHROUGH_ON ',
'9 ', &
143 'BOLD_OFF ',
'22 ', &
144 'ITALICS_OFF ',
'23 ', &
145 'UNDERLINE_OFF ',
'24 ', &
146 'INVERSE_OFF ',
'27 ', &
147 'STRIKETHROUGH_OFF',
'29 ', &
148 'FRAMED_ON ',
'51 ', &
149 'ENCIRCLED_ON ',
'52 ', &
150 'OVERLINED_ON ',
'53 ', &
151 'FRAMED_OFF ',
'54 ', &
152 'ENCIRCLED_OFF ',
'54 ', &
153 'OVERLINED_OFF ',
'55 ' &
156 character(15),
parameter :: colors_fg(1:2, 1:17) = reshape([ &
166 'BLACK_INTENSE ',
'90 ', &
167 'RED_INTENSE ',
'91 ', &
168 'GREEN_INTENSE ',
'92 ', &
169 'YELLOW_INTENSE ',
'93 ', &
170 'BLUE_INTENSE ',
'94 ', &
171 'MAGENTA_INTENSE',
'95 ', &
172 'CYAN_INTENSE ',
'96 ', &
173 'WHITE_INTENSE ',
'97 ' &
176 character(15),
parameter :: colors_bg(1:2, 1:17) = reshape([ &
186 'BLACK_INTENSE ',
'100 ', &
187 'RED_INTENSE ',
'101 ', &
188 'GREEN_INTENSE ',
'102 ', &
189 'YELLOW_INTENSE ',
'103 ', &
190 'BLUE_INTENSE ',
'104 ', &
191 'MAGENTA_INTENSE',
'105 ', &
192 'CYAN_INTENSE ',
'106 ', &
193 'WHITE_INTENSE ',
'107 ' &
209 module procedure :: render_diagnostic
210 module procedure :: render_text
211 module procedure :: render_text_with_label
212 module procedure :: render_text_with_labels
216 enumerator :: LEVEL_ERROR = 0
217 enumerator :: LEVEL_WARNING = 1
218 enumerator :: LEVEL_HELP = 2
219 enumerator :: LEVEL_NOTE = 3
220 enumerator :: LEVEL_INFO = 4
306 integer,
allocatable :: level
316 character(:),
allocatable :: text
320 module procedure :: label_new
321 module procedure :: label_new_with_line
341 character(:),
allocatable :: message
343 character(:),
allocatable :: source
351 module procedure diagnostic_new
356 integer :: first, finish
366 pure function colorize(string, foreground, background, style)
result(res)
367 character(*),
intent(in) :: string
368 character(*),
intent(in),
optional :: foreground
369 character(*),
intent(in),
optional :: background
370 character(*),
intent(in),
optional :: style
371 character(:),
allocatable :: res
377 if (
present(foreground))
then
378 i = color_index(upper(foreground))
379 if (i > 0) res = code_start // trim(colors_fg(2, i)) // code_end // res // code_clear
381 if (
present(background))
then
382 i = color_index(upper(background))
383 if (i > 0) res = code_start // trim(colors_bg(2, i)) // code_end // res // code_clear
385 if (
present(style))
then
386 i = style_index(upper(style))
387 if (i > 0) res = code_start // trim(styles(2, i)) // code_end // res // code_clear
395 elemental integer function color_index(color)
result(res)
396 character(*),
intent(in) :: color
401 do i = 1,
size(colors_fg, dim=2)
402 if (trim(colors_fg(1, i)) == trim(adjustl(color)))
then
410 elemental integer function style_index(style)
result(res)
411 character(*),
intent(in) :: style
416 do i = 1,
size(styles, dim=2)
417 if (trim(styles(1, i)) == trim(adjustl(style)))
then
425 elemental function upper(string)
426 character(*),
intent(in) :: string
427 character(len(string)) :: upper
429 integer,
parameter :: a = iachar(
'a'), z = iachar(
'z'), case_diff = iachar(
'a') - iachar(
'A')
432 do i = 1, len(string)
433 ichar = iachar(string(i:i))
434 if (ichar >= a .and. ichar <= z) ichar = ichar - case_diff
435 upper(i:i) = achar(ichar)
439 type(
label_type)
pure function label_new(text, first, length, level) result(that)
440 character(*),
intent(in) :: text
441 integer,
intent(in) :: first
442 integer,
intent(in) :: length
443 integer,
intent(in),
optional :: level
447 that%first = max(1, first)
448 that%finish = that%first + length
449 that%primary = .true.
450 if (
present(level)) that%level = level
453 type(
label_type)
pure function label_new_with_line(line, text, first, length, primary, level) result(that)
454 integer,
intent(in) :: line
455 character(*),
intent(in) :: text
456 integer,
intent(in) :: first
457 integer,
intent(in) :: length
458 logical,
intent(in),
optional :: primary
459 integer,
intent(in),
optional :: level
463 that%first = max(1, first)
464 that%finish = that%first + length
465 if (
present(primary))
then
466 that%primary = primary
468 that%primary = .true.
470 if (
present(level)) that%level = level
479 type(
diagnostic_report) function diagnostic_new(level, message, source, label, diagnostic) result(that)
480 integer,
intent(in) :: level
481 character(*),
intent(in),
optional :: message
482 character(*),
intent(in),
optional :: source
483 type(
label_type),
intent(in),
optional :: label(..)
489 if (
present(message)) that%message = message
490 if (
present(source)) that%source = source
491 if (
present(label))
then
492 if (
allocated(that%label))
deallocate(that%label)
495 allocate(that%label(1))
496 that%label(1) = label
497 if (.not.
allocated(that%label(1)%level)) that%label(1)%level = level
499 allocate(that%label, source=label)
500 do i = 1,
size(label)
501 if (.not.
allocated(that%label(i)%level)) that%label(i)%level = level
505 if (
present(diagnostic)) that%sub = diagnostic
507 if (
allocated(that%label))
then
508 if (.not. any(that%label(:)%primary))
then
509 that%label(1)%primary = .true.
514 pure function line_tokens(input)
result(res)
515 character(*),
intent(in) :: input
516 type(line_token),
allocatable :: res(:)
518 integer :: first, finish
520 if (len(input) == 0)
then
522 res(1)=line_token(1,0)
526 first = 1; finish = 0
528 do while (first <= len(input))
529 finish = index(input(first + 1:), nl) + first - 1
530 if (finish < first)
then
534 res = [res, line_token(first, finish)]
536 first = finish + (1 + len(nl))
540 pure recursive function render_diagnostic(diag, input, linemum)
result(res)
542 character(*),
intent(in) :: input
543 integer,
intent(in),
optional :: linemum
544 character(:),
allocatable :: res
548 res = render_message(diag%level, diag%message)
550 if (
allocated(diag%label))
then
551 res = res // nl // render_text_with_labels(input, diag%label, source=diag%source, linemum=linemum)
553 res = res // nl // render_text_with_labels(input, [
label_type(
'', 1, len_trim(input))], source=diag%source, linemum=&
557 if (
allocated(diag%sub))
then
558 do i = 1,
size(diag%sub)
559 res = res // nl // render_diagnostic(diag%sub(i), input, linemum)
564 pure function render_message(level, message)
result(res)
565 integer,
intent(in) :: level
566 character(*),
intent(in),
optional :: message
567 character(:),
allocatable :: res
569 if (
present(message))
then
570 res = level_name(level) // colorize(
': ' // message, style=
'bold_on')
572 res = level_name(level)
576 pure function level_name(level)
result(res)
577 integer,
intent(in) :: level
578 character(:),
allocatable :: res
580 character(:),
allocatable :: name, fg
584 name=
'error'; fg=
'red'
586 name=
'warning'; fg=
'yellow'
588 name=
'help'; fg=
'cyan'
590 name=
'note'; fg=
'blue'
592 name=
'info'; fg=
'magenta'
594 name=
'unknown'; fg=
'blue'
597 res = colorize(name, foreground=fg, style=
'bold_on')
600 pure function render_source(source, offset)
result(res)
601 character(*),
intent(in) :: source
602 integer,
intent(in) :: offset
603 character(:),
allocatable :: res
605 res = repeat(
' ', offset) // colorize(
'-->', foreground=
'blue') //
' ' // source
608 pure function render_text(input, source, linenum)
result(res)
609 character(*),
intent(in) :: input
610 character(*),
intent(in),
optional :: source
611 integer,
intent(in),
optional :: linenum
612 character(:),
allocatable :: res
614 integer :: i, offset, iline
615 type(line_token),
allocatable :: token(:)
617 iline = 1;
if (
present(linenum)) iline = linenum
618 token = line_tokens(input)
619 offset = integer_width(iline)
621 if (
present(source))
then
622 res = render_source(source, offset) // nl // &
623 repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue')
625 res = repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue')
628 do i = 1,
size(token)
629 res = res // nl // render_line(input(token(i)%first:token(i)%finish), to_string(iline + i - 1, offset))
631 res = res // nl // repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue')
634 pure function render_text_with_label(input, label, source, linenum)
result(res)
635 character(*),
intent(in) :: input
637 character(*),
intent(in),
optional :: source
638 integer,
intent(in),
optional :: linenum
639 character(:),
allocatable :: res
641 res = render_text_with_labels(input, [label], source, linenum)
644 pure function render_text_with_labels(input, labels, source, linemum)
result(res)
645 character(*),
intent(in) :: input
647 character(*),
intent(in),
optional :: source
648 integer,
intent(in),
optional :: linemum
649 character(:),
allocatable :: res
651 integer :: i, j, offset, first, finish, iline
652 type(line_token),
allocatable :: token(:)
653 logical,
allocatable :: display(:)
655 token = line_tokens(input)
656 first = max(1, minval(labels%line) - 1)
657 finish = min(
size(token), maxval(labels%line) + 1)
658 iline = 1;
if (
present(linemum)) iline = linemum
659 offset = integer_width(iline)
662 do j = 1,
size(labels)
663 if (labels(j)%primary)
then
669 if (
present(source))
then
670 res = render_source(source, offset) //
':' // &
671 to_string(labels(i)%line) //
':' // &
672 to_string(labels(i)%first) //
'-' // to_string(labels(i)%finish) // nl // &
673 repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue')
675 res = repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue')
678 allocate(display(first:finish), source=.false.)
679 do j = 1,
size(labels)
680 display(max(first, labels(j)%line - 1):min(finish, labels(j)%line + 1)) = .true.
684 if (.not. display(i))
then
686 if (display(i - 1))
then
688 repeat(
' ', offset + 1) // colorize(
':', foreground=
'blue')
695 & render_line(input(token(i)%first:token(i)%finish), &
696 & to_string(iline + i - 1, offset))
697 if (any(i == labels%line))
then
698 do j = 1,
size(labels)
699 if (labels(j)%line /= i) cycle
701 & repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue') // &
702 & render_label(labels(j))
706 res = res // nl // repeat(
' ', offset + 1) // colorize(
'|', foreground=
'blue')
709 pure function render_label(label)
result(res)
711 character(:),
allocatable :: res
714 character(1) :: marker
715 character(:),
allocatable :: this_color, fg
717 marker = merge(
'^',
'-', label%primary)
718 width = label%finish - label%first
721 if (
allocated(label%level))
then
722 select case (label%level)
732 res = repeat(
' ', label%first) // colorize(repeat(marker, width), foreground=fg)
733 if (
allocated(label%text))
then
734 res = res //
' ' // colorize(label%text, foreground=fg)
737 res = repeat(
' ', label%first) // repeat(marker, width)
738 if (
allocated(label%text))
then
739 res = res //
' ' // colorize(label%text, foreground=
'blue')
744 pure function render_line(input, line)
result(res)
745 character(*),
intent(in) :: input
746 character(*),
intent(in) :: line
747 character(:),
allocatable :: res
749 res = line //
' ' // colorize(
'|', foreground=
'blue') //
' ' // input
752 pure integer function integer_width(input)
result(res)
753 integer,
value :: input
761 do while (input /= 0)
769 pure function to_string(val, width)
result(res)
770 integer,
intent(in) :: val
771 integer,
intent(in),
optional :: width
772 character(:),
allocatable :: res
774 integer,
parameter :: buffer_len = range(val) + 2
775 character(buffer_len) :: buffer
777 character(1),
parameter :: numbers(0:9) = [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9']
790 buffer(pos:pos) = numbers(mod(n, 10))
795 buffer(pos:pos) =
'-'
798 if (
present(width))
then
799 res = repeat(
' ', max(width - (buffer_len + 1 - pos), 0)) // buffer(pos:)
812 subroutine printf(str, fmt, unit)
813 character(*),
intent(in) :: str
814 character(*),
intent(in),
optional :: fmt
815 integer,
intent(in),
optional :: unit
818 if (
present(fmt))
then
819 if (
present(unit))
then
825 if (
present(unit))
then
826 write(unit,
'(A)') str
logical, public nocolor
Switch for controling the ANSI color output Default value is .true. (color mode on)....
logical, public verbose
Master switch for verbose diagnostic output Default value is .false. (quiet mode)....
Generic renderer for diagnostics and source excerpts.
Structured compiler diagnostic.
Diagnostic label identifying a region of source text.