% Analyse syntaxique
% Produit l'ASA d'un programme. Donne un noeud
.
program(prog(T)) --> statement(T).
% Produit l'ASA d'un enonce. Donne un noeud .
% ::= if1(,) | if2(,,) | while(,) |
% do(,) | seq(,) | empty | expr()
statement(if1(E,S)) -->
token("if"),
paren_expr(E),
statement(S).
statement(if2(E,S1,S2)) -->
token("if"),
paren_expr(E),
statement(S1),
token("else"),
statement(S2).
statement(while(E,S)) -->
token("while"),
paren_expr(E),
statement(S).
statement(do(S,E)) -->
token("do"),
statement(S),
token("while"),
paren_expr(E),
token(";").
statement(S) -->
token("{"),
statement_list(S),
token("}").
statement(expr(E)) -->
expr(E),
token(";").
statement(empty) -->
token(";").
statement_list(empty) -->
[].
statement_list(seq(S1,S2)) -->
statement(S1),
statement_list(S2).
% ::= assign(,) | lt(,) | add(,) | sub(,) |
% var() | cst()
% est une variable (un atome Prolog a une lettre)
% est un nombre
paren_expr(E) -->
token("("),
expr(E),
token(")").
expr(E) -->
test(E).
expr(assign(V,E)) -->
token(id(V)),
token("="),
expr(E).
test(E2) -->
sum(E1),
test_next(E1,E2).
test_next(E,E) -->
[].
test_next(E1,lt(E1,E2)) -->
token("<"),
sum(E2).
sum(E2) -->
term(E1),
sum_next(E1,E2).
sum_next(E1,E3) -->
token("+"),
term(E2),
sum_next(add(E1,E2),E3).
sum_next(E1,E3) -->
token("-"),
term(E2),
sum_next(sub(E1,E2),E3).
sum_next(E,E) -->
[].
term(var(V)) -->
token(id(V)).
term(cst(N)) -->
token(int(N)).
term(E) -->
paren_expr(E).
% Analyseur lexical: consomme les espaces; produit une liste contenant
% un mot-cle ou un operateur, un identificateur `id()' ou un entier
% `int()'
token(T) -->
" ",
token(T).
token(T) -->
word(T).
token(int(N)) -->
integer(N).
token([X]) -->
[X].
word(T) -->
letters(L),
{word_next(L,T)}.
word_next([X],id(V)) :-
char_code(V,X).
word_next(L,L).
% Lit autant de lettres que possible
letters(""), [X] -->
[X],
{X < "a"; X > "z"}.
letters([X|L]) -->
[X],
{"a" =< X, X =< "z"},
letters(L).
% Lit autant de chiffres que possible
integer(N2) -->
[X],
{"0" =< X, X =< "9"},
{N1 is X - "0"},
int_next(N1,N2).
int_next(N,N), [X] -->
[X],
{X < "0"; X > "9"}.
int_next(N1,N3) -->
[X],
{"0" =< X, X =< "9"},
{N2 is 10 * N1 + X - "0"},
int_next(N2,N3).
% Generation de code
% Produit un melange d'atomes et de nombres signes conformes a ceux qui
% sont produits par le compilateur original, p. 99. Note: une autre
% grammaire DCG est utilisee pour PRODUIRE le code-octets; des
% etiquettes sont propagees pour connaitre la position de chaque
% instruction; comme les sauts sont relatifs, une variable est emise
% afin de representer la distance et le calcul de cette distance est
% remis a plus tard
compile(prog(N),Lin,Lout) -->
compile(N,Lin,L1),
emit(return,L1,Lout).
compile(if1(E,S),Lin,Lout) -->
compile(E,Lin,L1),
emit(ifeq,L1,L2),
emit_jump(Lout,L2,L3,C1),
compile(S,L3,Lout),
{C1}.
compile(if2(E,S1,S2),Lin,Lout) -->
compile(E,Lin,L1),
emit(ifeq,L1,L2),
emit_jump(L6,L2,L3,C1),
compile(S1,L3,L4),
emit(goto,L4,L5),
emit_jump(Lout,L5,L6,C2),
compile(S2,L6,Lout),
{C1, C2}.
compile(while(E,S),Lin,Lout) -->
compile(E,Lin,L1),
emit(ifeq,L1,L2),
emit_jump(Lout,L2,L3,C1),
compile(S,L3,L4),
emit(goto,L4,L5),
emit_jump(Lin,L5,Lout,C2),
{C1, C2}.
compile(do(S,E),Lin,Lout) -->
compile(S,Lin,L1),
compile(E,L1,L2),
emit(ifne,L2,L3),
emit_jump(Lin,L3,Lout,C1),
{C1}.
compile(seq(S1,S2),Lin,Lout) -->
compile(S1,Lin,L1),
compile(S2,L1,Lout).
compile(empty,Lin,Lin) -->
[].
compile(expr(E),Lin,Lout) -->
compile(E,Lin,L1),
emit(pop,L1,Lout).
compile(assign(V,E),Lin,Lout) -->
compile(E,Lin,L1),
emit(dup,L1,L2),
emit(istore,L2,L3),
{char_code(V,Code)},
{No is Code - "a"},
emit(No,L3,Lout).
compile(lt(E1,E2),Lin,Lout) -->
emit(bipush,Lin,L1),
emit(1,L1,L2),
compile(E1,L2,L3),
compile(E2,L3,L4),
emit(isub,L4,L5),
emit(iflt,L5,L6),
emit_jump(Lout,L6,L7,C1),
emit(pop,L7,L8),
emit(bipush,L8,L9),
emit(0,L9,Lout),
{C1}.
compile(add(E1,E2),Lin,Lout) -->
compile(E1,Lin,L1),
compile(E2,L1,L2),
emit(add,L2,Lout).
compile(sub(E1,E2),Lin,Lout) -->
compile(E1,Lin,L1),
compile(E2,L1,L2),
emit(sub,L2,Lout).
compile(var(V),Lin,Lout) -->
emit(iload,Lin,L1),
{char_code(V,Code)},
{No is Code - "a"},
emit(No,L1,Lout).
compile(cst(N),Lin,Lout) -->
emit(bipush,Lin,L1),
emit(N,L1,Lout).
emit_jump(Ldest,Lin,Lout,D is Ldest-Lin) -->
[D],
{Lout is Lin+1}.
emit(X,Lin,Lout) --> [X], {Lout is Lin+1}.
% Exemple d'utilisation de `compile':
% program(Node,"while (x<0) x=x+1;",""), compile(Node,0,_,BC,[]), write(BC).