15. Другие программы-утилиты Haskell
В этом разделе описываются другие программы, которые мы распространяем и которые помогают в большой задаче программирования на Haskell.
15.1. “Yacc для Haskell”: happy
Энди Гиль и Саймон Марлоу написали генератор парсеров для Haskell под названием happy. Happy для Haskell то же, что Yacc для C.
Вы можете получить happy с домашней страницы Happy.
Happy лучше всего работает, когда компилируется GHC.
15.2. Создание интерфейсов Haskell для кода C: hsc2hs
Команда hsc2hs может использоваться для автоматизации некоторых этапов процесса написания связывающих модулей Haskell с кодом C. Она читает почти-исходный код Haskell со встроенными специальными конструкциями и выводит реальный файл Haskell с обработанными конструкциями на основе информации из заголовков C. Дополнительные конструкции занимаются доступом к данным C из Haskell.
Она также может вывести файл C, который содержит дополнительные функции C для линковки в программу, а также заголовок C, который включается в C-код, к которому будет скомпилирован модуль Haskell (при компиляции через C), и в файл C. Эти два файла создаются при использовании конструкции #def (см. ниже).
На самом деле hsc2hs не выводит файл Haskell напрямую. Она создаёт C-программу, которая включает заголовки, автоматически компилируется и выполняется. Эта программа выводит код Haskell.
В дальнейшем «файл Haskell» — это основной вывод (обычно файл .hs), «скомпилированный файл Haskell» — это файл Haskell после того, как ghc скомпилировал его в C (т.е. файл .hc), «C-программа» — программа, которая выводит файл Haskell, «C-файл» — необязательно сгенерированный C-файл, и «C-заголовок» — его заголовочный файл.
15.2.1. Синтаксис командной строки
hsc2hs принимает файлы ввода в качестве аргументов и флаги, которые изменяют её поведение:
-
-o FILE, --output=FILE -
Имя файла Haskell.
-
-t FILE, --template=FILE -
Шаблонный файл (см. ниже).
-
-c PROG, --cc=PROG -
Компилятор C для использования (по умолчанию:
gcc) -
-l PROG, --ld=PROG -
Используемый линкер (по умолчанию:
gcc). -
-C FLAG, --cflag=FLAG -
Дополнительный флаг для передачи компилятору C.
-
-I DIR -
Передаётся компилятору C.
-
-L FLAG, --lflag=FLAG -
Дополнительный флаг для передачи линковщику.
-
-i FILE, --include=FILE -
Как будто соответствующая директива
#includeбыла размещена в исходном коде. -
-D NAME[=VALUE], --define=NAME[=VALUE] -
Как будто соответствующая директива
#defineбыла размещена в исходном коде. -
--no-compile -
Останавливается после записи промежуточной C-программы на диск. Имя файла для промежуточной C-программы — имя входного файла с
.hscзамещённым на_hsc_make.c. -
-k, --keep-files -
Продолжает работу в обычном режиме, но не удаляет промежуточные файлы.
-
-x, --cross-compile -
Активировать режим кросс-компиляции (см. Кросс-компиляция).
-
--cross-safe -
Ограничить .hsc-директивы теми, которые поддерживает режим
--cross-compile(см. Кросс-компиляция). Это должно быть полезно, если ваши файлы.hscдолжны безопасно кросс-компилироваться, и вы хотите предотвратить попадание в них некросс-компилируемых конструкций. -
-?, --help -
Отобразить краткое описание доступных флагов и успешно завершить выполнение.
-
-V, --version -
Вывести информацию о версии и успешно завершить выполнение.
Входной файл должен заканчиваться на .hsc (он должен содержать только обычный исходный код Haskell; лит-Haskell в данный момент не поддерживается). Выходные файлы по умолчанию получают имена с .hsc суффиксом, заменённым на:
| Файл Haskell |
| C-заголовок |
| C-файл |
C-программа компилируется с помощью компилятора Haskell. Это обеспечивает путь к включению HsFFI.h, который автоматически включается в C-программу.
15.2.2. Синтаксис входных данных
Вся специальная обработка запускается оператором #. Чтобы вывести буквальный #, напишите его дважды: ##. Внутри строковых литералов и комментариев символы # не обрабатываются.
За # следуют необязательные пробелы и табуляции, алфавитно-цифровой ключевой запрос, описывающий вид обработки, и его аргументы. Аргументы похожи на выражения C, разделённые запятыми (они не записываются в скобки). Они простираются до ближайшего несовпадающего ), ] или }, или до конца строки, если она встречается вне любых () [] {} '' "" /**/ и не предшествует обратной косой чертой. Обратная косая черта-новая строка удаляется.
Кроме того, #{stuff} эквивалентно #stuff за исключением того, что оно самоограничивается и, следовательно, не должно размещаться в конце строки или в скобках.
Значения конкретных ключевых слов:
-
#include <file.h>, #include "file.h" -
Указанный файл включается в C-программу, скомпилированный файл Haskell и C-заголовок.
<HsFFI.h>включается автоматически. -
#define ⟨name⟩, #define ⟨name ⟨value⟩, #undef ⟨name⟩ -
Аналогично
#include. Обратите внимание, что#includesи#definesможно поместить в один и тот же файл дважды, поэтому они не должны делать предположений. -
#let ⟨name⟩ ⟨parameters⟩ = "⟨definition⟩" -
Определяет макрос для применения к исходному коду Haskell. Имена параметров разделены запятыми, не заключены в скобки. Такой макрос вызывается как и другие конструкции
#, начиная с#name. Определение будет помещено в C-программу внутри скобок в качестве аргументовprintf. Чтобы сослаться на параметр, закройте кавычку, введите имя параметра и снова откройте кавычку, чтобы позволить C-строковым литералам конкатенироваться. Или используйте директивы форматаprintf. -
#def ⟨C_definition⟩ -
Определение (функции, переменной, структуры или typedef) записывается в C-файл, а его прототип или объявление extern — в C-заголовок. Встроенные функции обрабатываются корректно. определения struct и typedef записываются и в C-программу.
-
#if ⟨condition⟩, #ifdef ⟨name⟩, #ifndef ⟨name⟩, #elif ⟨condition⟩, #else, #endif, #error ⟨message⟩, #warning ⟨message⟩ -
Директивы условной компиляции передаются без изменений в C-программу, C-файл и C-заголовок. Поместив их в C-программу, можно пропустить соответствующие части файла Haskell.
-
#const ⟨C_expression⟩ -
Выражение должно быть преобразуемо в
longилиunsigned long. Его значение (литерал или отрицательный литерал) будет выведено. -
#const_str ⟨C_expression⟩ -
Выражение должно быть преобразуемо в указатель const char. Его значение (строковый литерал) будет выведено.
-
#type ⟨C_type⟩ -
Эквивалент C-числового типа будет выведен в Haskell. Это может быть
{Int,Word}{8,16,32,64},Float,Double,LDouble. -
#peek ⟨struct_type⟩, ⟨field⟩ -
Функция, которая получает поле C-структуры будет выведена. У неё будет тип
Storable b => Ptr a -> IO b. Цель в том, чтобы#peekи#pokeмогли использоваться для реализации операций классаStorableдля заданной C-структуры (см. модульForeign.Storableв документации библиотеки). -
#poke ⟨struct_type⟩, ⟨field⟩ -
Аналогично для poke. У неё будет тип
Storable b => Ptr a -> b -> IO (). -
#ptr ⟨struct_type⟩, ⟨field⟩ -
Создаёт указатель на поле структуры. У неё будет тип
Ptr a -> Ptr b. -
#offset ⟨struct_type⟩, ⟨field⟩ -
Вычисляет смещение, в байтах,
fieldвstruct_type. У неё будет типInt. -
#size ⟨struct_type⟩ -
Вычисляет размер, в байтах,
struct_type. У неё будет типInt. -
#alignment ⟨struct_type⟩ -
Вычисляет выравнивание, в байтах,
struct_type. У неё будет типInt. -
#enum ⟨type⟩, ⟨constructor⟩, ⟨value⟩, ⟨value⟩, ... -
Сокращение для множественных определений, использующих
#const. Каждыйvalue— это имя C-целочисленной константы, например, значение перечисления. Название будет переведено на Haskell, сделав каждый символ после нижнего подчёркивания заглавным, остальное строчным, и удалив нижние подчёркивания. Вы можете предоставить другое перевод, написавhs_name = c_valueвместоvalue, в этом случаеc_valueможет быть произвольным выражением.hs_nameбудет определён как имеющий указанныйtype. Его определение — указанноеconstructor(что фактически может быть выражением или пустым) применённым к соответствующему целочисленному значению. Вы можете иметь несколько определений#enumс тем жеtype; эта конструкция не выводит само определение типа.
15.2.3. Пользовательские конструкции
#const, #type, #peek, #poke и #ptr не жёстко запрограммированы в hsc2hs, а определены в C-шаблоне, который включён в C-программе: template-hsc.h. Пользовательские конструкции и шаблоны тоже могут использоваться. любая конструкция # с неизвестным ключом ожидается, что она будет обработана C-шаблоном.
C-шаблон должен определить макрос или функцию с префиксом hsc_ , которая обрабатывает конструкцию, выводя расширение в стандартный вывод. См. template-hsc.h для примеров.
Такие макросы также можно определять непосредственно в исходном коде. Они полезны для создания макроса типа #let, чьё развёртывание использует другие макросы #let. Простой макрос #let добавляет hsc_ к имени макроса и оборачивает определение в вызов printf.
15.2.4. Кросс-компиляция
hsc2hs обычно работает, создавая, компилируя и выполняя программу на C. Этот подход не работает при кросс-компиляции — в этом случае компилятор C генерирует код для целевой машины, а не для хостовой. Для этой ситуации есть специальный режим hsc2hs --cross-compile, который может генерировать .hs, извлекая информацию только из компиляций — конкретно, произошла ли компиляция успешно или нет.
Только подмножество синтаксиса .hsc поддерживается --cross-compile. Следующие не поддерживаются:
#{const_str}#{let}#{def}- Пользовательские конструкции
© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/8.10.2/docs/html/users_guide/utils.html