{- -----------------------------------------------------------------------------
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 Text.Regex.TDFA

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

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] -> Maybe String -> CompileMode
ExecuteTests [] Maybe String
forall a. Maybe a
Nothing) 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 -> LinkerMode -> String -> [String] -> CompileMode
CompileBinary (String -> CategoryName
CategoryName String
t) (String -> FunctionName
FunctionName String
fn) LinkerMode
LinkDynamic 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 -> Bool
isPrivateSource 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 (ExecuteTests [String]
tp Maybe String
cl) ForceMode
f) ((a
n,String
"--log-traces"):[(a, String)]
os) = [(a, String)] -> m ([(a, String)], CompileOptions)
forall (m :: * -> *) a.
ErrorContextM m =>
[(a, String)] -> m ([(a, String)], CompileOptions)
update [(a, String)]
os where
    update :: [(a, String)] -> m ([(a, String)], CompileOptions)
update ((a
_,String
cl2):[(a, String)]
os2)
      | Maybe String
cl Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
/= Maybe String
forall a. Maybe a
Nothing = a -> String -> String -> m ([(a, String)], CompileOptions)
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 = ([(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 ([String] -> Maybe String -> CompileMode
ExecuteTests [String]
tp (String -> Maybe String
forall a. a -> Maybe a
Just String
cl2)) 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
"--log-traces" String
"Requires an output filename."
  parseSingle CompileOptions
_ ((a
n,String
"--log-traces"):[(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
"--log-traces" String
"Set mode to test (-t) first."

  parseSingle (CompileOptions HelpMode
h [String]
is [String]
is2 [String]
ds [ExtraSource]
es [String]
ep String
p (CompileBinary CategoryName
t FunctionName
fn LinkerMode
lm 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 -> LinkerMode -> String -> [String] -> CompileMode
CompileBinary CategoryName
t FunctionName
fn LinkerMode
lm 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 -> Bool
isPublicSource  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 -> Bool
isPrivateSource 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 -> Bool
isTestSource    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 -> Bool
isPublicSource  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 -> Bool
isPrivateSource 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 -> Bool
isTestSource    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 -> Bool
isPublicSource  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 -> Bool
isPrivateSource 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 -> Bool
isTestSource    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 Maybe String
cl) = 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] -> Maybe String -> CompileMode
ExecuteTests ([String]
tp [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
d]) Maybe String
cl) 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

  | CompileMode -> Bool
isCompileUnspecified CompileMode
m =
    String -> m CompileOptions
forall (m :: * -> *) a. ErrorContextM m => String -> m a
compilerErrorM String
"Compiler mode must be specified explicitly."

  | (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