34

Haskellスタックオーバーフローレイアウトプリプロセッサ

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

これを最初に(1/2の方法で)プレイしたい場合は、「何をすべきか」にスキップして機能させます。私が少し気を悪くして、あなたが私が探している助けが何であるかを知りたいだけなら、私が欲しいものに
スキップしてください。

TLDR質問の要約:

  1. ghciにファイル名補完を:so自分で定義したコマンドに追加させることはできますghci.confか?
  2. どういうわけか、ghciコマンドを返す代わりにコンパイル用のコードを返すghciコマンドを定義できますか、またはghciは、ファイル拡張子固有のプリプロセッサとしてHaskellコードをプラグインするためのより良い方法を持っている:lので.hs.lhsいつものようにファイルしますが、.soファイルに私の手書きのプリプロセッサを使用しますか?

バックグラウンド:

.lhsHaskellは、2つの方法でソースファイルの文芸的プログラミングをサポートしています。

  • LaTeXスタイル\begin{code}\end{code}
  • バードトラック:コードはで始まり> 、それ以外はコメントです。
    コードとコメントの間には空白行が必要です(些細な偶発的な誤用を防ぐため>)。

Birdは、ルールをStackOverflowのコードブロックと同じように追跡しませんか?

参考資料: 1。.ghci マニュアル 2. GHCi haskellwiki3..ghci に関するNeilMitchell ブログ:{:}

プリプロセッサ

私はテキストエディタでSOの回答を書くのが好きで、機能するコードで構成される投稿を作成するのが好きですが、>投稿する前に編集しなければならないコメントブロックやsができてしまうので、面白くありません。

それで、私は自分自身にプリプロセッサを書きました。

  • いくつかのghciのものをコードブロックとして貼り付けた場合、通常は*またはで始まり:ます。
  • 行が完全に空白の場合、コードとして扱われることは望ましくありません。そうしないと、空白行に誤って残した4つのスペースが表示されないため、誤ってコメント行の次のコードエラーが発生します。
  • 前の行がコードではなかった場合、この行もコードではないはずです。そのため、コードブロックの外側でテキストレイアウトの目的でStackOverflowがインデントを使用することに対処できます。

最初は、この行がコードなのかテキストなのかわかりません(わかりません)。

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コマンドを 黙って再定義しました。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

使用法

今、私はこの投稿全体を保存して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. ghciにファイル名補完を:so自分で定義したコマンドに追加させることはできますghci.confか?
  2. どういうわけか、ghciコマンドを返す代わりにコンパイル用のコードを返すghciコマンドを定義できますか、またはghciは、ファイル拡張子固有のプリプロセッサとしてHaskellコードをプラグインするためのより良い方法を持っている:lので.hs.lhsいつものようにファイルしますが、.soファイルに私の手書きのプリプロセッサを使用しますか?
4

1 に答える 1

9

ファイル拡張子に応じて、SO前処理コードまたは標準のリテラシープリプロセッサを実行するスタンドアロンプ​​リプロセッサを作成しようとします。次に、で使用:set -pgmL SO-preprocessorghci.confます。

標準の文学的プリプロセッサの場合は、unlitプログラムを実行するか、を使用しますDistribution.Simple.PreProcess.Unlit

このように:load、ファイル名の補完は正常に機能します。

GHCIは-h、ラベル、ソースファイル名、宛先ファイル名の順に4つの引数をプリプロセッサに渡します。プリプロセッサは、ソースを読み取り、宛先に書き込む必要があります。ラベルは、#lineプラグマを出力するために使用されます。ソースの行数を変更しない場合(つまり、「コメント」行を--コメントまたは空白行に置き換える場合)は無視できます。

于 2012-10-01T10:20:23.500 に答える