我教过ghci编译我的StackOverflow帖子。我可以让它变得更冷静吗? [英] I taught ghci to compile my StackOverflow posts. Can I make it slicker?

查看:110
本文介绍了我教过ghci编译我的StackOverflow帖子。我可以让它变得更冷静吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Haskell堆栈溢出布局预处理器



 模块StackOverflow其中 - 是的,这篇文章的源代码编译为

如果您想玩这个游戏,请跳至如何运作首先(1/2下降)。

如果我有点wit and,而您只想找出我正在寻找的帮助,则跳至我想要的

TLDR问题摘要:




  1. 我可以通过ghci添加文件名完成在我的 ghci.conf 中定义的:so 命令?

  2. 我可以以某种方式定义一个ghci命令,该命令返回编译代码而不是返回ghci命令,或者
    没有ghci,而是有更好的方式让我插入Haskell代码一个
    文件扩展名特定的预处理器,所以:l 可用于 .hs .lhs 文件,但是使用我的手写预处理器来处理 .so 文件?



背景:



Haskell支持 .lhs 源文件,有两种方式:


  • LaTeX样式 \ begin {code} \end {code}

  • 小鸟曲目:代码以> ,其他都是注释。

    代码和注释之间必须有一个空行(以防止意外滥用> ) 。


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

参考文献: 1。 .ghci手册
2。 GHCi haskellwiki
3。 Neil Mitchell博客关于:{:} in .ghci



预处理程序



我喜欢在文本编辑器中编写SO答案,我喜欢发布一篇文章,其中包含
的代码,但最终会有评论块或> s,我必须在发布前编辑它们,是不那么有趣的。



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


  • 如果我以代码块的形式粘贴了一些ghci文件,它通常以 * 开头。

  • 如果该行完全空白,我不希望它被视为代码,因为否则
    我得到意外的代码 - 下一个注释行错误,因为我可以我没有看到4空格,我不小心
    留在一个空白的行。

  • 如果前面的行不是代码,这行不应该是,所以我们可以应付使用StackOverflow的
    将缩进用于代码块之外的文本布局。



起初我们不知道(不知道)这行是代码还是文本:

  dunnoNow :: [String]  - > [String] 
dunnoNow [] = []
dunnoNow(line:lines)
|全部(=='')line = line:dunnoNow行 - 下一行可以是
|否则= let(first4,therest)= splitAt 4行
if first4 / = -
|| null therest - 因此下一行不会崩溃
||头部那里`elem`*: - 特殊字符不会启动代码行。
然后是line:knowNow虚线 - 这不是代码,所以下一行不是
else('>':line):knowNow真行 - 这是代码,add >下一行必须是

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

  knowNow :: Bool  - > [字符串]  - > [String] 
knowNow _ [] = []
knowNow itsCode(line:lines)
|全部(=='')line = line:dunnoNow行
|否则=(如果itsCode then'>':line else line):knowNow itsCode lines



获取ghci使用预处理器



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

  loadso :: String  - > IO字符串
loadso fn = fmap(unlines.dunnoNow.lines)(readFile $ fn ++。so) - so2bird每行
>> = writeFile(fn ++_ so.lhs) - - 写入新文件
>> return(:def!rso(\\__ && return \:so++ fn ++\)\\\
:load++ fn ++_ so.lhs)

我默默地重新定义了:rso 命令因为我以前的尝试使用
let currentStackOverflowFile = .... currentStackOverflowFile < - return ...

怎样才能让它工作



现在我需要把它放在我的 ghci.conf 文件中,也就是 appdata / ghc / ghci.conf
根据说明

 :{
let dunnoNow [] = []
dunnoNow(line:lines)
|全部(=='')line = line:dunnoNow行 - 下一行可以是
|否则= let(first4,therest)= splitAt 4行
if first4 / = -
|| null therest - 因此下一行不会崩溃
||头部那里`elem`*: - 特殊字符不会启动代码行。
然后是line:knowNow虚线 - 这不是代码,所以下一行不是
else('>':line):knowNow真行 - 这是代码,add >并且下一行也必须是
knowNow _ [] = []
knowNow itsCode(line:lines)
|全部(=='')line = line:dunnoNow行
|否则=(if itsCode then'>':line else line):knowNow itsCode lines
loadso fn = fmap(unlines.dunnoNow.lines)(readFile $ fn ++。so) - 转换每行
>> = writeFile(fn ++_ so.lhs) - 写入新文件
>> return(:def!rso(\\__ && return \:so++ fn ++\)\\\
:load++ fn ++_ so.lhs)
:}
:def so loadso



用法



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

  *前奏> :所以StackOverflow 
[1的1]编译StackOverflow(StackOverflow_so.lhs,解释)
好​​了,模块加载:StackOverflow。

