Внимание. Этот документ в процессе написания.
Это перевод руководства по Alex версии 3.0. Перевод осуществил Симоненко Евгений [email protected].
Alex это инструмент для генерирования лексических анализаторов на Haskell на основе описание токенов, которые должны быть распознаны в виде регулярных выражений. Alex похож на такие инструменты как lex и flex для C/C++.
Alex принимает описание токенов на основе регулярных выражений и генерирует модуль на Haskell, содержащий код для эффективного сканирования текста. Alex разработан так, чтобы быть знакомым для пользователей lex, хотя в действительности он отличается от lex в нескольких отношениях.
Пример спецификации приведён на рисунке 2.1. Первые несколько строк между {
и
}
предоставляют кусок кода на Haskell, который будет вставлен в вывод
непосредственно, этот кусок вверху модуля -- обычная практика по объявлению
имени модуля для генерируемого кода на Haskell, в данном случае это Main
.
Следующая строка, % wrapper "basic"
, определяет, какой код поддержки должен
производить Alex вместе с основным сканером. Обёртка basic
выбирает сканер,
который токенизирует строку и возвращает список токенов. Обёртки подробно
описаны в Глава "Интерфейс к созданному Alex лексеру".
Следующие две строки определяют макросы $digit
и $alpha
для использования в
определениях токенов.
Строка tokens :-
заканчивает определение макросов и начинает определение
сканера.
Сканер специфицируется в виде серии определений токенов, где каждая спецификация токена принимает форму
regexp { code }
Назначение этого правила: «если вход соответствует regexp
, вернуть code
».
Часть кода вместе с фигурными скобками может быть заменена на просто ;
, что
означает, что во входном потоке этот токен следует игнорировать. Как вы можете
видеть, мы использовали это, чтобы игнорировать в нашем примере пробел.
Наш сканер настроен так, что действия являются функциями типа String -> Token
.
Когда токен сопоставлен, часть входного потока, который он сопоставил,
передается соответствующей функции действия в виде строки.
В нижней части файла есть еще один фрагмент кода, окруженный фигурными скобками
{ ... }
. В этом фрагменте мы объявляем тип токенов и предоставляем функцию
main
, которую мы можем использовать для тестирования; функция main
просто
выполняет токенизацию ввода и печатает результаты на стандартный вывод.
Alex любезно предоставил следующую функцию, которую мы можем использовать для вызова сканера:
alexScanTokens :: String -> [Token]
Alex организует токенизацию входного потока, каждой из функций действия передается соответствующая строка, а как результат возвращается список токенов. Если входной поток ленив, выходной поток также будет производиться лениво.
Здесь мы продемонстрировали простейшую форму сканера, которая была выбрана
строкой %wrapper "basic"
в верхней части файла. В общем случае, действия не
обязаны иметь тип String -> Token
, и сканер не обязан возвращать список
токенов.
Для этой спецификации в файле Tokens.x
Alex можно использовать для создания
Tokens.hs
:
{
module Main (main) where
}
%wrapper "basic"
$digit = 0-9 -- digits
$alpha = [a-zA-Z] -- alphabetic characters
tokens :-
$white+ ;
"--".* ;
let { \s -> Let }
in { \s -> In }
$digit+ { \s -> Int (read s) }
[\=\+\-\*\/\(\)] { \s -> Sym (head s) }
$alpha [$alpha $digit \_ \’]* { \s -> Var s }
{
-- Each action has type :: String -> Token
-- The token type:
data Token =
Let |
In |
Sym Char |
Var String |
Int Int
deriving (Eq,Show)
main = do
s <- getContents
print (alexScanTokens s)
}
(Рис. 2.1.)
alex Tokens.x
Если модуль необходимо поместить в другой файл, например Main.hs, то имя
выходного файла можно указать, используя опцию -o
:
alex Tokens.x -o Main.hs
Получаемый модуль совместим с Haskell 98. Его также можно легко использовать с
парсером Happy
(https://www.haskell.org/happy/).
В этом разделе мы описываем формат лексической спецификации Alex.
Лексический синтаксис Alex приведен ниже. Он написан как набор макроопределений с использованием собственного синтаксиса Alex. Эти макросы используются позже в BNF спецификации синтаксиса.
$digit = [0-9]
$octdig = [0-7]
$hexdig = [0-9A-Fa-f]
$special = [\.\;\,\$\|\*\+\?\#\~\-\{\}\(\)\[\]\^\/]
$graphic = $printable # $white
@string = \" ($graphic # \")* \"
@id = [A-Za-z][A-Za-z’_]*
@smac = ’$’ id
@rmac = ’@’ id
@char = ($graphic # $special) | @escape
@escape = ’\\’ ($printable | ’x’ $hexdig+ | ’o’ $octdig+ | $digit+)
@code = -- curly braces surrounding a Haskell code fragment
...
(c) 2018, Симоненко Евгений