{- -----------------------------------------------------------------------------
Copyright 2020 Kevin P. Barry

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----------------------------------------------------------------------------- -}

-- Author: Kevin P. Barry [ta0kira@gmail.com]

module Cli.ParseCompileOptions (
  optionHelpText,
  parseCompileOptions,
  validateCompileOptions,
) where

import Control.Monad (when)
import Data.List (isSuffixOf)
import Text.Regex.TDFA

import Base.CompilerError
import Cli.CompileOptions
import Types.TypeCategory (FunctionName(..))
import Types.TypeInstance (CategoryName(..))


optionHelpText :: [String]
optionHelpText :: [String]
optionHelpText = [
    String
"",
    String
"zeolite [options...] -m [category(.function)] -o [binary] [path]",
    String
"zeolite [options...] --fast [category(.function)] [.0rx source]",
    String
"zeolite [options...] -c [path]",
    String
"zeolite [options...] -r [paths...]",
    String
"zeolite [options...] -R [paths...]",
    String
"zeolite [options...] -t [paths...]",
    String
"",
    String
"zeolite [options...] --templates [paths...]",
    String
"zeolite --get-path",
    String
"zeolite --show-deps [paths...]",
    String
"zeolite --version",
    String
"",
    String
"Normal Modes:",
    String
"  -c: Only compile the individual files. (default)",
    String
"  -m [category(.function)]: Create a binary that executes the function.",
    String
"  --fast [category(.function)] [.0rx source]: Create a binary without needing a module.",
    String
"  -r: Recompile using the previous compilation options.",
    String
"  -R: Recursively recompile using the previous compilation options.",
    String
"  -t: Only execute tests, without other compilation.",
    String
"",
    String
"Special Modes:",
    String
"  --templates: Only create C++ templates for undefined categories in .0rp sources.",
    String
"  --get-path: Show the data path and immediately exit.",
    String
"  --show-deps: Show category dependencies for the modules.",
    String
"  --version: Show the compiler version and immediately exit.",
    String
"",
    String
"Options:",
    String
"  -f: Force compilation instead of recompiling with -r/-R.",
    String
"  -i [path]: A single source path to include as a *public* dependency.",
    String
"  -I [path]: A single source path to include as a *private* dependency.",
    String
"  -o [binary]: The name of the binary file to create with -m.",
    String
"  -p [path]: Set a path prefix for finding the specified source files.",
    String
"",
    String
"[category]: The name of a concrete category with no params.",
    String
"[function]: The name of a @type function (defaults to \"run\") with no args or params.",
    String
"[path(s...)]: Path(s) containing source or dependency files.",
    String
""
  ]

defaultMainFunc :: String
defaultMainFunc :: String
defaultMainFunc = String
"run"

parseCompileOptions :: CollectErrorsM m => [String] -> m CompileOptions
parseCompileOptions :: [String] -> m CompileOptions
parseCompileOptions = CompileOptions -> [(Int, String)] -> m CompileOptions
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
CompileOptions -> [(a, String)] -> m CompileOptions
parseAll CompileOptions
emptyCompileOptions ([(Int, String)] -> m CompileOptions)
-> ([String] -> [(Int, String)]) -> [String] -> m CompileOptions
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Int] -> [String] -> [(Int, String)]
forall a b. [a] -> [b] -> [(a, b)]
zip ([Int
1..] :: [Int]) where
  parseAll :: CompileOptions -> [(a, String)] -> m CompileOptions
parseAll CompileOptions
co [] = CompileOptions -> m CompileOptions
forall (m :: * -> *) a. Monad m => a -> m a
return CompileOptions
co
  parseAll CompileOptions
