-- | This module defines a generator for @getopt@ based command
-- line argument parsing.  Each option is associated with arbitrary
-- Python code that will perform side effects, usually by setting some
-- global variables.
module Futhark.CodeGen.Backends.GenericPython.Options
       ( Option (..)
       , OptionArgument (..)
       , generateOptionParser
       )
       where

import Futhark.CodeGen.Backends.GenericPython.AST

-- | Specification if a single command line option.  The option must
-- have a long name, and may also have a short name.
--
-- When the statement is being executed, the argument (if any) will be
-- stored in the variable @optarg@.
data Option = Option { Option -> String
optionLongName :: String
                     , Option -> Maybe Char
optionShortName :: Maybe Char
                     , Option -> OptionArgument
optionArgument :: OptionArgument
                     , Option -> [PyStmt]
optionAction :: [PyStmt]
                     }

-- | Whether an option accepts an argument.
data OptionArgument = NoArgument
                    | RequiredArgument String
                    | OptionalArgument

-- | Generate option parsing code that accepts the given command line options.  Will read from @sys.argv@.
--
-- If option parsing fails for any reason, the entire process will
-- terminate with error code 1.
generateOptionParser :: [Option] -> [PyStmt]
generateOptionParser :: [Option] -> [PyStmt]
generateOptionParser [Option]
options =
  [PyExp -> PyExp -> PyStmt
Assign (String -> PyExp
Var String
"parser")
   (PyExp -> [PyArg] -> PyExp
Call (String -> PyExp
Var String
"argparse.ArgumentParser")
    [String -> PyExp -> PyArg
ArgKeyword String
"description" (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$
     String -> PyExp
String String
"A compiled Futhark program."])] [PyStmt] -> [PyStmt] -> [PyStmt]
forall a. [a] -> [a] -> [a]
++
  (Option -> PyStmt) -> [Option] -> [PyStmt]
forall a b. (a -> b) -> [a] -> [b]
map Option -> PyStmt
parseOption [Option]
options [PyStmt] -> [PyStmt] -> [PyStmt]
forall a. [a] -> [a] -> [a]
++
  [PyExp -> PyExp -> PyStmt
Assign (String -> PyExp
Var String
"parser_result") (PyExp -> PyStmt) -> PyExp -> PyStmt
forall a b. (a -> b) -> a -> b
$
   PyExp -> [PyArg] -> PyExp
Call (String -> PyExp
Var String
"vars") [PyExp -> PyArg
Arg (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ PyExp -> [PyArg] -> PyExp
Call (String -> PyExp
Var String
"parser.parse_args") [PyExp -> PyArg
Arg (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ String -> PyExp
Var String
"sys.argv[1:]"]]] [PyStmt] -> [PyStmt] -> [PyStmt]
forall a. [a] -> [a] -> [a]
++
  (Option -> PyStmt) -> [Option] -> [PyStmt]
forall a b. (a -> b) -> [a] -> [b]
map Option -> PyStmt
executeOption [Option]
options
  where parseOption :: Option -> PyStmt
parseOption Option
option =
          PyExp -> PyStmt
Exp (PyExp -> PyStmt) -> PyExp -> PyStmt
forall a b. (a -> b) -> a -> b
$ PyExp -> [PyArg] -> PyExp
Call (String -> PyExp
Var String
"parser.add_argument") ([PyArg] -> PyExp) -> [PyArg] -> PyExp
forall a b. (a -> b) -> a -> b
$
          (String -> PyArg) -> [String] -> [PyArg]
forall a b. (a -> b) -> [a] -> [b]
map (PyExp -> PyArg
Arg (PyExp -> PyArg) -> (String -> PyExp) -> String -> PyArg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> PyExp
String) [String]
name_args [PyArg] -> [PyArg] -> [PyArg]
forall a. [a] -> [a] -> [a]
++
          [PyArg]
argument_args
          where name_args :: [String]
name_args = ([String] -> [String])
-> (Char -> [String] -> [String])
-> Maybe Char
-> [String]
-> [String]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [String] -> [String]
forall a. a -> a
id ((:) (String -> [String] -> [String])
-> (Char -> String) -> Char -> [String] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char
'-'Char -> String -> String
forall a. a -> [a] -> [a]
:) (String -> String) -> (Char -> String) -> Char -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> String -> String
forall a. a -> [a] -> [a]
:[])) (Option -> Maybe Char
optionShortName Option
option)
                            [String
"--" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Option -> String
optionLongName Option
option]
                argument_args :: [PyArg]
argument_args = case Option -> OptionArgument
optionArgument Option
option of
                  RequiredArgument String
t ->
                    [String -> PyExp -> PyArg
ArgKeyword String
"action" (String -> PyExp
String String
"append"),
                     String -> PyExp -> PyArg
ArgKeyword String
"default" (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ [PyExp] -> PyExp
List [],
                     String -> PyExp -> PyArg
ArgKeyword String
"type" (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ String -> PyExp
Var String
t]

                  OptionArgument
NoArgument ->
                    [String -> PyExp -> PyArg
ArgKeyword String
"action" (String -> PyExp
String String
"append_const"),
                     String -> PyExp -> PyArg
ArgKeyword String
"default" (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ [PyExp] -> PyExp
List [],
                     String -> PyExp -> PyArg
ArgKeyword String
"const" PyExp
None]

                  OptionArgument
OptionalArgument ->
                    [String -> PyExp -> PyArg
ArgKeyword String
"action" (String -> PyExp
String String
"append"),
                     String -> PyExp -> PyArg
ArgKeyword String
"default" (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ [PyExp] -> PyExp
List [],
                     String -> PyExp -> PyArg
ArgKeyword String
"nargs" (PyExp -> PyArg) -> PyExp -> PyArg
forall a b. (a -> b) -> a -> b
$ String -> PyExp
String String
"?"]

        executeOption :: Option -> PyStmt
executeOption Option
option =
          String -> PyExp -> [PyStmt] -> PyStmt
For String
"optarg" (PyExp -> PyIdx -> PyExp
Index (String -> PyExp
Var String
"parser_result") (PyIdx -> PyExp) -> PyIdx -> PyExp
forall a b. (a -> b) -> a -> b
$
                        PyExp -> PyIdx
IdxExp (PyExp -> PyIdx) -> PyExp -> PyIdx
forall a b. (a -> b) -> a -> b
$ String -> PyExp
String (String -> PyExp) -> String -> PyExp
forall a b. (a -> b) -> a -> b
$ Option -> String
fieldName Option
option) ([PyStmt] -> PyStmt) -> [PyStmt] -> PyStmt
forall a b. (a -> b) -> a -> b
$
            Option -> [PyStmt]
optionAction Option
option

        fieldName :: Option -> String
fieldName = (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
escape (String -> String) -> (Option -> String) -> Option -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Option -> String
optionLongName
          where escape :: Char -> Char
escape Char
'-' = Char
'_'
                escape Char
c = Char
c