【问题标题】:Converting literate Haskell (.lhs) to Haskell (.hs)将 literate Haskell (.lhs) 转换为 Haskell (.hs)
【发布时间】:2020-02-05 20:11:02
【问题描述】:

有没有一种简单的方法可以将读写的 Haskell 文件 (.lhs) 转换为常规的 Haskell (.hs) 源文件?

我认为可能有 GHC 选项,但 GHC 手册似乎没有太多关于 literate 程序或 .lhs 格式的信息。 “识字”这个词甚至没有出现在索引中!

Wiki 上的 Literate programming 链接包含指向在“bird”和“\begin{code}..\end{code}”样式之间转换或将 .lhs 转换为 TeX 格式的脚本的链接,仅此而已。

【问题讨论】:

  • 为什么这个问题跑题了?它清晰简洁,当然是实用且可回答的。我也认为识字的haskell文件的语法和语义在“关于Haskell的问题”的范围内非常重要

标签: haskell literate-programming


【解决方案1】:

GHC 本身使用名为unlit 的独立C 程序来处理.lhs 文件。您可能会发现它安装在您的 GHC 安装中的某个位置。如果你运行它,它会显示一些命令行选项而没有解释:

$ cd ~/.stack/programs/x86_64-linux/ghc-8.6.4/lib/ghc-8.6.4/bin
$ ./unlit
usage: unlit [-q] [-n] [-c] [-#] [-P] [-h label] file1 file2

深入研究源代码,看起来选项是:

-q   "quiet": ignore certain errors, so permit missing empty lines before and after
     "bird" style and accept completely empty programs
-n   (default) "noisy": opposite of -q, so be noisy about errors
-c   "crunch": don't leave blank lines where literate comments are removed
-#   delete lines that start with "#!"
-P   suppress CPP line number pragma, but only matters if `-h` is supplied
-h   File name for an initial `#line` pragma

所以,命令行:

$ /path/to/unlit -c myfile.lhs myfile.hs

可能会很好地转换myfile.lhs

This is a literate program

> main :: IO ()

using both code styles
\begin{code}
  main = putStrLn "hello"
\end{code}

到一个“文盲”程序myfile.hs

  main :: IO ()
  main = putStrLn "hello"

对于“鸟”样式,它实际上用空格替换了 '>' 字符并将其余缩进保留在原位,因此对于我上面的示例,myfile.hs 中的两行都缩进了两个空格,这可以成为一个缺点。

【讨论】:

    【解决方案2】:

    只需编写如下所示的快速脚本即可:) 这会将文字部分转换为 line-cmets。

    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ViewPatterns #-}
    
    import Control.Monad (forM_)
    import System.FilePath.Glob (glob)
    import qualified Data.Text as T
    import qualified Data.Text.IO as T
    import System.FilePath (replaceExtension)
    import Development.Shake.Command
    
    unLiterateHaskell :: T.Text -> T.Text
    unLiterateHaskell = T.unlines . map processLine . T.lines
    
    processLine :: T.Text -> T.Text
    processLine (T.stripEnd -> txt) = case T.uncons txt of
      Nothing -> txt
      Just ('>', txt) -> T.drop 1 txt -- may need to adjust here depending on style
      Just{} -> "-- " <> txt
    
    main :: IO ()
    main = do
      files <- glob "**/*.lhs"
      forM_ files $ \f -> do
        src <- T.readFile f
        T.writeFile (replaceExtension f "hs") (unLiterateHaskell src)
        Exit _ <- cmd ("mv" :: String) f (f <> ".bak")
        pure ()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-19
      • 2011-02-16
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多