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

And*_*ewC 34 haskell ghci

Haskell Stack Overflow布局预处理器

module StackOverflow where  -- yes, the source of this post compiles as is
Run Code Online (Sandbox Code Playgroud)

如果您想先玩这个(1/2向下),请跳到下面的操作以使其正常工作.如果我稍微
停下来,你会跳到我想要的东西,你只想找到我正在寻求的帮助.

TLDR问题摘要:

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

背景:

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

  • 乳胶风格\begin{code}\end{code}.
  • Bird track:Code开头>,其他任何东西都是评论.
    代码和注释之间必须有一个空行(以防止意外误操作>).

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

参考文献: 1. 将.ghci手册 2. GHCI haskellwiki 3. 关于尼尔·米切尔的博客:{,并:}在.ghci

预处理器

我喜欢在文本编辑器中编写SO答案,我喜欢发表一个包含有效代码的帖子,但最后还是>在发布之前我必须编辑的注释块或者s,这不那么有趣.

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

  • 如果我在代码块中粘贴了一些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
Run Code Online (Sandbox Code Playgroud)

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

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

让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")
Run Code Online (Sandbox Code Playgroud)

我曾经默默地重新定义了这个:rso命令,因为我之前使用过的let currentStackOverflowFile = ....或者currentStackOverflowFile <- return ... 没有把我带到 任何地方.

怎么做才能让它发挥作用

现在我需要将它放在我的ghci.conf文件中,即appdata/ghc/ghci.conf 根据说明

:{
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
Run Code Online (Sandbox Code Playgroud)

用法

现在我可以保存这整篇文章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>
Run Code Online (Sandbox Code Playgroud)

万岁!

我想要的是什么:

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

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

(我喜欢重写我在C.代码我也没有想从源代码重新编译ghci的.)

TLDR问题提醒:

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

n. *_* m. 9

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

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

这样,:load文件名完成就可以正常工作了.

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