Я научил ghci составлять мои сообщения Stackru. Могу ли я сделать это более гладким?

Препроцессор макета Haskell Stack Overflow

module Stackru 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 файлы?

Фон:

Haskell поддерживает грамотное программирование в .lhs Исходные файлы двумя способами:

  • Латекс стиль \begin{code} а также \end{code},
  • Следы птиц: код начинается с > Все остальное является комментарием.
    Между кодом и комментариями должна быть пустая строка (чтобы избежать случайного случайного неправильного использования >).

Разве правила отслеживания птиц не похожи на блоки кода Stackru?

Ссылки: 1. Руководство.ghci 2. GHCi haskellwiki 3. Блог Нила Митчелла о :{ а также :} в.ghci

Препроцессор

Мне нравится писать SO ответы в текстовом редакторе, и мне нравится делать пост, который состоит из кода, который работает, но в конечном итоге с блоками комментариев или >s, что я должен отредактировать перед публикацией, что не так весело.

Итак, я написал себе препроцессор.

  • Если я вставил некоторые элементы ghci как блок кода, он обычно начинается с * или же :,
  • Если строка совершенно пустая, я не хочу, чтобы она воспринималась как код, потому что в противном случае я получаю случайные ошибки code-next-to-comment-line, потому что не вижу 4 пробела, которые я случайно оставил в пустой строке.
  • Если в предыдущей строке не было кода, эта строка также не должна быть, поэтому мы можем справиться с использованием отступа в Stackru для разметки текста вне блоков кода.

Сначала мы не знаем (не знаю), является ли эта строка кодом или текстом:

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 currentStackruFile = .... или же currentStackruFile <- 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 Stackru
[1 of 1] Compiling Stackru    ( Stackru_so.lhs, interpreted )
Ok, modules loaded: Stackru.

*Stackru> :rso
[1 of 1] Compiling Stackru    ( Stackru_so.lhs, interpreted )
Ok, modules loaded: Stackru.

*Stackru>

Ура!

Что бы я хотел:

Я бы предпочел включить ghci для более прямой поддержки. Было бы неплохо избавиться от промежуточного .lhs файл.

Кроме того, кажется, что ghci выполняет завершение имени файла, начиная с самой короткой подстроки :load который определяет, что вы на самом деле делаете load так что используя :lso вместо :so не обманешь

не хотел бы переписывать свой код на C. Я также не хотел бы перекомпилировать ghci из исходного кода.)

TLDR Вопрос напоминание:

  1. Могу ли я получить ghci, чтобы добавить завершение имени файла в :so команда, которую я определил в моем ghci.conf?
  2. Могу ли я как-то определить команду ghci, которая возвращает код для компиляции вместо возврата команды ghci, или же вместо этого у ghci есть лучший способ подключить код на Haskell в качестве препроцессора, специфичного для расширения файла, так что :l будет работать для .hs а также .lhs файлы, как обычно, но используйте мой рукописный препроцессор для .so файлы?

1 ответ

Решение

Я бы попытался создать автономный препроцессор, который запускает код предварительной обработки SO или стандартный литературный препроцессор, в зависимости от расширения файла. Тогда просто используйте :set -pgmL SO-preprocessor в ghci.conf,

Для стандартного литературного препроцессора запустите unlit запрограммировать или использовать Distribution.Simple.PreProcess.Unlit,

Сюда, :load и завершение имени файла просто работает нормально.

GHCI передает препроцессору 4 аргумента в следующем порядке: -h, метка, имя исходного файла и имя файла назначения. Препроцессор должен прочитать источник и записать в место назначения. Метка используется для вывода #line псевдокомментарии. Вы можете проигнорировать это, если не измените количество строк в источнике (т.е. замените строки "комментария" на -- комментарии или пустые строки).

Другие вопросы по тегам