co [(a, String)]
os = do
    ([(a, String)]
os',CompileOptions
co') <- CompileOptions
-> [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
CompileOptions
-> [(a, String)] -> m ([(a, String)], CompileOptions)
parseSingle CompileOptions
co [(a, String)]
os
    CompileOptions -> [(a, String)] -> m CompileOptions
parseAll CompileOptions
co' [(a, String)]
os'
  argError :: a -> String -> String -> m a
argError a
n String
o String
m = String -> m a
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM (String -> m a) -> String -> m a
forall a b. (a -> b) -> a -> b
$ String
"Argument " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" (\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
o String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\"): " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
m
  checkPathName :: a -> String -> String -> m ()
checkPathName a
n String
f String
o
    | String
f String -> String -> Bool
forall source source1 target.
(RegexMaker Regex CompOption ExecOption source,
 RegexContext Regex source1 target) =>
source1 -> source -> target
=~ String
"^(/[^/]+|[^-/][^/]*)(/[^/]+)*$" = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    | String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
o    = a -> String -> String -> m ()
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
f String
"Invalid file path."
    | Bool
otherwise = a -> String -> String -> m ()
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
f (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
"Invalid file path for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
o String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"."
  checkCategoryName :: a -> String -> String -> m ()
checkCategoryName a
n String
c String
o
    | String
c String -> String -> Bool
forall source source1 target.
(RegexMaker Regex CompOption ExecOption source,
 RegexContext Regex source1 target) =>
source1 -> source -> target
=~ String
"^[A-Z][A-Za-z0-9]+$" = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    | Bool
otherwise = a -> String -> String -> m ()
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
c (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
"Invalid category name for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
o String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"."
  checkFunctionName :: a -> String -> String -> m ()
checkFunctionName a
n String
d String
o
    | String
d String -> String -> Bool
forall source source1 target.
(RegexMaker Regex CompOption ExecOption source,
 RegexContext Regex source1 target) =>
source1 -> source -> target
=~ String
"^[a-z][A-Za-z0-9]+$" = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    | Bool
otherwise = a -> String -> String -> m ()
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
d (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
"Invalid function name for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
o String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"."

  parseSingle :: CompileOptions
-> [(a, String)] -> m ([(a, String)], CompileOptions)
parseSingle CompileOptions
_ [] = m ([(a, String)], CompileOptions)
forall a. HasCallStack => a
undefined

  parseSingle (CompileOptions HelpMode
_ [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
_,String
"-h"):[(a, String)]
os) =
    ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions HelpMode
HelpNeeded [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
_) ((a
_,String
"-f"):[(a, String)]
os) =
    ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
ForceAll)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-c"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-c" String
"Compiler mode already set."
    | Bool
otherwise = ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p ([String] -> CompileMode
CompileIncremental []) ForceMode
f)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-r"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-r" String
"Compiler mode already set."
    | Bool
otherwise = ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
CompileRecompile ForceMode
f)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-R"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-r" String
"Compiler mode already set."
    | Bool
otherwise = ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
CompileRecompileRecursive ForceMode
f)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-t"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-t" String
"Compiler mode already set."
    | Bool
otherwise = ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p ([String] -> CompileMode
ExecuteTests []) ForceMode
f)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"--templates"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-t" String
"Compiler mode already set."
    | Bool
otherwise = ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
CreateTemplates ForceMode
f)

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-m"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-m" String
"Compiler mode already set."
    | Bool
otherwise = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
      update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
n2,String
c):[(a, String)]
os2) =  do
        (String
t,String
fn) <- (String, String) -> m (String, String)
forall (m :: * -> *) a.
ErrorContextM m =>
(a, String) -> m (a, String)
check ((String, String) -> m (String, String))
-> (String, String) -> m (String, String)
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.') String
c
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkCategoryName a
n2 String
t  String
"-m"
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkFunctionName a
n2 String
fn String
"-m"
        let m2 :: CompileMode
m2 = CategoryName -> FunctionName -> String -> [String] -> CompileMode
CompileBinary (String -> CategoryName
CategoryName String
t) (String -> FunctionName
FunctionName String
fn) String
"" []
        ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m2 ForceMode
f) where
          check :: (a, String) -> m (a, String)
