parse_namelist Subroutine

public subroutine parse_namelist(filename, p_namelist, status_ok)

Parse a Fortran namelist into a json_value structure.

The file may contains multiple namelists, and multiple instances of the same namelist (this case is handled by creating an array).

The structure of the JSON file will be:

   {
       "namelist1":{ "var1":val1, "var2":val2, ... },
       "namelist2":{ "var1":val1, "var2":val2, ... },
       "namelist_array":[{ "var1":val1, "var2":val2, ... },
                         { "var1":val1, "var2":val2, ... }]
   }

Note

Currently it only works for a subset of valid namelists, specifically, where each variable/value assignment is on a separate line with the form 'var = val'. It also works for derived types and arrays, but each element of an array must be on a separate line.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: filename

the namelist file to parse

type(json_value), pointer :: p_namelist

the resulting JSON structure

logical, intent(out) :: status_ok

true if there were no errors


Calls

proc~~parse_namelist~~CallsGraph proc~parse_namelist namelist_parser_module::parse_namelist add add proc~parse_namelist->add add_by_path add_by_path proc~parse_namelist->add_by_path create_array create_array proc~parse_namelist->create_array create_object create_object proc~parse_namelist->create_object create_string create_string proc~parse_namelist->create_string get get proc~parse_namelist->get info info proc~parse_namelist->info initialize initialize proc~parse_namelist->initialize proc~add_variable namelist_parser_module::add_variable proc~parse_namelist->proc~add_variable proc~lowercase_string namelist_parser_module::lowercase_string proc~parse_namelist->proc~lowercase_string proc~read_line_from_file namelist_parser_module::read_line_from_file proc~parse_namelist->proc~read_line_from_file replace replace proc~parse_namelist->replace proc~add_variable->add_by_path proc~add_variable->create_string create_double create_double proc~add_variable->create_double create_integer create_integer proc~add_variable->create_integer create_logical create_logical proc~add_variable->create_logical proc~to_integer namelist_parser_module::to_integer proc~add_variable->proc~to_integer proc~to_logical namelist_parser_module::to_logical proc~add_variable->proc~to_logical proc~to_real namelist_parser_module::to_real proc~add_variable->proc~to_real proc~to_logical->proc~lowercase_string

