Включение грамматик (kwtypes)
Задача выделения дат является очень распространенной при извлечении фактов из текста. Чтобы не писать одни и те же правила каждый раз, мы можем включать уже написанную грамматику в другие грамматики.
Допустим, в стишке про воробья у нас не только сказано, у кого он обедал, но и уточняется, когда:
(13) — В зоопарке у зверей.
— Где обедал, воробей?
Пообедал я сперва
За решеткою у льва.
Подкрепился у лисицы 14 августа.
1 сентября у моржа попил водицы.
Ел морковку у слона 29 декабря 2011 года.
С журавлем поел пшена.
и т.д.
Мы помним, что у нас уже есть грамматика, извлекающая точные даты, и грамматика, извлекающая информацию о том, у кого обедал воробей. Чтобы выяснить, у кого и когда обедал воробей, достаточно включить одну грамматику в другую. Это можно делать двумя способами:
С помощью директивы include.
#encoding "utf-8" #include <date.cxx> Animal -> Noun<kwtype=animal>; WithWho -> 'у' (Adj<gnc-agr[1]>) Animal<gram='род', rt, gnc-agr[1]> interp (Sparrow.Who); WithWho -> 'с' (Adj<gnc-agr[1]>) Animal<gram='твор', rt, gnc-agr[1]> interp (Sparrow.Who); S -> Date interp (Sparrow.When) WithWho; S -> WithWho Date interp (Sparrow.When);
Директива include включает текст из указанного файла вместо самой себя. Т.е. парсер «увидит» вот такое текст:
#encoding "utf-8" // грамматика извлечения дат из date.cxx DayOfWeek -> Noun<kwtype="день_недели">; Day -> AnyWord<wff=/([1-2]?[0-9])|(3[0-1])/>; Month -> Noun<kwtype="месяц">; YearDescr -> "год" | "г. "; Year -> AnyWord<wff=/[1-2]?[0-9]{1,3}г?\.?/>; Year -> Year YearDescr; Date -> DayOfWeek interp (Date.DayOfWeek) (Comma) Day interp (Date.Day) Month interp (Date.Month) (Year interp (Date.Year)); Date -> Day interp (Date.Day) Month interp (Date.Month) (Year interp (Date.Year)); Date -> Month interp (Date.Month) Year interp (Date.Year); //основная грамматика Animal -> Noun<kwtype=animal>; WithWho -> 'у' (Adj<gnc-agr[1]>) Animal<gram='род', rt, gnc-agr[1]> interp (Sparrow.Who); WithWho -> 'с' (Adj<gnc-agr[1]>) Animal<gram='твор', rt, gnc-agr[1]> interp (Sparrow.Who); S -> Date interp (Sparrow.When) WithWho; S -> WithWho Date interp (Sparrow.When);
Таким образом, директива include равносильна тому, что мы скопировали текст одной грамматики и вставили его в другую.
С помощью kwtype’ов газеттира.
Мы помним, что каждой грамматике соответствует статья в корневом словаре. По сути, такая статья содержит в себе все цепочки, которые собирает данная грамматика, а значит, использовать одну грамматику в другой можно с помощью уже известной нам пометы kwtype. Выглядеть это будет так:
#encoding "utf-8" Date -> AnyWord<kwtype='**дата**'>; // используем статью "дата" из словаря Animal -> Noun<kwtype=animal>; WithWho -> 'у' (Adj<gnc-agr[1]>) Animal<gram='род', rt, gnc-agr[1]> interp (Sparrow.Who); WithWho -> 'с' (Adj<gnc-agr[1]>) Animal<gram='твор', rt, gnc-agr[1]> interp (Sparrow.Who); S -> Date interp (Sparrow.When) WithWho; S -> WithWho Date interp (Sparrow.When);
В этом примере нетерминал Date соответствует любой цепочке, собираемой грамматикой date.cxx (которая в корневом словаре лежит в статье “дата”), например, 20 августа 2012 года.
Результат в первом и втором случае будет одинаковым.
Разница между первым и вторым способом включения грамматик заключается в том, что во втором случае сначала собираются цепочки, обозначенные kwtype’ами, а уже потом — цепочки, описываемые в правилах самой грамматики. Это означает, что во втором случае грамматика получает на вход следующий текст:
(14) — Где обедал, воробей?
— В зоопарке у зверей.
Пообедал я сперва
За решеткою у льва.
Подкрепился у лисицы 14_августа.
1_сентября у моржа попил водицы.
Ел морковку у слона 29_декабря_2011_года.
С журавлем поел пшена.
14_августа, 1_сентября и 29_декабря_2011_года – это теперь неделимые сущности, которые функционируют в тексте как одно слово. Грамматика уже не различает отдельные слова в их составе. Такие единицы наследует морфологические характеристики главного слова цепочки, из которой они были собраны.
Такое положение дел верно не только для kwtype’ов, за которыми стоят грамматики, но и для всех прочих. Например, если kwtype ссылается на статью с многословными ключами, то сначала эти многословные ключи объединятся в неделимое целое, а потом начнут работать правила грамматики. Так, если у нас есть такая статья в словаре:
animal "слон"
{
key = "африканский слон"
}
А на вход дана следующая строчка:
(15) Ел морковку у африканского слона 29 декабря 2011 года.
То после применения kwtype’ов наша грамматика получит следующий текст:
(16) Ел морковку у африканского_слона 29_декабря_2011_года.
И именно его парсер будет обрабатывать правилами.
Исходные файлы проекта tutorial5
tutorial5/config.proto | Конфигурационный файл парсера |
tutorial5/kwtypes.proto | Объявление kw-типа animals |
tutorial5/facttypes.proto | Описание типов фактов |
tutorial5/mydic.gzt | Корневой словарь |
tutorial5/animals_dict.gzt | Словарь с названиями животных |
tutorial5/mammals.txt | Список млекопитающих |
tutorial5/main.cxx | Основная грамматика |
tutorial5/date.cxx | Грамматика для дат |
tutorial5/test.txt | Текст «Где обедал воробей ...» с датами |
tutorial5/config.proto | Конфигурационный файл парсера |
tutorial5/kwtypes.proto | Объявление kw-типа animals |
tutorial5/facttypes.proto | Описание типов фактов |
tutorial5/mydic.gzt | Корневой словарь |
tutorial5/animals_dict.gzt | Словарь с названиями животных |
tutorial5/mammals.txt | Список млекопитающих |
tutorial5/main.cxx | Основная грамматика |
tutorial5/date.cxx | Грамматика для дат |
tutorial5/test.txt | Текст «Где обедал воробей ...» с датами |