Esta sección describe otros programas que distribuimos, que ayudan con la gran tarea de programación de Haskell.

12.1. “Yacc para Haskell”: happy

Andy Gill y Simon Marlow han escrito un analizador-generador para Haskell, llamado happy. Happy es para Haskell lo que Yacc es para C.

Puedes obtener happy de la página de inicio feliz.

Happy brilla mejor cuando lo compila GHC.

12.2. Escribir interfaces de Haskell en código C: hsc2hs

los hsc2hs El comando se puede usar para automatizar algunas partes del proceso de escritura de enlaces Haskell en código C. Lee una fuente casi Haskell con construcciones especiales incrustadas y genera un archivo Haskell real con estas construcciones procesadas, basado en información tomada de algunos encabezados C. Las construcciones adicionales se ocupan del acceso a datos C de Haskell.

También puede generar un archivo C que contiene funciones C adicionales para vincularlas al programa, junto con un encabezado C que se incluye en el código C en el que se compilará el módulo Haskell (cuando se compila a través de C) y en el archivo C . Estos dos archivos se crean cuando el #def se utiliza la construcción (ver más abajo).

Realmente hsc2hs no genera el archivo Haskell directamente. Crea un programa en C que incluye los encabezados, se compila y ejecuta automáticamente. Ese programa genera el código Haskell.

A continuación, el “archivo Haskell” es el resultado principal (normalmente un .hs archivo), “archivo Haskell compilado” es el archivo Haskell después ghc lo ha compilado en C (es decir, un .hc archivo), “programa C” es el programa que genera el archivo Haskell, “archivo C” es el archivo C generado opcionalmente y “encabezado C” es su archivo de encabezado.

12.2.1. sintaxis de la línea de comandos

hsc2hs toma archivos de entrada como argumentos y banderas que modifican su comportamiento:

-o FILE, --output=FILE
Nombre del archivo Haskell.
-t FILE, --template=FILE
El archivo de plantilla (ver más abajo).
-c PROG, --cc=PROG
El compilador de C a usar (predeterminado: gcc)
-l PROG, --ld=PROG
El vinculador que se utilizará (predeterminado: gcc).
-C FLAG, --cflag=FLAG
Una bandera adicional para pasar al compilador de C.
-I DIR
Pasado al compilador de C.
-L FLAG, --lflag=FLAG
Una bandera adicional para pasar al enlazador.
-i FILE, --include=FILE
Como si el apropiado #include La directiva se colocó en la fuente.
-D NAME[=VALUE], --define=NAME[=VALUE]
Como si el apropiado #define La directiva se colocó en la fuente.
--no-compile
Deténgase después de escribir el programa C intermedio en el disco. El nombre del archivo para el programa C intermedio es el nombre del archivo de entrada con .hsc reemplazadas con _hsc_make.c.
-k, --keep-files
Proceda de la forma habitual, pero no elimine ningún archivo intermedio.
-x, --cross-compile
Activar el modo de compilación cruzada (ver Compilación cruzada).
--cross-safe
Restringir las directivas .hsc a aquellas admitidas por el --cross-compile modo (ver Compilación cruzada). Esto debería ser útil si su .hsc Los archivos deben compilarse de forma cruzada de forma segura y desea evitar que las construcciones no compilables de forma cruzada se introduzcan en ellos.
-?, --help
Muestre un resumen de las banderas disponibles y salga con éxito.
-V, --version
Genere información de la versión y salga correctamente.

El archivo de entrada debe terminar con .hsc (solo debe ser una fuente simple de Haskell; no se admite Haskell alfabetizado en este momento). Los archivos de salida por defecto obtienen nombres con la .hsc sufijo reemplazado:

.hs Archivo Haskell
_hsc.h Encabezado C
_hsc.c Archivo C

El programa C se compila utilizando el compilador Haskell. Esto proporciona la ruta de inclusión para HsFFI.h que se incluye automáticamente en el programa C.

12.2.2. Sintaxis de entrada

Todo el procesamiento especial es activado por el # operador. Para generar un literal #, escríbelo dos veces: ##. Dentro string literales y comentarios # los caracteres no se procesan.

A # va seguida de espacios y tabulaciones opcionales, una palabra clave alfanumérica que describe el tipo de procesamiento y sus argumentos. Los argumentos tienen el aspecto de expresiones C separadas por comas (no están escritas entre paréntesis). Se extienden hasta el inigualable más cercano ), ] o }, o hasta el final de la línea si ocurre fuera de cualquier () [] '' "" /**/ y no está precedido por una barra invertida. Los pares de barra invertida-nueva línea se eliminan.

Además #stuff es equivalente a #stuff excepto que está delimitado por sí mismo y, por lo tanto, no necesita colocarse al final de la línea o entre paréntesis.

Significados de palabras clave específicas:

