DCG в Прологе - струны - PullRequest
       40

DCG в Прологе - струны

2 голосов
/ 08 декабря 2010

Я пишу переводчик Lisp-to-C, используя встроенные возможности Prolog DCG. Вот как я работаю с арифметикой:

expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d + %d", [M, N])}.
expr(Z) --> "(", "-", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d - %d", [M, N])}.
expr(Z) --> "(", "*", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d * %d", [M, N])}.
expr(Z) --> "(", "/", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d / %d", [M, N])}.
expr(E) --> number(E).

number(C) --> "-", digits(X), {C is -X}.
number(C) --> digits(C).
digits(D) --> digit(D);digit(A),digits(B), {number_codes(B,Cs),length(Cs,L), D is A*(10^L)+B}.
digit(D) --> [C], {"0"=<C, C=<"9", D is C - "0"}.

Как и сейчас, он не обрабатывает вложенные выражения. Вот то, что я думал, будет работать:

expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%s + %s", [M, N])}.
expr(E) --> number(N), {swritef(E, "%d", [N])}.

Но я получаю это:

?- expr(E, "42", []).
E = "42" %all OK

?- expr(E, "(+ 3 (* 2 2))", []).
E = "%s + %s" %not OK

Как мне заставить это работать?

Ответы [ 2 ]

2 голосов
/ 08 декабря 2010

Проблема в том, что для спецификатора формата% s аргумент должен быть списком символов. Так что вы можете сделать это примерно так:

:-set_prolog_flag(double_quotes, codes).  % This is for SWI 7+ to revert to the prior interpretation of quoted strings.

expr(Z) --> "(", "+", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s + %s", [M, N])}.
expr(Z) --> "(", "-", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s - %s", [M, N])}.
expr(Z) --> "(", "*", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s * %s", [M, N])}.
expr(Z) --> "(", "/", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s / %s", [M, N])}.
expr(N) --> number(N).

lexpr(Z) --> expr(M), {atom_chars(M, Z)}.

number(C) --> "-", digits(X), {C is -X}.
number(C) --> digits(C).

digits(D) --> digit(D);digit(A),digits(B), {number_codes(B,Cs),length(Cs,L), D is A*(10^L)+B}.
digit(D) --> [C], {"0"=<C, C=<"9", D is C - "0"}.

spaces --> " ", spaces.
spaces --> [].

Предикат lexpr просто преобразует проанализированное выражение в список символов.

Редактировать: 07.03.2016: Начиная с версии SWI 7.0 текст, заключенный в двойные кавычки, больше не интерпретируется как список кодов символов. Вы можете изменить двойные кавычки с помощью обратных кавычек (`) или добавить директиву;

:-set_prolog_flag(double_quotes, codes).

в начале кода.

1 голос
/ 08 декабря 2010

Используйте % t или% w, а не% d в вашем swritef.Обратите внимание, что% d отличается от формата printf на языке C.

Если вы просто переводите lisp-like в C-like, вам не нужно преобразовывать строковое представление чисел в число.Просто оставьте это как строку.(Конечно, это зависит от сложности вашей задачи).В противном случае правила верхнего уровня находят число там, где они ожидают строку.

Поместите полученный C-код в скобки, чтобы в результате были правильными приоритет и ассоциативность.

expr(Z) --> "(", "-", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t - %t)", [M, N])}.
expr(Z) --> "(", "*", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t * %t)", [M, N])}.
expr(Z) --> "(", "/", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t / %t)", [M, N])}.
expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t + %t)", [M, N])}.
expr(E) --> number(N), {swritef(E, "%s", [N])}.

spaces --> " ".

number([C|Cs]) --> "-", {C = "-"}, digits(Cs).
number(C) --> digits(C).

digits([D|[]]) --> digit(D).

digits([D|Ds]) --> digit(D), digits(Ds).
digit(D) --> [D], {code_type(D, digit)}.

, и этокак это работает.

?- expr(E, "(* 1342 (/ 44 -17))", []).
E = "(1342 * (44 / -17))" ;
false.
...