import Minilang.REPL (evaluatorFromEnv, withIO, withTerminal) import Minilang.REPL.Types (Evaluator (..)) import Minilang.Server (startServer, waitServer) import Options.Applicative import System.IO (stdin, stdout) import System.Posix.IO (stdInput) import System.Posix.Terminal (queryTerminal) import Text.Read (readMaybe) data RunMode = Local | Server deriving (Eq, Show) data Options = Options { serverPort :: Int, runMode :: RunMode, -- | 'Nothing' means "read from MINILANG_EVAL env var". evalOpt :: Maybe Evaluator } deriving (Eq, Show) optionsParser :: ParserInfo Options optionsParser = info (helper <*> minilangOptions) ( header "Minilang" <> fullDesc ) minilangOptions :: Parser Options minilangOptions = Options <$> portOption <*> runModeOption <*> evalOption portOption :: Parser Int portOption = option auto ( long "port" <> short 'p' <> value 7890 <> metavar "INT" <> help "port to listen on (default: 7890)" ) runModeOption :: Parser RunMode runModeOption = flag Local Server ( long "server" <> short 'S' <> help "run in server mode, listening for client REPLs over HTTP" ) evalOption :: Parser (Maybe Evaluator) evalOption = optional $ option (eitherReader parseEvaluator) ( long "eval" <> short 'E' <> metavar "EVALUATOR" <> help "reduction strategy: DeBruijn (default) or NbE. \ \Falls back to the MINILANG_EVAL environment variable." ) parseEvaluator :: String -> Either String Evaluator parseEvaluator s = case readMaybe s of Just ev -> Right ev Nothing -> Left $ "Unknown evaluator: " <> show s <> ". Valid values: DeBruijn, NbE" main :: IO () main = do Options {..} <- execParser optionsParser -- CLI flag takes precedence; fall back to env var. ev <- maybe evaluatorFromEnv pure evalOpt case runMode of Local -> do hasTty <- queryTerminal stdInput if hasTty then withTerminal ev else withIO ev stdin stdout Server -> startServer serverPort ev >>= waitServer