subroutine parse_from_lines(this, lines, n_lines, label, errors, n_err)
!! Walk an in-memory namelist (already split into `lines`): strip
!! comments, scan for `&group` headers, dispatch keys. `label`
!! names the source in error messages — a file path for parse_file,
!! a sentinel (e.g. "<config-string>") for buffer parses.
type(nml_schema_t), intent(inout) :: this
character(len=*), intent(in) :: lines(:)
integer, intent(in) :: n_lines
character(len=*), intent(in) :: label
character(len=:), allocatable, intent(inout) :: errors(:)
integer, intent(inout) :: n_err
integer :: li, col, gline
character(len=:), allocatable :: gname, prefix
logical, allocatable :: group_seen(:)
if (allocated(this%groups)) then
allocate (group_seen(size(this%groups)))
group_seen = .false.
else
allocate (group_seen(0))
end if
li = 1
col = 1
do
! Scan for the next '&' that opens a group.
call next_amp(lines, n_lines, li, col)
if (li > n_lines) exit
gline = li
call read_group_name(lines, n_lines, li, col, gname)
prefix = make_prefix(label, gline)
if (len_trim(gname) == 0) then
call add_err(errors, n_err, prefix//"malformed group header (expected name after '&')")
! Skip to next '/' to recover.
call skip_to_slash(lines, n_lines, li, col)
cycle
end if
call dispatch_group(this, lines, n_lines, li, col, gname, prefix, &
group_seen, errors, n_err, label)
end do
end subroutine parse_from_lines