2
$\begingroup$

I have been trying to parse command line arguments. There are a couple of types of options.

1. Mandatory
2. Optional
3. Alternative

There are built-in/3rd party libraries for this purpose in many other languages.

I haven't found any built-in functions for this purpose in Wolfram Lang. Is there any good way to do it in Wolfram Lang?

enter image description here

Update 2017-05-27

There is a example of parsing command line arguments in Haskell.

Example

universalbuild (-p|--project name.xcodeproj) (-s|--scheme schemename) (-c|--configuration configurationname)

Implementation in Haskell

projectOption = strOption (long "project" <> short 'p' <> metavar "name.xcodeproj" <> help "Build the project name.xcodeproj." )
schemeOption = strOption (long "scheme" <> short 's' <> metavar "schemename"  <> help "Build the scheme specified by schemename." )
configuraitonOption = strOption (long "configuration" <> short 'c' <> metavar "configurationname" <> help "Use the build configuration specified by configurationname when building each target.")

options :: Parser UBOptions
options = UBOptions <$> projectOption <*> schemeOption <*> configuraitonOption

I think the implementation in Haskell is concise and expressive. I don't expect it can be done like this in Mathematica, at least I think it illustrates how DSL can simplify tasks.

$\endgroup$
  • $\begingroup$ It is not clear what are you asking: parsing arguments given to wolframscript, or parsing of a particular DSL in WL. $\endgroup$ – Anton Antonov May 27 '17 at 16:16
  • $\begingroup$ @AntonAntonov parsing arguments given to wolframscript. I didn't realise that arguments are DSL. I think they are DSL indeed. $\endgroup$ – UnchartedWorks May 27 '17 at 20:10
  • $\begingroup$ I would like to mention that my answer fits your Haskell implementation -- the parsers are generated from EBNF for convenience. You can see this answer that uses both direct implementation with functional parsers and parsers generation from EBNF for Clojure expressions. $\endgroup$ – Anton Antonov May 27 '17 at 21:26
  • $\begingroup$ @AntonAntonov Thanks, I will try to understand how it works. :) $\endgroup$ – UnchartedWorks May 27 '17 at 21:39
  • $\begingroup$ I updated my answer with implementation corresponding to the Haskell code you posted. I hope that helps. $\endgroup$ – Anton Antonov May 27 '17 at 22:07
3
$\begingroup$

The DSL perspective

As it is discussed in the comments, the question can be seen as parsing of a particular DSL in WL.

Direct implementation

This notebook screenshot image shows a direct implementation corresponding to the Haskell example in the question with the package "FunctionalParsers.m":

enter image description here

Details on how to use the package "FunctionalParsers.m" can be found here: "Functional parsers for an integration requests language grammar.pdf".

Some notes on the operators used:

  • Shortcut operators:

    • "⊕" for ParseAlternativeComposition,
    • "⊗" for ParseSequentialComposition,
    • "⊙" for ParseApply.
  • ParseOption is for optional parsing.

  • I put function wrappers around the parsed results with ParseApply.

Here is the code:

pOptionValue = 
  ParsePredicate[
   StringMatchQ[#, 
     Except[{"-", "(", ")", "[", "]", WhitespaceCharacter}] ..] &];
pProgramName = Program\[CircleDot]pOptionValue;
pProjectOption = 
  Project\[CircleDot]((ParseSymbol["-p"]\[CirclePlus]ParseSymbol[
        "--project"])\[CircleTimes]pOptionValue);
pSchemeOption = 
  Scheme\[CircleDot]((ParseSymbol["-s"]\[CirclePlus]ParseSymbol[
        "--scheme"])\[CircleTimes]pOptionValue);
pConfigurationOption = 
  Configuration\[CircleDot]((ParseSymbol[
        "-c"]\[CirclePlus]ParseSymbol[
        "--configuration"])\[CircleTimes]pOptionValue);