Source Code

    subroutine parse_namelist(filename,p_namelist,status_ok)

    implicit none

    character(len=*),intent(in) :: filename  !! the namelist file to parse
    type(json_value),pointer :: p_namelist   !! the resulting JSON structure
    logical,intent(out) :: status_ok         !! true if there were no errors

    type(json_value),pointer :: p
    type(json_value),pointer :: p_array
    character(len=:),allocatable :: line
    character(len=:),allocatable :: namelist_name
    character(len=:),allocatable :: path
    character(len=:),allocatable :: val
    type(json_core) :: json     !! for manipulation the JSON pointers
    integer :: iunit            !! file unit number
    integer :: istat            !! file `iostat` flag
    integer :: status           !! status code during parsing
    integer :: i                !! index of `=` in a line
    integer :: iline            !! line number counter
    integer :: var_type         !! type of a JSON variable
    integer :: n_children       !! number of elements in an array of namelists
    character(len=256) :: istr  !! for integer to string conversion
    logical :: found            !! to check if namelist with same name already processed
    integer :: len_val          !! length of value string
    logical :: eof              !! end of file flag when reading namelist file

    ! namelist characters:
    character(len=1),parameter :: nml_start_char = '&'
    character(len=1),parameter :: nml_end_char   = '/'
    character(len=1),parameter :: comment_char   = '!'
    character(len=1),parameter :: equals_char    = '='
    character(len=1),parameter :: sep            = '%'
    character(len=1),parameter :: comma          = ','

    ! use fortran style path separators:
    call json%initialize(path_separator='%')

    ! create root object:
    call json%create_object(p_namelist,'')

    ! open the namelist file:
    open(newunit=iunit,file=trim(filename),status='OLD',iostat=istat)
    if (istat==0) then

        status = 0 ! looking for namelist
        iline = 0  ! line counter
        eof = .false.
        do

            if (eof) exit ! finished

            ! read a line from the file
            iline = iline + 1
            call read_line_from_file(iunit,line,eof)
            line = trim(adjustl(line))
            if (line(1:1)==comment_char .or. line == '') cycle ! skip it

            select case (status)

            case(0) ! looking for a namelist

                if (line(1:1)==nml_start_char) then
                    ! a new namelist has been found
                    status = 1 ! parsing a namelist
                    namelist_name = lowercase_string(line(2:))
                    ! does it exist already?
                    nullify(p)
                    call json%get(p_namelist,namelist_name,p,found)
                    if (found) then
                        ! we need to turn it into an array if
                        ! it isn't already one
                        call json%info(p,var_type=var_type,n_children=n_children)
                        if (var_type == json_array) then
                            ! add the array index to the name:
                            write(istr,'(I256)') n_children + 1
                            namelist_name = namelist_name//'('//trim(adjustl(istr))//')'
                        elseif (var_type == json_object) then
                            ! replace this with an array where the
                            ! current object is the first element
                            call json%create_array(p_array,namelist_name)
                            call json%replace(p,p_array,destroy=.false.)
                            call json%add(p_array,p)
                            namelist_name = namelist_name//'(2)'
                        else
                            call line_parse_error(iline,line,'invalid type')
                        end if
                    end if

                end if

            case(1) ! in the process of parsing a namelist

                if (line(1:1)==nml_end_char) then
                    ! finished with this namelist
                    status = 0 ! looking for namelist
                else
                    ! continue parsing this namelist

                    ! we are assuming that:
                    !  * each line contains an equal sign: `var = val`
                    !  * it maybe ends in a comma: `var = val,`
                    !  * val can be enclosed in quotes: `var = "val",`
                    i = index(line,equals_char)

                    if (i<=1) then
                        call line_parse_error(iline,line,'invalid line')
                    else

                        ! full path, including the namelist name
                        ! [also convert to lower case]
                        path = lowercase_string(namelist_name//sep//line(1:i-1))

                        ! the value after the equal sign:
                        val = trim(adjustl(line(i+1:)))

                        ! remove the comma after the value if necessary:
                        len_val = len(val)
                        if (val(len_val:len_val)==comma) then
                            val = val(1:len_val-1)
                        end if

                        ! remove quotes around strings if necessary:
                        ! [either type of quotes is allowed]
                        len_val = len(val)
                        if ((val(1:1)=='"' .and. val(len_val:len_val)=='"') .or. &
                            (val(1:1)=="'" .and. val(len_val:len_val)=="'")) then

                            if (len_val>2) then
                                val = val(2:len_val-1)
                            else
                                val = ''
                            end if
                            ! since we know it is a string:
                            call json%create_string(p,val,'')
                            call json%add_by_path(p_namelist,path,p)

                        else
                            ! infer the variable type and
                            ! add it to the JSON structure:
                            call add_variable(json,p_namelist,path,val)
                        end if

                    end if

                end if

            case default
                call line_parse_error(iline,line,'invalid status flag')
            end select

        end do

        status_ok = .true.
        close(unit=iunit,iostat=istat)  ! close the namelist file

    else
        status_ok = .false.
    end if

    contains

        subroutine line_parse_error(iline,line,error_msg)

        !!  throw an error and stop the program.

        implicit none

        integer,intent(in)          :: iline
        character(len=*),intent(in) :: line
        character(len=*),intent(in) :: error_msg

        write(*,*) ''
        write(*,*) error_msg
        write(*,*) ''
        write(*,*) '  line number: ',iline
        write(*,*) '  line:        '//line
        write(*,*) ''
        error stop 'fatal error'

        end subroutine line_parse_error

    end subroutine parse_namelist