2

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.

webcpu
  • 3,182
  • 12
  • 17

2 Answers2

4

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.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
4

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".

Anton Antonov
  • 37,787
  • 3
  • 100
  • 178