Словом парсер называется механизм, вычленяющий из текстовых данных какую-либо информацию. Типичный пример парсера – функция reads
. Напомним его тип:
reads :: Read a => String -> [(a,String)]
Парсеры такого вида используются для сложных грамматик, требующих для распознания нетривиально структурированного перебора вариантов. В этом же и нескольких последующих упражнениях договоримся о следующем обозначении
type Parser a = String -> Maybe (a,String)
Договоримся, что выходной текст любого парсера является суффиксом (возможно, полным) входного.
Напишите семейство парсеров char :: Char -> Parser Char
, распознающих заданный символ в начале текста.
Напишите парсер integer :: Parser Integer
, распознающий целое число (знак перед числом не допускается).
Напишите комбинатор inject :: (a->b) -> Parser a -> Parser b
, применяющий заданное преобразование к результату работы парсера в случае его успеха.
Напишите комбинатор alt :: [Parser a] -> Parser a
, делающий из последовательности парсеров сложный парсер, пробующий по очереди парсеры этой последовательности до первого удачного исхода.
Напишите комбинатор bind :: Parser a -> (a -> Parser b) -> Parser b
, делающий из парсера и генератора парсеров сложный парсер, строящий новый парсер по результату первого парсера, а затем применяющий его к оставшейся после первого парсера части текста.
Также напишите генератор trivial :: a -> Parser a
, делающий из входной величины парсер, всегда распознающий эту величину, оставляя текст неизменным.
Договоримся дальше иногда использовать комбинатор
thenP :: Parser a -> Parser b -> Parser b
thenP x y = x `bind` const y
Рассмотрим следующий комбинатор:
sim :: [Parser a] -> Parser [a]
sim [] text = trivial [] text
sim (p:ps) text = case p text of
Just (x,rest) -> case sim ps rest of
Just (xs,last) -> Just (x:xs,last)
Nothing -> Nothing
Nothing -> Nothing
Выразите этот комбинатор при помощи foldr
без явной рекурсии. Вам может пригодиться комбинатор bind
из прошлого упражнения.
Приведём пример парсера, вычисляющиего значение арифметического выражения, состоящего из цифр и знаков сложения.
expr :: Parser Integer
expr = sum `inject` sim [integer, alt [char '+' `thenP` expr, trivial 0]]
Сделайте этот парсер нечувствительным к пробелам.
Добавьте к этому парсеру возможность обработки знаков умножения.
Добавьте к получившемуся в результате выполнения предыдущего упражнения парсеру возможность обработки знаков вычитания, а также – знака перед первым числом выражения.
И, наконец, добавьте возможность обработки скобок.
Сделайте последнее упражнение к главе 6.
@ 2016 arbrk1, all rights reversed