Отвечаю на свой глупый вопрос:
@CapelliC дал работающее решение (+1). Он делает что-то, чего я не понимаю :-(, но настоящая проблема заключалась в том, что я не понимал проблему, которую пытался решить. Настоящая проблема заключалась в следующем:
Проблема
У вас есть входной список кодов, который вам нужно проанализировать. В результате должен получиться термин. Вы знаете довольно близко к началу этого списка кодов, как выглядят остальные. Другими словами, он начинается с «ключевого слова», определяющего содержимое. В некоторых случаях, после некоторой точки ввода, остальное содержимое не нужно разбирать: вместо этого оно собирается в результирующем термине в виде списка кодов.
Решение
Одно из возможных решений — разбить синтаксический анализ на два вызова phrase/3
(поскольку нет причин не делать этого?):
- Прочитайте ключевое слово (первый вызов
phrase/3
) и сделайте его атомом;
- Посмотрите в таблице, как должно выглядеть остальное;
- Анализировать только то, что необходимо проанализировать (второй вызов
phrase/3
).
Код
Итак, используя подход из (O'Keefe 1990) и используя преимущества library(dcg/basics)
, доступные в SWI-Prolog, с файлом rest.pl
:
:- use_module(library(dcg/basics)).
codes_term(Codes, Term) :-
phrase(dcg_basics:nonblanks(Word), Codes, Codes_rest),
atom_codes(Keyword, Word),
kw(Keyword, Content, Rest, Term),
phrase(items(Content), Codes_rest, Rest).
kw(foo, [space, integer(N), space, integer(M)], [], foo(N, M)).
kw(bar, [], Text, bar(Text)).
kw(baz, [space, integer(N), space], Rest, baz(N, Rest)).
items([I|Is]) -->
item(I),
items(Is).
items([]) --> [].
item(space) --> " ".
item(integer(N)) --> dcg_basics:integer(N).
Важно, что здесь остальное вообще не должно обрабатываться правилом DCG.
Пример использования
Это решение хорошо, потому что оно детерминировано и его очень легко расширить: просто добавьте предложения в таблицу kw/4
и правила item//1
. (Обратите внимание на использование флага --traditional
при запуске SWI-Prolog для списков кодов, разделенных двойными кавычками)
$ swipl --traditional --quiet
?- [rest].
true.
?- codes_term("foo 22 7", T).
T = foo(22, 7).
?- codes_term("bar 22 7", T).
T = bar([32, 50, 50, 32, 55]).
?- codes_term("baz 22 7", T).
T = baz(22, [55]).
person
Community
schedule
10.09.2014