{- -----------------------------------------------------------------------------
Copyright 2020-2021 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 Lens.Micro
import Text.Regex.TDFA
import qualified Data.Set as Set

import Base.CompilerError
import Cli.CompileOptions
import Module.ProcessMetadata (isPrivateSource,isPublicSource,isTestSource)
import Types.TypeCategory (FunctionName(..))
import Types.TypeInstance (CategoryName(..))


optionHelpText :: [String]
optionHelpText :: [String]
optionHelpText = [
    String
"",
    String
"Compilation Modes:",
    String
"",
    String
"  zeolite [options...] --fast [category(.function)] [.0rx source]",
    String
"    Create a binary without needing a config.",
    String
"",
    String
"  zeolite [options...] -r [modules...]",
    String
"    Recompile using each module's .zeolite-module config.",
    String
"",
    String
"  zeolite [options...] -R [modules...]",
    String
"    Recursively recompile using each module's .zeolite-module config.",
    String
"",
    String
"  zeolite [options...] -t (--log-traces [filename]) [modules...] (tests...)",
    String
"    Only execute tests, without other compilation.",
    String
"",
    String
"Configuration Modes:",
    String
"",
    String
"  zeolite [options...] -c [module]",
    String
"    Create a new .zeolite-module config for a libary module.",
    String
"",
    String
"  zeolite [options...] -m [category(.function)] (-o [binary]) [module]",
    String
"    Create a new .zeolite-module config for a binary module.",
    String
"",
    String
"Special Modes:",
    String
"",
    String
"  zeolite [options...] --templates [modules...]",
    String
"    Only create C++ templates for undefined categories in .0rp sources.",
    String
"",
    String
"  zeolite (-p [path]) --clean [modules...]",
    String
"    Remove all cached data for the modules.",
    String
"",
    String
"  zeolite (-p [path]) --missed-lines [filename] [modules...]",
    String
"    List all lines missed in a log file taken from --log-traces.",
    String
"",
    String
"  zeolite (-p [path]) --show-deps [modules...]",
    String
"    Show category dependencies for the modules.",
    String
"",
    String
"  zeolite (-p [path]) --show-traces [modules...]",
    String
"    Show the possible code traces for the modules.",
    String
"",
    String
"Compiler Info:",
    String
"",
    String
"  zeolite --get-path",
    String
"    Show the data path and immediately exit.",
    String
"",
    String
"  zeolite --version",
    String
"    Show the compiler version and immediately exit.",
    String
"",
    String
"Options:",
    String
"  -f: Force an operation that zeolite would otherwise reject.",
    String
"  -i [module]: A single source module to include as a public dependency.",
    String
"  -I [module]: A single source module to include as a private dependency.",
    String
"  -j [# parallel]: Parallel execution of compilation subprocesses.",
    String
"  -o [binary]: The name of the binary file to create with -m.",
    String
"  -p [path]: Set a path prefix for finding modules or files.",
    String
"  --log-traces [filename]: Log call traces to a file when running tests.",
    String
"",
    String
"Argument Types:",
    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
"  module: Path to a directory containing an existing or to-be-created Zeolite module.",
    String
"  test: Name of a .0rt file to limit test execution to.",
    String
"",
    String
"Examples:",
    String
"",
    String
"  # Create a config for a binary in myprogram that calls MyProgram.run() that uses lib/util.",
    String
"  zeolite -I lib/util -m MyProgram.run myprogram",
    String
"",
    String
"  # Compile a binary that calls MyProgram.run() from myprogram.0rx without a config.",
    String
"  zeolite -I lib/util --fast MyProgram.run myprogram.0rx",
    String
"",
    String
"  # Create a config for a library in mylibrary that uses lib/util.",
    String
"  zeolite -I lib/util -c mylibrary",
    String
"",
    String
"  # Recompile myprogram, recursively recompiling its dependencies first.",
    String
"  zeolite -R myprogram",
    String
"",
    String
"  # Execute the tests from .0rt files in mylibrary.",
    String
"  zeolite -t mylibrary",
    String
"",
    String
"  # Display the contexts of all code lines missed by tests for mylibrary.",
    String
"  zeolite -t --log-traces traces.csv mylibrary",
    String
"  zeolite --missed-lines traces.csv mylibrary",
    String
""
  ]

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

optionsWithArgs :: Set.Set Char
optionsWithArgs :: Set Char
optionsWithArgs = forall a. Ord a => [a] -> Set a
Set.fromList String
"iIjmop"

splitOptionClusters :: [String] -> [String]
splitOptionClusters :: [String] -> [String]
splitOptionClusters (o :: String
o@(Char
'-':Char
c:String
rest):[String]
os)
  | Char
c forall a. Eq a => a -> a -> Bool
== Char
'-' Bool -> Bool -> Bool
|| forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
rest          = String
o forall a. a -> [a] -> [a]
: [String] -> [String]
splitOptionClusters [String]
os
  | Char
c forall a. Ord a => a -> Set a -> Bool
`Set.member` Set Char
optionsWithArgs = (Char
'-'forall a. a -> [a] -> [a]
:Char
cforall a. a -> [a] -> [a]
:[]) forall a. a -> [a] -> [a]
: String
rest forall a. a -> [a] -> [a]
: [String] -> [String]
splitOptionClusters [String]
os
  | Bool
otherwise                      = (Char
'-'forall a. a -> [a] -> [a]
:Char
cforall a. a -> [a] -> [a]
:[]) forall a. a -> [a] -> [a]
: [String] -> [String]
splitOptionClusters ((Char
'-'forall a. a -> [a] -> [a]
:String
rest)forall a. a -> [a] -> [a]
:[String]
os)
splitOptionClusters (String
o:[String]
os) = String
o forall a. a -> [a] -> [a]
: [String] -> [String]
splitOptionClusters [String]
os
splitOptionClusters [] = []

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

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

  parseSingle CompileOptions
opts ((a
_,String
"-h"):[(a, String)]
os) =
    forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> b -> s -> t
.~ HelpMode
HelpNeeded)

  parseSingle CompileOptions