pHelp = Help\[CircleDot](ParseSymbol["-h"]\[CirclePlus]ParseSymbol[
      "--help"]);
pOptions = 
  ParseMany[
   pProjectOption\[CirclePlus]pSchemeOption\[CirclePlus]\
pConfigurationOption\[CirclePlus]pHelp];
pCommand = 
  ParseShortest[
   Flatten\[CircleDot](pProgramName\[CircleTimes]ParseOption[pOptions])];

Answer using parser generation from EBNF

I am posting this part of the answer since the following command line DSL was easy to program with "FunctionalParsers.m" (it took me 15-20 minutes) using EBNF specification and parsers generation.

First we program the command line DSL in Extended Backus-Naur Form (EBNF):

Import["https://raw.githubusercontent.com/antononcube/MathematicaForPrediction/master/FunctionalParsers.m"]

ebnfCommandLine = "
  <command-line> = <program> , [ <arg-list> ] ;
  <program> = '_WordString' <@ CLProgram ;
  <arg-list> = { <arg> } <@ CLArgumentList ;
  <arg> = <timeout-spec> | <source-spec> | <help> | <option-name> ,  <option-value> <@ CLArgument ;
  <option-name> = '_String' <@ CLOptionName ;
  <option-value> = '_String' <@ CLOptionValue ;
  <help> = '--help' | '-h' <@ CLHelp ;
  <timeout-spec> = ( '--timeout' | '-t' ) &> '_?NumberQ' <@ CLTimeoutSpec ;
  <source-spec> = ( '--source' | '-src' ) &> '_String' <@ CLSourceSpec ;
  ";

Note that in the EBNF string above I have used function wrappers for the parsing rules. Some of them can be skipped (e.g. CLArgumentList).

The following generates the parsers from the EBNF string:

GenerateParsersFromEBNF[ToTokens[ebnfCommandLine]];
LeafCount[res]
(* 258 *)

Here we overwrite the parser for <option-name> in order to make it more specific. (Hard to do in the EBNF string.)

pOPTIONNAME = 
  ParseApply[CLOptionName, 
   ParsePredicate[
    StringMatchQ[#, ("-" | "--") ~~ (WordCharacter ..)] &]];

Here is a table of example command parsings:

commands = {"example",
   "example --help",
   "example1 --timeout 123",
   "example2 -t 34 -src ./MyFile.php",
   "exampleUknownOption --timeout 34 --source ./MyFile.php -out ./MyFile.PHP",
   "exampleFail1 --timeout 34 --source ./MyFile.php -;out ./MyFile.PHP",
   "exampleFail2 5 6 out xxx",
   "exampleMisSpell --timeout 34 --sourse ./MyFile.php"
   };
ParsingTestTable[ParseShortest[pCOMMANDLINE], commands, "Layout" -> "Vertical"]

enter image description here

For more details on programming DSLs see the blog post: "Creating and programming domain specific languages".

$\endgroup$
4
$\begingroup$

Command line arguments are stored in $CommandLine. Mine looks like this:

$CommandLine
(* {"/Applications/Mathematica 11.app/Contents/MacOS/WolframKernel", 
    "-wstp", "-mathlink", 
    "-linkprotocol", "SharedMemory", "-linkconnect", "-linkname", "cddhm_shm"} *)

You can parse these relatively easily with the usual functions such as MemberQ, Position, Replace, etc. For example,

Replace[$CommandLine, {___, "-linkname", linkname_, ___} :> linkname]
(* "cddhm_shm" *)

Several of the standard arguments, such as -noinit or -nopaclet are in fact handled in Wolfram Language code. You can find references to these if you browse the $InstallationDirectory.

Many tools these days use - for single letter flags and -- for multi-letter ones. I do not think it is a good idea to use this convention with Mathematica because its multi-letter built-in options all use a single -. It would not be possible to distinguish them from single-letter flags.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.