check (a
t,String
"")     = (a, String) -> m (a, String)
forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
defaultMainFunc)
          check (a
t,Char
'.':String
fn) = (a, String) -> m (a, String)
forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
fn)
          check (a, String)
_          = a -> String -> String -> m (a, String)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
c (String -> m (a, String)) -> String -> m (a, String)
forall a b. (a -> b) -> a -> b
$ String
"Invalid entry point."
      update [(a, String)]
_ = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-m" String
"Requires a category name."

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"--fast"):[(a, String)]
os)
    | CompileMode
m CompileMode -> CompileMode -> Bool
forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"--fast" String
"Compiler mode already set."
    | Bool
otherwise = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
      update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
n2,String
c):(a
n3,String
f2):[(a, String)]
os2) =  do
        (String
t,String
fn) <- (String, String) -> m (String, String)
forall (m :: * -> *) a.
ErrorContextM m =>
(a, String) -> m (a, String)
check ((String, String) -> m (String, String))
-> (String, String) -> m (String, String)
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.') String
c
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkCategoryName a
n2 String
t  String
"--fast"
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkFunctionName a
n2 String
fn String
"--fast"
        Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rx" String
f2) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ a -> String -> String -> m ()
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n3 String
f2 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
"Must specify a .0rx source file."
        let m2 :: CompileMode
m2 = CategoryName -> FunctionName -> String -> CompileMode
CompileFast (String -> CategoryName
CategoryName String
t) (String -> FunctionName
FunctionName String
fn) String
f2
        ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m2 ForceMode
f) where
          check :: (a, String) -> m (a, String)
check (a
t,String
"")     = (a, String) -> m (a, String)
forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
defaultMainFunc)
          check (a
t,Char
'.':String
fn) = (a, String) -> m (a, String)
forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
fn)
          check (a, String)
_          = a -> String -> String -> m (a, String)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
c (String -> m (a, String)) -> String -> m (a, String)
forall a b. (a -> b) -> a -> b
$ String
"Invalid entry point."
      update [(a, String)]
_ = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"--fast" String
"Requires a category name and a .0rx file."

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p (CompileBinary CategoryName
t FunctionName
fn String
o [String]
lf) ForceMode
f) ((a
n,String
"-o"):[(a, String)]
os)
    | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
o = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-o" String
"Output name already set."
    | Bool
otherwise = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
      update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
n2,String
o2):[(a, String)]
os2) = do
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
o2 String
"-o"
        ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p (CategoryName -> FunctionName -> String -> [String] -> CompileMode
CompileBinary CategoryName
t FunctionName
fn String
o2 [String]
lf) ForceMode
f)
      update [(a, String)]
_ = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-o" String
"Requires an output name."
  parseSingle CompileOptions
_ ((a
n,String
"-o"):[(a, String)]
_) = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-o" String
"Set mode to binary (-m) first"

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-i"):[(a, String)]
os) = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
    update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
n2,String
d):[(a, String)]
os2)
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rp" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
d String
"Cannot directly include .0rp source files."
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rx" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
d String
"Cannot directly include .0rx source files."
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rt" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
d String
"Cannot directly include .0rt test files."
      | Bool
otherwise = do
          a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
d String
"-i"
          ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) ([String]
is [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
d]) [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f)
    update [(a, String)]
_ = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-i" String
"Requires a source path."

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-I"):[(a, String)]
os) = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
    update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
n2,String
d):[(a, String)]
os2)
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rp" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
d String
"Cannot directly include .0rp source files."
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rx" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
d String
"Cannot directly include .0rx source files."
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rt" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
d String
"Cannot directly include .0rt test files."
      | Bool
otherwise = do
          a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
d String
"-i"
          ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is ([String]
is2 [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
d]) [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f)
    update [(a, String)]
_ = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-I" String
"Requires a source path."

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
"-p"):[(a, String)]
os)
    | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
p = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-p" String
"Path prefix already set."
    | Bool