opts ((a
_,String
"-f"):[(a, String)]
os) =
    forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions ForceMode
coForce forall s t a b. ASetter s t a b -> b -> s -> t
.~ ForceMode
ForceAll)

  parseSingle CompileOptions
opts ((a
n,String
"-c"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = 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 = forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ ([String] -> CompileMode
CompileIncremental []))

  parseSingle CompileOptions
opts ((a
n,String
"-r"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = 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 = forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ CompileMode
CompileRecompile)

  parseSingle CompileOptions
opts ((a
n,String
"-R"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = 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 = forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ CompileMode
CompileRecompileRecursive)

  parseSingle CompileOptions
opts ((a
n,String
"-t"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = 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 = forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ ([String] -> Maybe String -> CompileMode
ExecuteTests [] forall a. Maybe a
Nothing))

  parseSingle CompileOptions
opts ((a
n,String
"--templates"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"--templates" String
"Compiler mode already set."
    | Bool
otherwise = forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ CompileMode
CreateTemplates)

  parseSingle CompileOptions
opts ((a
n,String
"-m"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = 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 = 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) <- forall {m :: * -> *} {a}.
ErrorContextM m =>
(a, String) -> m (a, String)
check forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> ([a], [a])
break (forall a. Eq a => a -> a -> Bool
== Char
'.') String
c
        forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkCategoryName a
n2 String
t  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 -> LinkerMode -> String -> [String] -> CompileMode
CompileBinary (String -> CategoryName
CategoryName String
t) (String -> FunctionName
FunctionName String
fn) LinkerMode
LinkDynamic String
"" []
        forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ CompileMode
m2) where
          check :: (a, String) -> m (a, String)
check (a
t,String
"")     = forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
defaultMainFunc)
          check (a
t,Char
'.':String
fn) = forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
fn)
          check (a, String)
_          = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
c forall a b. (a -> b) -> a -> b
$ String
"Invalid entry point."
      update [(a, String)]
_ = 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
opts ((a
n,String
"--fast"):[(a, String)]
os)
    | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) forall a. Eq a => a -> a -> Bool
/= CompileMode
CompileUnspecified = 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 = 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) <- forall {m :: * -> *} {a}.
ErrorContextM m =>
(a, String) -> m (a, String)
check forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> ([a], [a])
break (forall a. Eq a => a -> a -> Bool
== Char
'.') String
c
        forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkCategoryName a
n2 String
t  String
"--fast"
        forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkFunctionName a
n2 String
fn String
"--fast"
        forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ String -> Bool
isPrivateSource String
f2) forall a b. (a -> b) -> a -> b
$ forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n3 String
f2 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
        forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ CompileMode
