我教ghci来编译我的StackOverflow帖子。我可以让它更光滑吗?

时间:2012-10-01 01:55:03

标签: haskell ghci

Haskell Stack Overflow布局预处理器

module StackOverflow where  -- yes, the source of this post compiles as is

如果您想先玩这个(1/2路向下),请跳至如何使其正常工作
如果我有点烦恼,你可以跳到我想要的东西,你只想找到我正在寻求的帮助。

TLDR问题摘要:

  1. 我可以在我的:so中定义的ghci.conf命令中添加ghci来添加文件名完成吗?
  2. 我可以以某种方式定义一个ghci命令,它返回编译代码而不是返回一个ghci命令,或者 ghci是否有更好的方法让我插入Haskell代码作为 特定于文件扩展名的预处理器,因此:l像往常一样适用于.hs.lhs个文件,但是使用我的手写预处理器来处理.so个文件?
  3. 背景:

    Haskell支持.lhs源文件中的文字编程,有两种方式:

    • LaTeX样式\begin{code}\end{code}
    • Bird track:代码以>开头,其他任何内容都是评论 代码和注释之间必须有一个空行(以防止意外误用>)。

    Bird不跟踪规则声音类似于StackOverflow的代码块吗?

    参考文献: 1。 The .ghci manual   2。 GHCi haskellwiki   3。 Neil Mitchell blogs about :{ and :} in .ghci

    预处理器

    我喜欢在文本编辑器中编写SO答案,我喜欢发表一个包含有效代码的帖子, 但最终会发布评论块或>,我必须在发布前编辑,这样不那么有趣。

    所以,我给自己写了一个预处理器。

    • 如果我将某些ghci内容粘贴为代码块,则通常以*:开头。
    • 如果该行完全空白,我不希望将其视为代码,否则将其视为代码 我得到了意外的代码 - 接下来的评论行错误,因为我不小心看不到4个空格 留在另外一条空行。
    • 如果前一行不是代码,那么这一行也不应该是,所以我们可以应付StackOverflow的 在代码块之外使用缩进进行文本布局。

    首先,我们不知道(dunno)这一行是代码还是文本:

    dunnoNow :: [String] -> [String]
    dunnoNow [] = []
    dunnoNow (line:lines)
      | all (==' ') line = line:dunnoNow lines     -- next line could be either
      | otherwise = let (first4,therest) = splitAt 4 line in 
         if first4 /="    "                 -- 
            || null therest                 -- so the next line won't ever crash
            || head therest `elem` "*:"     -- special chars that don't start lines of code.
         then line:knowNow False lines      -- this isn't code, so the next line isn't either
         else ('>':line):knowNow True lines -- this is code, add > and the next line has to be too
    

    但是如果我们知道,我们应该保持相同的模式,直到我们打出一个空行:

    knowNow :: Bool -> [String] -> [String]
    knowNow _ [] = []
    knowNow itsCode (line:lines) 
      | all (==' ') line = line:dunnoNow lines
      | otherwise = (if itsCode then '>':line else line):knowNow itsCode lines
    

    让ghci使用预处理器

    现在我们可以获取一个模块名称,预处理该文件,并告诉ghci加载它:

    loadso :: String -> IO String
    loadso fn = fmap (unlines.dunnoNow.lines) (readFile $ fn++".so") -- so2bird each line
            >>= writeFile (fn++"_so.lhs")                     -- write to a new file
            >> return (":def! rso (\\_ -> return \":so "++ fn ++"\")\n:load "++fn++"_so.lhs")
    

    我曾经默默地重新定义:rso命令,因为我之前使用的是attemts let currentStackOverflowFile = ....currentStackOverflowFile <- return ... 没让我到任何地方。

    如何使其正常工作

    现在我需要将它放在我的ghci.conf文件中,即在appdata/ghc/ghci.conf中 根据{{​​3}}

    :{
    let dunnoNow [] = []
        dunnoNow (line:lines)
          | all (==' ') line = line:dunnoNow lines     -- next line could be either
          | otherwise = let (first4,therest) = splitAt 4 line in 
             if first4 /="    "                 -- 
                || null therest                 -- so the next line won't ever crash
                || head therest `elem` "*:"     -- special chars that don't start lines of code.
             then line:knowNow False lines      -- this isn't code, so the next line isn't either
             else ('>':line):knowNow True lines -- this is code, add > and the next line has to be too
        knowNow _ [] = []
        knowNow itsCode (line:lines) 
          | all (==' ') line = line:dunnoNow lines
          | otherwise = (if itsCode then '>':line else line):knowNow itsCode lines
        loadso fn = fmap (unlines.dunnoNow.lines) (readFile $ fn++".so") -- convert each line
            >>= writeFile (fn++"_so.lhs")                            -- write to a new file
            >> return (":def! rso (\\_ -> return \":so "++ fn ++"\")\n:load "++fn++"_so.lhs")
    :}
    :def so loadso
    

    用法

    现在我可以将这整篇文章保存在LiterateSo.so中并在ghci中做一些可爱的事情,比如

    *Prelude> :so StackOverflow
    [1 of 1] Compiling StackOverflow    ( StackOverflow_so.lhs, interpreted )
    Ok, modules loaded: StackOverflow.
    
    *StackOverflow> :rso
    [1 of 1] Compiling StackOverflow    ( StackOverflow_so.lhs, interpreted )
    Ok, modules loaded: StackOverflow.
    
    *StackOverflow>
    

    万岁!

    我想要的是什么:

    我更愿意让ghci更直接地支持它。摆脱中间.lhs文件会很好。

    此外,似乎ghci从确定的:load的最短子串开始执行文件名完成 你实际上在做load,所以使用:lso代替:so不会欺骗它。

    (我喜欢在C中重写我的代码。我也喜欢从源代码重新编译ghci。)

    TLDR问题提醒:

    1. 我可以在我的:so中定义的ghci.conf命令中添加ghci来添加文件名完成吗?
    2. 我可以以某种方式定义一个ghci命令,它返回编译代码而不是返回一个ghci命令,或者 ghci是否有更好的方法让我插入Haskell代码作为 特定于文件扩展名的预处理器,因此:l像往常一样适用于.hs.lhs个文件,但是使用我的手写预处理器来处理.so个文件?

1 个答案:

答案 0 :(得分:9)

我会尝试制作一个运行SO预处理代码或标准文学预处理器的独立预处理器,具体取决于文件扩展名。然后在:set -pgmL SO-preprocessor中使用ghci.conf

对于标准文学预处理器,请运行unlit程序,或使用Distribution.Simple.PreProcess.Unlit

这样,:load和文件名完成才能正常工作。

GHCI按顺序将4个参数传递给预处理器:-h,标签,源文件名和目标文件名。预处理器应该读取源并写入目标。标签用于输出#line pragma。如果不改变源的行数,则可以忽略它(即用--注释或空行替换“注释”行。