otherwise = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
      update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
n2,String
p2):[(a, String)]
os2) = do
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
p2 String
"-p"
        ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p2 CompileMode
m ForceMode
f)
      update [(a, String)]
_ = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-p" String
"Requires a path prefix."

  parseSingle CompileOptions
_ ((a
n,o :: String
o@(Char
'-':String
_)):[(a, String)]
_) = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
o String
"Unknown option."

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f) ((a
n,String
d):[(a, String)]
os)
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rp" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
d String
"Cannot directly include .0rp source files."
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rx" String
d = a -> String -> String -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
d String
"Cannot directly include .0rx source files."
      | String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSuffixOf String
".0rt" String
d = do
        Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isExecuteTests CompileMode
m) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
          a -> String -> String -> m ()
forall (m :: * -> *) a a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
d String
"Test mode (-t) must be enabled before listing any .0rt test files."
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n String
d String
""
        let (ExecuteTests [String]
tp) = CompileMode
m
        ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p ([String] -> CompileMode
ExecuteTests ([String] -> CompileMode) -> [String] -> CompileMode
forall a b. (a -> b) -> a -> b
$ [String]
tp [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
d]) ForceMode
f)
      | Bool
otherwise = do
        a -> String -> String -> m ()
forall (m :: * -> *) a.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n String
d String
""
        ([(a, String)], CompileOptions)
-> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,HelpMode
-> [String]
-> [String]
-> [String]
-> [ExtraSource]
-> [String]
-> String
-> CompileMode
-> ForceMode
-> CompileOptions
CompileOptions (HelpMode -> HelpMode
maybeDisableHelp HelpMode
h) [String]
is [String]
is2 ([String]
ds [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
d]) [ExtraSource]
es [String]
ep String
p CompileMode
m ForceMode
f)

validateCompileOptions :: CollectErrorsM m => CompileOptions -> m CompileOptions
validateCompileOptions :: CompileOptions -> m CompileOptions
validateCompileOptions co :: CompileOptions
co@(CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
_ [String]
_ String
_ CompileMode
m ForceMode
_)
  | HelpMode
h HelpMode -> HelpMode -> Bool
forall a. Eq a => a -> a -> Bool
/= HelpMode
HelpNotNeeded = CompileOptions -> m CompileOptions
forall (m :: * -> *) a. Monad m => a -> m a
return CompileOptions
co

  | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([String] -> Bool) -> [String] -> Bool
forall a b. (a -> b) -> a -> b
$ [String]
is [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
is2) Bool -> Bool -> Bool
&& (CompileMode -> Bool
isExecuteTests CompileMode
m) =
    String -> m CompileOptions
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Include paths (-i/-I) are not allowed in test mode (-t)."

  | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([String] -> Bool) -> [String] -> Bool
forall a b. (a -> b) -> a -> b
$ [String]
is [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
is2) Bool -> Bool -> Bool
&& (CompileMode -> Bool
isCompileRecompile CompileMode
m) =
    String -> m CompileOptions
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Include paths (-i/-I) are not allowed in recompile mode (-r/-R)."

  | ([String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
ds Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0) Bool -> Bool -> Bool
&& (CompileMode -> Bool
isCompileFast CompileMode
m) =
    String -> m CompileOptions
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Input path is not allowed with fast mode (--fast)."
  | [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
ds Bool -> Bool -> Bool
&& (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isCompileFast CompileMode
m) =
    String -> m CompileOptions
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Please specify at least one input path."
  | ([String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
ds Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
1) Bool -> Bool -> Bool
&& (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isCompileRecompile CompileMode
m) Bool -> Bool -> Bool
&& (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isExecuteTests CompileMode
m) =
    String -> m CompileOptions
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Multiple input paths are only allowed with recompile mode (-r/-R) and test mode (-t)."

  | Bool
otherwise = CompileOptions -> m CompileOptions
forall (m :: * -> *) a. Monad m => a -> m a
return CompileOptions
co