我教过ghci编译我的StackOverflow帖子。我可以让它变得更冷静吗? [英] I taught ghci to compile my StackOverflow posts. Can I make it slicker?
问题描述
Haskell堆栈溢出布局预处理器
模块StackOverflow其中 - 是的,这篇文章的源代码编译为
如果您想玩这个游戏,请跳至如何运作首先(1/2下降)。
如果我有点wit and,而您只想找出我正在寻找的帮助,则跳至我想要的。
TLDR问题摘要:
- 我可以通过ghci添加文件名完成在我的
ghci.conf
中定义的:so
命令? - 我可以以某种方式定义一个ghci命令,该命令返回编译代码而不是返回ghci命令,或者
没有ghci,而是有更好的方式让我插入Haskell代码一个
文件扩展名特定的预处理器,所以:l
可用于.hs
和.lhs
文件,但是使用我的手写预处理器来处理.so
文件?
背景:
Haskell支持 .lhs
源文件,有两种方式:
\ begin {code}
和 \end {code}
。
> $ c开头$ c>,其他都是注释。
代码和注释之间必须有一个空行(以防止意外滥用>
) 。
不要让Bird跟踪规则声音与StackOverflow的代码块类似吗?
参考文献: 1。 .ghci手册
2。 GHCi haskellwiki
3。 Neil Mitchell博客关于:{
和:}
in .ghci
预处理程序
我喜欢在文本编辑器中编写SO答案,我喜欢发布一篇文章,其中包含
的代码,但最终会有评论块或>
s,我必须在发布前编辑它们,是不那么有趣的。
所以,我自己写了一个预处理器。
*
或:
开头。
我得到意外的代码 - 下一个注释行错误,因为我可以我没有看到4空格,我不小心
留在一个空白的行。
将缩进用于代码块之外的文本布局。
起初我们不知道(不知道)这行是代码还是文本:
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)
我默默地重新定义了 现在我需要把它放在我的 现在我可以将整篇文章保存在 万岁! 我希望启用ghci以更直接地支持这个。这将是很好的摆脱中间 此外,似乎ghci文件名完成从:rso
命令因为我以前的尝试使用
let currentStackOverflowFile = ....
或 currentStackOverflowFile < - return ... $ c $
怎样才能让它工作
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>
我想要的是什么:
.lhs
文件。
:load
的最短子字符串开始,它决定了
实际上是在执行 load
,所以使用:lso
而不是:所以
不会欺骗它。
< (我不会喜欢用C重写我的代码。我也不愿意重新编译源代码中的ghci。)
TLDR问题提醒:
- 我可以通过ghci将文件名完成添加到
:so
命令我在ghci.conf中定义
? - 我可以以某种方式定义一个ghci命令,该命令返回编译代码而不是返回ghci命令,或者
没有ghci,而是有更好的方式让我插入Haskell代码一个
文件扩展名特定的预处理器,所以:l
可用于.hs
和.lhs
文件,但是使用我的手写预处理器来处理.so
文件?
我会尝试制作一个独立的预处理程序,用于运行SO预处理代码或标准文字预处理程序,具体取决于文件扩展名。然后在 ghci.conf
中使用:set -pgmL SO-preprocessor
。
对于标准文字预处理器,运行 通过这种方式, GHCI将4个参数传递给预处理器,依次为: unlit
程序,或使用 Distribution.Simple.PreProcess.Unlit $ c $
:load
和文件名完成工作正常。
-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:
- Can I get ghci to add filename completion to the
:so
command I defined in myghci.conf
? - 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:
- Can I get ghci to add filename completion to the
:so
command I defined in myghci.conf
? - 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屋!