Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • best practice parsing sub-option of option

    Hi all,

    I am working on a command that uses syntax to parse user input. One option, rowlabel, allows for sub-options (there are more options but they are omitted here for simplicity). I use syntax to parse the user input like this:

    Code:
    syntax varlist , rowlabel(string)
    where the option rowlabel both has keywords and sub-options, for example, like this:

    Code:
    rowlabel(label , text(divorce divorce rate))
    Instead of the keyword label the user could also enter name. After syntax parses the user input, I have a local called rowlabel that holds the string "label , text(divorce divorce rate)". I would then like to do something similar to using syntax again to parse the content of the local rowlabel. Is there any best practice how to parse that so that I get the following three locals:

    Code:
    local label "label"
    local name ""
    local text "divorce divorce rate"
    I could manually parse that myself using gettoken, but since this should be fairly common I thought that there might be a tool for this. I am considering creating a sub-command that takes the content of local rowlabel as input that parse the string using syntax and return it as locals in the r() results. That might be the best way, but I wanted to ask if there is an even better way out there.

    With the sub-command I would first remove the comma in rowlabel, pass the content of rowlabel as input to the sub-commandand then use syntax like this:

    Code:
    syntax  , [label name text(string)]
    
    return local name "`name'"
    return local label "`label'"
    return local text `"`text'"'
    Any better ideas?

  • #2
    That is a trick I use a lot in my own programs. It tends to help to keep the code clean and easy to maintain. So, go for it.
    ---------------------------------
    Maarten L. Buis
    University of Konstanz
    Department of history and sociology
    box 40
    78457 Konstanz
    Germany
    http://www.maartenbuis.nl
    ---------------------------------

    Comment


    • #3
      Note that if you use syntax again -- which wants to process the contents of local macro 0, so make sure that is populated -- then whatever it created earlier will be blanked out, so copy macros like varlist, if, in to safe places first.

      I often use gettoken for tasks like this.

      Code:
      gettoken label rowlabel: rowlabel, parse(,)
      gettoken comma rowlabel : rowlabel, parse(,)

      Comment


      • #4
        I am considering creating a sub-command
        You should do this. Took me a long time to realize that more sub-commands usually result in better code. When the main command does not return anything in r(), I tend to write code that preserves previous contents in r(). In that case, consider returning the stuff you need from sub-commands in s(). If you do not care, r() is fine, too.

        Best
        Daniel

        Comment


        • #5
          Thanks Maarten!

          Nick, my first intention was to do something similar, but if someone entered rowlabel(label name somethingillegal, text(divorce divorce rate)) then I had to keep parsing the local you call label to be able to test that. And I do not know if it is label that is used, it could be name, so I did not know what to call the local in the first gettoken. Remedies to each of those issues can be implemented easily but I felt the number of corner cases would pile up and make the code heavy. I rather rely on built in tools like syntax. I implemented the sub-command method and I liked it, so I am only asking in order to learn, so only reply if you have time.

          I always put macros like 0, varlist, etc. in a safe place first as I think it makes the code easier to read.

          Thank you both!

          Comment


          • #6
            OK, but (unhelpful but often true) it's bad design if it's too complicated to implement!

            I often allow options within options but typically most possibilities will be dealt with by an inbuilt command. So I peel off my "own" suboptions and then leave the rest to the command in question.

            Comment


            • #7
              Thanks Daniel and Nick!

              I went with rclass as I always store r()-results immediately in locals with clear and unique names as that makes the code easier to maintain in case you add a new segment of code in the middle of the command.

              I love myself when threads like this I find when googling ends with an example that I can put in a do-file and experiment with, so here is a generalization of the method that I went for:


              Code:
                  clear all
                  
                  **This command has an option that has
                  * the sub-options key1, key2, and text().
                  * Sub-option text takes a string as value.
              
                  program define command
              
                      syntax  , option(string)
                      
                      **The comma needs to be removed as all
                      * sub-options are called as options in the
                      * sub-command
                      local option = subinstr(`"`option'"', ",", "", 1)
                      
                      *Call the subcommand and then show the returned values
                      subcommand  , `option'
                      return list
                  end
              
              
                  **This command takes the content of option() in
                  * the main command, use syntax to parse the
                  * content (and report illegal input), and returns
                  * each sub-option in locals
              
                  program define subcommand, rclass
              
                      syntax  , [key1 key2 text(string)]
              
                      return local key1 "`key1'"
                      return local key2 "`key2'"
                      return local text "`text'"
                      
                  end
              
                  
                  **Call the main command. Play around with the sub-options in option() to
                  * test how it works
                  command , option( key1, text("This is a string"))
              Last edited by Kristoffer Bjarkefur; 29 May 2018, 13:16.

              Comment

              Working...
              X