Skip to main content
added 226 characters in body
Source Link

Btw, as a MOLCAS contributor (if I understand correctly from your Chemistry stackExchange posts) you may know Jeppe Olsen, who used to be a MOLCAS contributor too. We are building a molecular photoionization code together, now.

Btw, as a MOLCAS contributor (if I understand correctly from your Chemistry stackExchange posts) you may know Jeppe Olsen, who used to be a MOLCAS contributor too. We are building a molecular photoionization code together, now.

Source Link

Thank you for the code alternative. I like many of the changes you did: The non-quadratic tokenizer, of course, as well as the use of a stack of the minimum size needed, and the is_number check.

I am less fond of the allocation of a temporary array to the possible maximum number of tokens, though, even if the data in the unused elements is not allocated. As I see it, the function you wrote splits naturally in a token counter and in a token reader. Once the number of tokens is known, it is easy to fetch them from the input expression with the index function. Therefore, I would rather replace the tokenize function with something like the following

    !> @brief
    !> Count tokens in string.
    pure integer function countTokens(expr,delimiter) result(nTokens)
        character(*), intent(in)    :: expr
        character(*), intent(in)    :: delimiter
        !
        integer                     :: low, high

        low = 1; nTokens = 0
        do while (low <= len(expr))
            do while (expr(low : low) == delimiter)
                low = low + 1
                if (low > len(expr)) exit
            end do
            if (low > len(expr)) exit
            high = low
            if (high < len(expr)) then
                do while (expr(high + 1 : high + 1) /= delimiter)
                    high = high + 1
                    if (high == len(expr)) exit
                end do
            end if
            nTokens = nTokens + 1
            low = high + 2
        end do
    end function countTokens

    !> @brief
    !> Split string by whitespace.
    pure function tokenize(expr) result(res)
        character(*), intent(in)    :: expr
        type(Token_t), allocatable  :: res(:)
        !
        character(len=1), parameter :: delimiter     = ' '
        integer         , parameter :: TOKEN_MAX_LEN = 50
        !
        character(len=TOKEN_MAX_LEN):: sBuf
        integer                     :: iToken, nTokens, low

        nTokens = countTokens(expr,delimiter)
        allocate(res(nTokens))
        low=1
        do iToken = 1, nTokens
            read(expr(low:),*) sBuf
            res(iToken)%str = trim(adjustl(sBuf))
            if(iToken == nTokens)exit
            low = low + index(expr(low:),res(iToken)%str) - 1
            low = low + index(expr(low:)," ")
        enddo

    end function

I am on the fence regarding the select case statement. I originally used this same algorithm, but I did not like (and still do not like) repeating the boilerplate code A = stack%pop(); B = stack%pop() over and over. The only possible advantage I can see is if the compiler implements a binary search across the listed cases (?), which would be best for a large number of operators, of course. If, however, it goes linearly through the cases, then the gain over a list of ifs is just of about a factor of two.

I understand also the appeal of operators with arbitrary arity. I think a possible approach to avoid too much boilerplate would be to separately specify the kind (fixed number, number specified at run time, or to be determined from the stack) and, if applicable, the value of this operator attribute, and on this basis popping as many operands as needed.