I'm writing a Lisp-to-C translator using Prolog's built-in DCG capabilities. This is how I handle arithmetic:
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"}.
As it is now, it doesn't handle nested expressions. Here is what I thought would work:
expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%s + %s", [M, N])}.
expr(E) --> number(N), {swritef(E, "%d", [N])}.
But I'm getting this:开发者_C百科
?- expr(E, "42", []).
E = "42" %all OK
?- expr(E, "(+ 3 (* 2 2))", []).
E = "%s + %s" %not OK
How do I make it work?
The problem is that the %s format specifier needs the argument to be a list of characters. So you can do it with something like this:
:-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 --> [].
The predicate lexpr just converts the parsed expression to a list of chars.
Edit: 03/07/2016: As of SWI version 7.0, text enclosed in double quotes are not interpreted as a list of character codes anymore. You can either change double quotes with back quotes (`) or add a directive ;
:-set_prolog_flag(double_quotes, codes).
at the beginning of the code.
Use %t or %w, not %d in your swritef
. Note, %d is not what it is in C's printf formats.
If you just translating lisp-like to C-like you don't really need to convert the string representation of numbers to a number. Just leave it as string. (Of course it dependes on the complexity of your task). Otherwise the upper level rules foound a number where they expect a string.
Put the resultant C code into parentheses so that the priority and associativity are correct in the result.
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)}.
and this is how it works.
?- expr(E, "(* 1342 (/ 44 -17))", []).
E = "(1342 * (44 / -17))" ;
false.
精彩评论