Routine to Parse Options in Args Array

(Originally posted by Timur Safin  on Intersystems CODE, 3/2/15) This code snippet is a routine that parses options saved in an arguments array. The subroutine "test" runs the code:


ROUTINE timur.getOptionsArgs
/// Let assume we want to parse options saved in args array in the form
///   args=5
///   args(1)="/o"
///   args(2)="all"
///   args(3)="/verbose"
///   args(4)="the"
///   args(5)="rest"
/// we want to get handled boolean options /o and /v (or -o and -v in unix-style)
///    set option = 0, verbose = 0
/// we call GetOptions function this way:
///    GetOptions(.args,"-o",.option,"/verbose|/v",.verbose,"/include:",.include)
/// NB! This example is handling only boolean options
#define OptionBoolean 1
#define OptionString 2
/// f.k.a. "getOptions"
test(&args,&fmtDest...) public {
    #dim known ; known options
 
    // 1. scan options
    for i=1:2:$get(fmtDest) {
        #dim opts As %String = $get(fmtDest(i))
        continue:opts=""
        for j=1:1:$length(opts,"|") {
            #dim opt As %String = $zstrip($piece(opts,"|",j), "<>", "-/")
            #dim lastC As %String = $e(opt,*)
            #dim optType As %Integer = $case(lastC, ":":$$$OptionString, :$$$OptionBoolean)
            set opt = $zstrip(opt,"<>",":")
             
            set known(opt) = $lb(optType, i + 1) ; remember option type and destination argument pointer
        }
    }
     
    #dim argsN = "" ; new args without processed known options
    // 2. process passed args while creating new modified args (without known /options)
    for i=1:1:$get(args) {
        #dim arg As %String = $get(args(i))
        #dim isOption As %Boolean = $case($extract(arg,1),"/":1, "-":1, :0)
 
        if isOption {
            #dim optInfo = $get(known($zstrip(arg, "<>", "-/:")))
 
            if $length(optInfo)>0 {
                #dim type As %Integer = $li(optInfo,1)
                #dim index As %Integer = $li(optInfo,2)
                if type=$$$OptionBoolean {
                    set fmtDest(index) = 1
                } else {
                    set fmtDest(index) = $get(args($i(i)))
                }
            } else {
                return i ; unknown /option - bails out
            }
        } else {
            set argsN($i(argsN)) = arg
        }
    }
    // 3. save not consumed arguments back to args array
    if $get(argsN)<$get(args) {
        kill args merge args = argsN
    }
    quit 0
}

Here's a link to the code on GitHub

Comments

This is nice. The only thing I'm wondering about is why there are #dim statements for regular variables? @Timur Safin, why did you include the #dim statements? In my opinion, #dim statements for variables that are not object references are misleading:

  1. #dim gives developers that are new to ObjectScript the impression that you must declare all variables like in some other languages. Declaring variables like this is not necessary in ObjectScript.
  2. #dim's main purpose is to help Atelier/Studio provide code completion for variables that are object references. If you write this code #dim i as %Integer, and then on the next line you type do i. ,  after the period Atelier/Studio will suggest methods from the %Integer class. If you happen to accept one of the suggestions, the resulting code will not compile. This is because our datatype classes (like %String and %Integer) are not object classes.

Timur would unlikely be notified on your comment as he has two profiles here: .../user/timur-safin-0 (with more recent activity) and .../user/timur-safin, an older one, where the site points to when the user '@ Timur . Safin' is mentioned.

Besides, it seems that Timur was moving farther and farther away from Caché during last 2 years...

Hear, hear :)

P.S.

There are always good friends which make me know

Yes, Track Variables in Studio is great! But I don't see any value in Option Explicit, which forces #dim, which leads to the issues in my original comment.

#dim are easier to notice, as they are different from the usual set.

The shorter, not very much serious answer - #dim is recommended by COS Guidelines we used in our local community - COS Guidelines

[But well, whom am I tricking about - these rules written by myself, and it was my personal push to use this practice here]

Here is the longer question: I hate dynamic typing systems, they tend to break sooner, and they frequently complicate understanding for your team mates. #dim constructs was a small set forward in making COS less dynamic typed, and more static typed language. Originally it was created as a hint for Studio auto-complete, but we always understood that it could be reused for some sort of static analyser (if it would ever be created) which will help find some COS errors rather sooner, at the compile time (or around of compilation time). But these has never materialized  (at least it has not happened till last time I checked a couple of years ago). 

So, if we could not check something automatically, via analysers, we could enforce it manually via policy written to guidelines, and obligatory peer-review before commits...

Hi Timur! Your short answer is funny, and your longer answer makes sense (even though my preferences are different).