Препроцессор макета Haskell
module StackOverflow where -- yes, the source of this post compiles as is
Перейти к Что делать, чтобы заставить его работать, если вы хотите играть с этим первым (1/2 пути вниз).
Пропустите вниз до Что бы я хотел, если немного помахать, и вы просто хотите узнать, какую помощь я ищу.
TL;DR Вопрос резюме:
- Могу ли я получить ghci для добавления завершения имени файла в команду
:so
, которую я определил в своемghci.conf
? - Могу ли я каким-то образом определить команду ghci, которая возвращает код для компиляции вместо того, чтобы возвращать команду ghci, или
у ghci вместо этого есть лучший способ подключить код Haskell в качестве
предварительный процессор с расширением файла, поэтому
:l
будет работать как обычно, для.hs
и.lhs
файлов, но использовать мой рукописный препроцессор для.so
файлов?
История:
Haskell поддерживает грамотное программирование в исходных файлах .lhs
двумя способами:
- Стиль LaTeX
\begin{code}
и\end{code}
. - Птицы: код начинается с
>
, все остальное - комментарий.
Должна быть пустая строка между кодом и комментариями (чтобы остановить тривиальное случайное неправильное использование>
).
Не звучат ли правила Bird Bird, похожие на блоки кода StackOverflow?
Ссылки: 1. Руководство .ghci
2. GHCi haskellwiki
3. Нейл Митчелл блоги о :{
и :}
в .ghci
Препроцессор
Мне нравится писать ответы SO в текстовом редакторе, и мне нравится делать сообщение, состоящее из кода, который работает,
но в итоге есть блоки комментариев или >
, которые мне нужно отредактировать перед публикацией, что менее интересно.
Итак, я написал себе предварительный процессор.
- Если я вставлял некоторый материал 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 из исходного кода.)
Ответ на вопросник TL;DR:
- Могу ли я получить ghci для добавления завершения имени файла в команду
:so
, которую я определил в своемghci.conf
? - Могу ли я каким-то образом определить команду ghci, которая возвращает код для компиляции вместо того, чтобы возвращать команду ghci, или
у ghci вместо этого есть лучший способ подключить код Haskell в качестве
предварительный процессор с расширением файла, поэтому
:l
будет работать как обычно, для.hs
и.lhs
файлов, но использовать мой рукописный препроцессор для.so
файлов?