#include , #include "file.h"
El archivo especificado se incluye en el programa C, el archivo Haskell compilado y el encabezado C. se incluye automáticamente.
#define ⟨name⟩, #define ⟨name ⟨value⟩, #undef ⟨name⟩
Similar a #include. Tenga en cuenta que #includes y #defines pueden colocarse en el mismo archivo dos veces, por lo que no deben asumir lo contrario.
#let ⟨name⟩ ⟨parameters⟩ = "⟨definition⟩"
Define un macro que se aplicará a la fuente Haskell. Los nombres de los parámetros están separados por comas, no entre paréntesis. Tal macro se invoca como otro #-construye, comenzando con #name. La definición se pondrá en el programa C dentro de parens como argumentos de printf. Para hacer referencia a un parámetro, cierre la cotización, ponga un nombre de parámetro y abra la cotización nuevamente, para dejar que C string los literales se concatenan. O usar printfdirectivas de formato. Los valores de los argumentos se deben dar como cadenas, a menos que el macro los encadena él mismo usando el preprocesador de C #parameter sintaxis.
#def ⟨C_definition⟩
La definición (de una función, variable, estructura o typedef) se escribe en el archivo C y su prototipo o declaración externa en el encabezado C. Las funciones en línea se manejan correctamente. Las definiciones de estructura y las definiciones de tipo también se escriben en el programa C. los inline, struct o typedef la palabra clave debe venir justo después def.
#if ⟨condition⟩, #ifdef ⟨name⟩, #ifndef ⟨name⟩, #elif ⟨condition⟩, #else, #endif, #error ⟨message⟩, #warning ⟨message⟩
Las directivas de compilación condicional se pasan sin modificar al programa C, al archivo C y al encabezado C. Ponerlos en el programa C significa que se omitirán las partes apropiadas del archivo Haskell.
#const ⟨C_expression⟩
La expresión debe ser convertible a long o unsigned long. Se generará su valor (literal o literal negado).
#const_str ⟨C_expression⟩
La expresión debe ser convertible a puntero char const. Es valioso (string literal) se emitirá.
#type ⟨C_type⟩
Se generará un equivalente Haskell del tipo numérico C. Será uno de Int,Word8,16,32,64, Float, Double, LDouble.
#peek ⟨struct_type⟩, ⟨field⟩
Se generará una función que eche un vistazo a un campo de una estructura C. Tendrá el tipo Storable b => Ptr a -> IO b. La intencion es que #peek y #poke se puede utilizar para implementar las operaciones de clase Storable para una estructura C dada (ver el Foreign.Storable módulo en la documentación de la biblioteca).
#poke ⟨struct_type⟩, ⟨field⟩
Del mismo modo para empujar. Tendrá el tipo Storable b => Ptr a -> b -> IO ().
#ptr ⟨struct_type⟩, ⟨field⟩
Hace un puntero a una estructura de campo. Tendrá el tipo Ptr a -> Ptr b.
#offset ⟨struct_type⟩, ⟨field⟩
Calcula el desplazamiento, en bytes, de field en struct_type. Tendrá tipo Int.
#size ⟨struct_type⟩
Calcula el tamaño, en bytes, de struct_type. Tendrá tipo Int.
#alignment ⟨struct_type⟩
Calcula la alineación, en bytes, de struct_type. Tendrá tipo Int.
#enum ⟨type⟩, ⟨constructor⟩, ⟨value⟩, ⟨value⟩, ...
Un atajo para múltiples definiciones que usan #const. Cada value es un nombre de una constante entera de C, por ejemplo, valor de enumeración. El nombre se traducirá a Haskell haciendo que cada letra siga a un subrayado en mayúscula, haciendo todo el resto en minúsculas y eliminando los subrayados. Puede proporcionar una traducción diferente escribiendo hs_name = c_value en lugar de un value, en ese caso c_value puede ser una expresión arbitraria. los hs_name se definirá como teniendo el especificado type. Su definición es la especificada constructor (que de hecho puede ser una expresión o estar vacío) aplicado al valor entero apropiado. Puedes tener varios #enum definiciones con el mismo type; esta construcción no emite la definición de tipo en sí.

12.2.3. Construcciones personalizadas

#const, #type, #peek, #poke y #ptr no están cableados en el hsc2hs, pero se definen en una plantilla C que se incluye en el programa C: template-hsc.h. También se pueden utilizar construcciones y plantillas personalizadas. Alguna #-construir con desconocido key se espera que sea manejado por una plantilla C.

La plantilla de CA debe definir un macro o función con nombre prefijado por hsc_ que maneja la construcción emitiendo la expansión a stdout. Ver template-hsc.h por ejemplo.

Estas macros también se pueden definir directamente en la fuente. Son útiles para hacer un #let-igual que macro cuya expansión utiliza otros #let macros. Sencillo #let antepone hsc_ al macro nombre y envuelve la definición en un printf llama.

12.2.4. Compilación cruzada

hsc2hs normalmente funciona creando, compilando y ejecutando un programa en C. Ese enfoque no funciona cuando se realiza una compilación cruzada; en este caso, el compilador de C genera código para la máquina de destino, no para la máquina host. Para esta situación, hay un modo especial. hsc2hs --cross-compile que puede generar el .hs extrayendo información solo de las compilaciones, específicamente, si la compilación falla o no.

Solo un subconjunto de .hsc la sintaxis es compatible con --cross-compile. Los siguientes no son compatibles:

  • #const_str
  • #let
  • #def
  • Construcciones personalizadas