m2) where
          check :: (a, String) -> m (a, String)
check (a
t,String
"")     = forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
defaultMainFunc)
          check (a
t,Char
'.':String
fn) = forall (m :: * -> *) a. Monad m => a -> m a
return (a
t,String
fn)
          check (a, String)
_          = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
c forall a b. (a -> b) -> a -> b
$ String
"Invalid entry point."
      update [(a, String)]
_ = 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
opts ((a
n,String
"--log-traces"):[(a, String)]
os) = forall {m :: * -> *} {a}.
ErrorContextM m =>
CompileMode -> [(a, String)] -> m ([(a, String)], CompileOptions)
update (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) [(a, String)]
os where
    update :: CompileMode -> [(a, String)] -> m ([(a, String)], CompileOptions)
update (ExecuteTests [String]
tp Maybe String
cl) ((a
_,String
cl2):[(a, String)]
os2)
      | Maybe String
cl forall a. Eq a => a -> a -> Bool
/= forall a. Maybe a
Nothing = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"--log-traces" String
"Trace-log filename already set."
      | Bool
otherwise = forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ ([String] -> Maybe String -> CompileMode
ExecuteTests [String]
tp (forall a. a -> Maybe a
Just String
cl2)))
    update CompileMode
_ [] = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"--log-traces" String
"Requires an output filename."
    update CompileMode
_ [(a, String)]
_  = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"--log-traces" String
"Set mode to test (-t) first."

  parseSingle CompileOptions
opts ((a
n,String
"-o"):[(a, String)]
os) = forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
CompileMode -> [(a, String)] -> m ([(a, String)], CompileOptions)
update (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) [(a, String)]
os where
    update :: CompileMode -> [(a, String)] -> m ([(a, String)], CompileOptions)
update (CompileBinary CategoryName
t FunctionName
fn LinkerMode
lm String
o [String]
lf) ((a
n2,String
o2):[(a, String)]
os2)
      | Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
o =  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 = do
          forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
o2 String
"-o"
          forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ (CategoryName
-> FunctionName -> LinkerMode -> String -> [String] -> CompileMode
CompileBinary CategoryName
t FunctionName
fn LinkerMode
lm String
o2 [String]
lf))
    update CompileMode
_ [] = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-o" String
"Requires an output name."
    update CompileMode
_ [(a, String)]
_  = 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
opts ((a
n,String
"-i"):[(a, String)]
os) = 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 -> Bool
isPublicSource  String
d = 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 -> Bool
isPrivateSource String
d = 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 -> Bool
isTestSource    String
d = 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
          forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
d String
"-i"
          forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions [String]
coPublicDeps forall a s t. Monoid a => ASetter s t a a -> a -> s -> t
<>~ [String
d])
    update [(a, String)]
_ = 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
opts ((a
n,String
"-I"):[(a, String)]
os) = 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 -> Bool
isPublicSource  String
d = 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 -> Bool
isPrivateSource String
d = 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 -> Bool
isTestSource    String
d = 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
          forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
d String
"-i"
          forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions [String]
coPrivateDeps forall a s t. Monoid a => ASetter s t a a -> a -> s -> t
<>~ [String
d])
    update [(a, String)]
_ = 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
opts ((a
n,String
"-p"):[(a, String)]
os)
    | Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Bool
null (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions String
coSourcePrefix) = 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 = 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
        forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n2 String
p2 String
"-p"
        forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions String
coSourcePrefix forall s t a b. ASetter s t a b -> b -> s -> t
.~ String
p2)
      update [(a, String)]
_ = 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
opts ((a
n,String
"-j"):[(a, String)]
os) = 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
k):[(a, String)]
os2) = do
      case forall a. Read a => ReadS a
reads String
k of
           [(Int
pn,[])] -> if Int
pn forall a. Ord a => a -> a -> Bool
> Int
0
                           then forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os2,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions Int
coParallel forall s t a b. ASetter s t a b -> b -> s -> t
.~ Int
pn)
                           else forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
k String
"Must be > 0."
           [(Int, String)]
_ -> forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n2 String
k String
"Not a valid integer."
    update [(a, String)]
