[英]haskell - Parsing command-line and REPL commands and options
我正在編寫一個既有命令行界面又有交互模式的程序。 在CLI模式下,它執行一個命令,打印結果並退出。 在交互模式下,它使用GNU readline重復讀取命令,執行它們並打印結果(以REPL的精神)。
命令及其參數的語法幾乎相同,無論它們來自命令行還是frmo stdin。 我想通過使用單個框架來解析命令行和交互模式輸入來最大化代碼重用。
我建議的語法是(方括號表示可選部分,括號重復),如下所示:
來自shell:
program-name {[GLOBAL OPTION] ...} <command> [{<command arg>|<GLOBAL OPTION>|<LOCAL OPTION> ...}]
在交互模式下:
<command> [{<command arg>|<GLOBAL OPTION>|<LOCAL OPTION> ...}]
本地選項僅對一個特定命令有效(不同的命令可以為一個選項指定不同的含義)。
我的問題是CL和交互式接口之間存在一些差異:某些全局選項僅在命令行中有效(如--help, - version或--config-file)。 顯然還有'quit'命令在交互模式下非常重要,但是從CL使用它是沒有意義的。
為了解決這個問題,我搜索了網絡和hackage的命令行解析庫。 我發現最有趣的是cmdlib和optparse-applicative。 但是,我對Haskell很新,即使我可以通過復制和修改庫文檔中的示例代碼來創建工作程序,我還不太了解這些庫的機制,因此無法解決我的問題。
我有這些問題:
如何為CL和REPL接口常用的命令和選項創建基本解析器,然后能夠使用新命令和選項擴展基本解析器?
如何在輸入錯誤或使用“--help”時阻止這些庫退出程序?
我計划為我的程序添加完整的i18n支持。 因此,我想阻止我選擇的庫打印任何消息,因為所有消息都需要翻譯。 怎么做到這一點?
所以我希望你能給我一些關於從哪里開始的提示。 cmdlib或optparse-applicative(或其他一些庫)是否支持我正在尋找的內容? 或者我應該恢復到手工制作的解析器?
我想你可以使用我的庫http://hackage.haskell.org/package/options來做到這一點。 子命令功能與您正在查找的命令標志解析行為完全匹配。
在兩個不相交的選項集之間共享子命令會有點棘手,但輔助類型類應該能夠做到。 粗略的示例代碼:
-- A type for options shared between CLI and interactive modes.
data CommonOptions = CommonOptions
{ optSomeOption :: Bool
}
instance Options CommonOptions where ...
-- A type for options only available in CLI mode (such as --version or --config-file)
data CliOptions = CliOptions
{ common :: CommonOptions
, version :: Bool
, configFile :: String
}
instance Options CliOptions where ...
-- if a command takes only global options, it can use this subcommand option type.
data NoOptions = NoOptions
instance Options NoOptions where
defineOptions = pure NoOptions
-- typeclass to let commands available in both modes access common options
class HasCommonOptions a where
getCommonOptions :: a -> CommonOptions
instance HasCommonOptions CommonOptions where
getCommonOptions = id
instance HasCommonOptions CliOptions where
getCommonOptions = common
commonCommands :: HasCommonOptions a => [Subcommand a (IO ())]
commonCommands = [... {- your commands here -} ...]
cliCommands :: HasCommonOptions a => [Subcommand a (IO ())]
cliCommands = commonCommands ++ [cmdRepl]
interactiveCommands :: HasCommonOptions a => [Subcommand a (IO ())]
interactiveCommands = commonCommands ++ [cmdQuit]
cmdRepl :: HasCommonOptions a => Subcommand a (IO ())
cmdRepl = subcommand "repl" $ \opts NoOptions -> do
{- run your interactive REPL here -}
cmdQuit :: Subcommand a (IO ())
cmdQuit = subcommand "quit" (\_ NoOptions -> exitSuccess)
我懷疑像runSubcommand
這樣的輔助函數不夠專業,因此一旦從REPL提示中拆分輸入字符串,就會想要用parseSubcommand
調用解析器。 文檔提供了如何檢查已解析選項的示例,包括檢查用戶是否請求了幫助。
選項解析器本身不會打印任何輸出,但可能很難國際化默認類型解析器生成的錯誤消息。 如果對圖書館有任何改變,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.