* StackOverflow> :rso
[1的1]编译StackOverflow(StackOverflow_so.lhs,解释)
好​​了,模块加载:StackOverflow。

* StackOverflow>

万岁!



我想要的是什么:



我希望启用ghci以更直接地支持这个。这将是很好的摆脱中间 .lhs 文件。



此外,似乎ghci文件名完成从:load 的最短子字符串开始,它决定了
实际上是在执行 load ,所以使用:lso 而不是:所以不会欺骗它。



< (我不会喜欢用C重写我的代码。我也不愿意重新编译源代码中的ghci。)



TLDR问题提醒:




  1. 我可以通过ghci将文件名完成添加到:so 命令我在 ghci.conf中定义

  2. 我可以以某种方式定义一个ghci命令,该命令返回编译代码而不是返回ghci命令,或者
    没有ghci,而是有更好的方式让我插入Haskell代码一个
    文件扩展名特定的预处理器,所以:l 可用于 .hs .lhs 文件,但是使用我的手写预处理器来处理 .so 文件?


解决方案

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



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

通过这种方式,:load 和文件名完成工作正常。



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


Haskell Stack Overflow layout preprocessor

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

Skip down to What to do to get it working if you want to play with this first (1/2 way down).
Skip down to What I would like if I witter on a bit and you just want to find out what help I'm seeking.

TLDR Question summary:

  1. Can I get ghci to add filename completion to the :so command I defined in my ghci.conf?
  2. Could I somehow define a ghci command that returns code for compilation instead of returning a ghci command, or does ghci instead have a better way for me to plug in Haskell code as a file-extension-specific pre-processor, so :l would work for .hs and .lhs files as usual, but use my handwritten preprocessor for .so files?

Background:

Haskell supports literate programming in .lhs source files, two ways:

  • LaTeX style \begin{code} and \end{code}.
  • Bird tracks: Code starts with >, anything else is a comment.
    There must be a blank line between code and comments (to stop trivial accidental misuse of >).

Don't Bird tracks rules sound similar to StackOverflow's code blocks?

References: 1. The .ghci manual 2. GHCi haskellwiki 3. Neil Mitchell blogs about :{ and :} in .ghci

The preprocessor

I like writing SO answers in a text editor, and I like to make a post that consists of code that works, but end up with comment blocks or >s that I have to edit out before posting, which is less fun.

So, I wrote myself a pre-processor.

  • If I've pasted some ghci stuff in as a code block, it usually starts with * or :.
  • If the line is completely blank, I don't want it treated as code, because otherwise I get accidental code-next-to-comment-line errors because I can't see the 4 spaces I accidentally left on an otherwise blank line.
  • If the preceeding line was not code, this line shouldn't be either, so we can cope with StackOverflow's use of indentation for text layout purposes outside code blocks.

At first we don't know (dunno) whether this line is code or text:

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

but if we know, we should keep in the same mode until we hit a blank line:

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

Getting ghci to use the preprocessor

Now we can take a module name, preprocess that file, and tell ghci to load it:

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")

I've used silently redefining the :rso command becuase my previous attemts to use let currentStackOverflowFile = .... or currentStackOverflowFile <- return ... didn't get me anywhere.

What to do to get it working

Now I need to put it in my ghci.conf file, i.e. in appdata/ghc/ghci.conf as per the instructions

:{
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

Usage

Now I can save this entire post in LiterateSo.so and do lovely things in ghci like

*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>

Hooray!

What I would like:

I would prefer to enable ghci to support this more directly. It would be nice to get rid of the intermediate .lhs file.

Also, it seems ghci does filename completion starting at the shortest substring of :load that determines you're actually doing load, so using :lso instead of :so doesn't fool it.

(I would not like to rewrite my code in C. I also would not like to recompile ghci from source.)

TLDR Question reminder:

  1. Can I get ghci to add filename completion to the :so command I defined in my ghci.conf?
  2. Could I somehow define a ghci command that returns code for compilation instead of returning a ghci command, or does ghci instead have a better way for me to plug in Haskell code as a file-extension-specific pre-processor, so :l would work for .hs and .lhs files as usual, but use my handwritten preprocessor for .so files?

解决方案

I would try to make a standalone preprocessor that runs SO preprocessing code or the standard literary preprocessor, depending on file extension. Then just use :set -pgmL SO-preprocessor in ghci.conf.

For the standard literary preprocessor, run the unlit program, or use Distribution.Simple.PreProcess.Unlit.

This way, :load and filename completion just work normally.

GHCI passes 4 arguments to the preprocessor, in order: -h, the label, the source file name, and the destination file name. The preprocessor should read the source and write to the destination. The label is used to output #line pragmas. You can ignore it if you don't alter the line count of the source (i.e. replace "comment" lines with -- comments or blank lines).

这篇关于我教过ghci编译我的StackOverflow帖子。我可以让它变得更冷静吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