| 1 | module jump_stack_module |
| 2 | use iso_fortran_env, only: int32 |
| 3 | implicit none |
| 4 | private |
| 5 | |
| 6 | public :: jump_stack_t |
| 7 | public :: init_jump_stack, cleanup_jump_stack |
| 8 | public :: push_jump_location, pop_jump_location |
| 9 | public :: peek_jump_location, is_jump_stack_empty |
| 10 | |
| 11 | ! Maximum number of jump locations to track |
| 12 | integer, parameter :: MAX_JUMP_STACK = 100 |
| 13 | |
| 14 | ! Jump location entry |
| 15 | type :: jump_location_t |
| 16 | character(len=:), allocatable :: filename |
| 17 | integer(int32) :: line |
| 18 | integer(int32) :: column |
| 19 | end type jump_location_t |
| 20 | |
| 21 | ! Jump stack for navigation history |
| 22 | type :: jump_stack_t |
| 23 | type(jump_location_t), allocatable :: locations(:) |
| 24 | integer :: top = 0 |
| 25 | integer :: capacity = MAX_JUMP_STACK |
| 26 | end type jump_stack_t |
| 27 | |
| 28 | contains |
| 29 | |
| 30 | subroutine init_jump_stack(stack) |
| 31 | type(jump_stack_t), intent(out) :: stack |
| 32 | |
| 33 | allocate(stack%locations(MAX_JUMP_STACK)) |
| 34 | stack%top = 0 |
| 35 | stack%capacity = MAX_JUMP_STACK |
| 36 | end subroutine init_jump_stack |
| 37 | |
| 38 | subroutine cleanup_jump_stack(stack) |
| 39 | type(jump_stack_t), intent(inout) :: stack |
| 40 | integer :: i |
| 41 | |
| 42 | if (allocated(stack%locations)) then |
| 43 | ! Deallocate all filename strings |
| 44 | do i = 1, stack%top |
| 45 | if (allocated(stack%locations(i)%filename)) then |
| 46 | deallocate(stack%locations(i)%filename) |
| 47 | end if |
| 48 | end do |
| 49 | deallocate(stack%locations) |
| 50 | end if |
| 51 | stack%top = 0 |
| 52 | end subroutine cleanup_jump_stack |
| 53 | |
| 54 | subroutine push_jump_location(stack, filename, line, column) |
| 55 | type(jump_stack_t), intent(inout) :: stack |
| 56 | character(len=*), intent(in) :: filename |
| 57 | integer(int32), intent(in) :: line, column |
| 58 | integer :: i |
| 59 | |
| 60 | ! Don't push if same as current top location |
| 61 | if (stack%top > 0) then |
| 62 | if (allocated(stack%locations(stack%top)%filename)) then |
| 63 | if (stack%locations(stack%top)%filename == filename .and. & |
| 64 | stack%locations(stack%top)%line == line) then |
| 65 | return ! Don't duplicate the same location |
| 66 | end if |
| 67 | end if |
| 68 | end if |
| 69 | |
| 70 | ! If stack is full, shift everything down |
| 71 | if (stack%top >= stack%capacity) then |
| 72 | ! Deallocate the first location's filename |
| 73 | if (allocated(stack%locations(1)%filename)) then |
| 74 | deallocate(stack%locations(1)%filename) |
| 75 | end if |
| 76 | |
| 77 | ! Shift all locations down by one |
| 78 | do i = 1, stack%capacity - 1 |
| 79 | stack%locations(i) = stack%locations(i + 1) |
| 80 | end do |
| 81 | stack%top = stack%capacity - 1 |
| 82 | end if |
| 83 | |
| 84 | ! Add new location |
| 85 | stack%top = stack%top + 1 |
| 86 | if (allocated(stack%locations(stack%top)%filename)) then |
| 87 | deallocate(stack%locations(stack%top)%filename) |
| 88 | end if |
| 89 | allocate(character(len=len_trim(filename)) :: stack%locations(stack%top)%filename) |
| 90 | stack%locations(stack%top)%filename = trim(filename) |
| 91 | stack%locations(stack%top)%line = line |
| 92 | stack%locations(stack%top)%column = column |
| 93 | end subroutine push_jump_location |
| 94 | |
| 95 | function pop_jump_location(stack, filename, line, column) result(success) |
| 96 | type(jump_stack_t), intent(inout) :: stack |
| 97 | character(len=:), allocatable, intent(out) :: filename |
| 98 | integer(int32), intent(out) :: line, column |
| 99 | logical :: success |
| 100 | |
| 101 | success = .false. |
| 102 | if (stack%top <= 0) return |
| 103 | |
| 104 | ! Get the top location |
| 105 | if (allocated(stack%locations(stack%top)%filename)) then |
| 106 | allocate(character(len=len(stack%locations(stack%top)%filename)) :: filename) |
| 107 | filename = stack%locations(stack%top)%filename |
| 108 | line = stack%locations(stack%top)%line |
| 109 | column = stack%locations(stack%top)%column |
| 110 | |
| 111 | ! Remove from stack |
| 112 | deallocate(stack%locations(stack%top)%filename) |
| 113 | stack%top = stack%top - 1 |
| 114 | success = .true. |
| 115 | end if |
| 116 | end function pop_jump_location |
| 117 | |
| 118 | function peek_jump_location(stack, filename, line, column) result(success) |
| 119 | type(jump_stack_t), intent(in) :: stack |
| 120 | character(len=:), allocatable, intent(out) :: filename |
| 121 | integer(int32), intent(out) :: line, column |
| 122 | logical :: success |
| 123 | |
| 124 | success = .false. |
| 125 | if (stack%top <= 0) return |
| 126 | |
| 127 | ! Get the top location without removing it |
| 128 | if (allocated(stack%locations(stack%top)%filename)) then |
| 129 | allocate(character(len=len(stack%locations(stack%top)%filename)) :: filename) |
| 130 | filename = stack%locations(stack%top)%filename |
| 131 | line = stack%locations(stack%top)%line |
| 132 | column = stack%locations(stack%top)%column |
| 133 | success = .true. |
| 134 | end if |
| 135 | end function peek_jump_location |
| 136 | |
| 137 | function is_jump_stack_empty(stack) result(is_empty) |
| 138 | type(jump_stack_t), intent(in) :: stack |
| 139 | logical :: is_empty |
| 140 | |
| 141 | is_empty = (stack%top <= 0) |
| 142 | end function is_jump_stack_empty |
| 143 | |
| 144 | end module jump_stack_module |