_ = forall {m :: * -> *} {a} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m a
argError a
n String
"-j" String
"Requires an integer > 0."

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

  parseSingle CompileOptions
opts ((a
n,String
d):[(a, String)]
os)
      | String -> Bool
isPublicSource  String
d = 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 -> Bool
isPrivateSource String
d = 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 -> Bool
isTestSource    String
d = do
        case CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode of
             ExecuteTests [String]
tp Maybe String
cl -> do
               forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n String
d String
""
               forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions CompileMode
coMode forall s t a b. ASetter s t a b -> b -> s -> t
.~ ([String] -> Maybe String -> CompileMode
ExecuteTests ([String]
tp forall a. [a] -> [a] -> [a]
++ [String
d]) Maybe String
cl))
             CompileMode
_ -> 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."
      | Bool
otherwise = do
        forall {m :: * -> *} {a}.
(ErrorContextM m, Show a) =>
a -> String -> String -> m ()
checkPathName a
n String
d String
""
        forall (m :: * -> *) a. Monad m => a -> m a
return ([(a, String)]
os,CompileOptions
opts forall a b. a -> (a -> b) -> b
& Lens' CompileOptions HelpMode
coHelp forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ HelpMode -> HelpMode
maybeDisableHelp forall a b. a -> (a -> b) -> b
& Lens' CompileOptions [String]
coPaths forall a s t. Monoid a => ASetter s t a a -> a -> s -> t
<>~ [String
d])

validateCompileOptions :: CollectErrorsM m => CompileOptions -> m CompileOptions
validateCompileOptions :: forall (m :: * -> *).
CollectErrorsM m =>
CompileOptions -> m CompileOptions
validateCompileOptions CompileOptions
opts
  | (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions HelpMode
coHelp) forall a. Eq a => a -> a -> Bool
/= HelpMode
HelpNotNeeded = forall (m :: * -> *) a. Monad m => a -> m a
return CompileOptions
opts

  | CompileMode -> Bool
isCompileUnspecified (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode) =
    forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Compiler mode must be specified explicitly."

  | (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall a b. (a -> b) -> a -> b
$ (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPublicDeps) forall a. [a] -> [a] -> [a]
++ (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPrivateDeps)) Bool -> Bool -> Bool
&& (CompileMode -> Bool
isExecuteTests (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) =
    forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Include paths (-i/-I) are not allowed in test mode (-t)."

  | (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall a b. (a -> b) -> a -> b
$ (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPublicDeps) forall a. [a] -> [a] -> [a]
++ (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPrivateDeps)) Bool -> Bool -> Bool
&& (CompileMode -> Bool
isCompileRecompile (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) =
    forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Include paths (-i/-I) are not allowed in recompile mode (-r/-R)."

  | (forall (t :: * -> *) a. Foldable t => t a -> Int
length (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPaths) forall a. Eq a => a -> a -> Bool
/= Int
0) Bool -> Bool -> Bool
&& (CompileMode -> Bool
isCompileFast (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) =
    forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Input path is not allowed with fast mode (--fast)."
  | forall (t :: * -> *) a. Foldable t => t a -> Bool
null (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPaths) Bool -> Bool -> Bool
&& (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isCompileFast (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) =
    forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Please specify at least one input path."
  | (forall (t :: * -> *) a. Foldable t => t a -> Int
length (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions [String]
coPaths) forall a. Ord a => a -> a -> Bool
> Int
1) Bool -> Bool -> Bool
&& (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isCompileRecompile (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) Bool -> Bool -> Bool
&& (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ CompileMode -> Bool
isExecuteTests (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) =
    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 = do
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions Int
coParallel) forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
&& Bool -> Bool
not (CompileMode -> Bool
isCompileRecompile (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode)) Bool -> Bool -> Bool
&& Bool -> Bool
not (CompileMode -> Bool
isCompileFast (CompileOptions
opts forall s a. s -> Getting a s a -> a
^. Lens' CompileOptions CompileMode
coMode))) forall a b. (a -> b) -> a -> b
$
      forall (m :: * -> *). ErrorContextM m => String -> m ()
compilerWarningM String
"Parallel processing (-j) has no effect outside of recompile mode (-r/-R) and fast mode (--fast)."
    forall (m :: * -> *) a. Monad m => a -> m a
return CompileOptions
opts