Le enseñé a ghci a compilar mis publicaciones de StackOverflow. ¿Puedo hacerlo más resbaladizo?


Haskell Stack Overflow layout preprocesador

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

, pase a ¿Qué hacer para que funcione si quieres jugar con este primer (1/2 hacia abajo).
Saltar hacia abajo a Lo que me gustaría si witter en un poco y solo quiere averiguar qué ayuda estoy buscando.

Resumen de la pregunta de TLDR:

  1. ¿Puedo hacer que ghci agregue la terminación del nombre de archivo al comando :so que definí en mi ghci.conf?
  2. Podría de alguna manera definir un comando ghci que devuelve código para compilación en lugar de devolver un comando ghci, o en cambio, ghci tiene una mejor manera para mí de conectar el código Haskell como un preprocesador específico de extensión de archivo, por lo que :l funcionaría para los archivos .hs y .lhs como de costumbre, pero ¿usar mi preprocesador escrito a mano para los archivos .so?

Antecedentes:

Haskell soporta la programación alfabetizada en .lhs archivos fuente, de dos maneras:

  • estilo LaTeX \begin{code} y \end{code}.
  • Huellas de aves: Código comienza con >, cualquier otra cosa es un comentario.
    Debe haber una línea en blanco entre el código y los comentarios (para detener el mal uso accidental trivial de >).

¿Las reglas de Bird tracks no suenan similares a los bloques de código de StackOverflow?

Referencias: 1. El .manual de ghci 2. GHCi haskellwiki 3. Neil Mitchell blogs sobre :{ y :} en .ghci

El preprocesador

Me gusta escribir ASÍ responde en un editor de texto, y me gusta hacer un post que consiste en código que funciona, pero terminan con bloques de comentarios o >que tengo que editar antes de publicar, lo cual es menos divertido.

Así que, me escribí un pre-procesador.

  • Si he pegado algunas cosas ghci como un bloque de código, generalmente comienza con * o :.
  • Si la línea está completamente en blanco, no quiero que se trate como código, porque de lo contrario Me pongo accidental errores de código al lado de la línea de comentarios porque no puedo ver los 4 espacios que accidentalmente izquierda en una línea de lo contrario en blanco.
  • Si la línea que precede no era código, esta línea tampoco debería serlo, por lo que podemos hacer frente a Stackoverflow's uso de sangría para propósitos de diseño de texto fuera de los bloques de código.

Al principio no sabemos (no sé) si esta línea es código o texto:

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

Pero si lo sabemos, debemos mantenernos en el mismo modo hasta que encontremos un espacio en blanco línea:

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

Conseguir que ghci use el preprocesador

Ahora podemos tomar un nombre de módulo, preprocesar ese archivo, y decirle a ghci que lo cargue:

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

He usado silenciosamente redefinir el comando :rso porque mis intentos anteriores para usar let currentStackOverflowFile = .... o currentStackOverflowFile <- return ... no me llevó a ninguna parte.

¿Qué hacer para que funcione

Ahora necesito ponerlo en mi ghci.conf archivo, es decir, en appdata/ghc/ghci.conf según las instrucciones

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

Uso

Ahora puedo guardar todo este post en LiterateSo.so y hacer cosas hermosas en ghci como

*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>
[41] ¡Hurra!

Lo que me gustaría:

Preferiría habilitar a ghci para apoyar esto más directamente. Sería bueno deshacerse del archivo intermedio .lhs.

Además, parece que ghci completa el nombre de archivo comenzando en la subcadena más corta de :load que determina en realidad estás haciendo load, por lo que usar :lso en lugar de :so no lo engaña.

(me gustaría nome gustaría reescribir mi código en C. También me gustaría no recompilar ghci desde el código fuente.)

Recordatorio de pregunta de TLDR:

  1. ¿Puedo hacer que ghci agregue la terminación del nombre de archivo al comando :so que definí en mi ghci.conf?
  2. Podría de alguna manera definir un comando ghci que devuelve código para compilación en lugar de devolver un comando ghci, o en cambio, ghci tiene una mejor manera para mí de conectar el código Haskell como un preprocesador específico de extensión de archivo, entonces :l funcionaría para los archivos .hs y .lhs como de costumbre, pero ¿usar mi preprocesador escrito a mano para los archivos .so?
Author: AndrewC, 2012-10-01

1 answers

Intentaría hacer un preprocesador independiente que ejecute el código de preprocesamiento o el preprocesador literario estándar, dependiendo de la extensión del archivo. Entonces simplemente use :set -pgmL SO-preprocessor en ghci.conf.

Para el preprocesador literario estándar, ejecute el programa unlit, o use Distribution.Simple.PreProcess.Unlit.

De esta manera, :load y la finalización del nombre de archivo simplemente funcionan normalmente.

GHCI pasa 4 argumentos al preprocesador, en orden: -h, la etiqueta, el nombre del archivo de origen y el nombre del archivo de destino. El preprocesador debe leer la fuente y escribir en el destino. La etiqueta se usa para generar #line pragmas. Puede ignorarlo si no altera el recuento de líneas de la fuente (es decir, reemplaza las líneas de "comentario" con -- comentarios o líneas en blanco).

 9
Author: n.m.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-01 10:20:23