Pisi-Algoli transleerimiseks HLA assemblerkoodiks on tarvis muuta vaid väljundi genereerimise tegevusi getExpr ja getomist, kogu programmi loogika jääb täpselt samaks.
Pisi-Algoli suunamiskäskude GOTO-de asemele tulevad assembleri suunamiskäsud jmp. Ka aritmeetiliste avaldiste arvutamine muutub veidi: assembleris peab üks operand olema register ja ka tulemus saadakse registris, seega kolmeaadressilise koodi omistuskäsk X := Y op Z, kus op {+, -, *} muutub HLA koodiridadeks:
mov(y, eax);
m_op(z, eax);
mov(eax, x);
mov(y, eax);
cdq();
idiv(z, edx:eax);
mov(eax, x);
Kolmeaadressilise koodi jaoks polnud väljundkäskudel mõtet, kuid HLA-s on, sellepärast on Pisi-Algolile lisatud ka väljundkäsk kujul PRINT arg; kus arg on kas identifikaator (transleerimisel määratakse sellele HLA-s kümne sümboli pikkune tekstiväli), tekstistring või reavahetusmärk RV (transleeritakse HLA reavahetussümboliks nl).
Järgnevas on Antlr-i sisndfail, mis genereerib Pisi-Algoli translaatori HLA-ks. Kuna HLA-s peab muutujad deklareerima, siis tuleb moodustada ka nimede (siin vaid identifikaatorid) tabel. Kuna Pisi-Algol-is deklaratsioone ei ole, kogutakse omistamislauses vasakul olevad identifikaatorid puu läbimise ajal Java klassi java.util.Set andmestruktuuri idents - see ongi siin nimede tabel. Nimede tabeli moodustamine võimaldab teha ka semantilist kontrolli: kas omistamislauses paremal pool olevatel identifikaatoritel on juba väärtus? Pisi-Algolis võis identifikaator saada väärtuse vaid siis, kui ta enne seda esines kusagil omistamislause vasakul pool, s.t. on juba kantud nimede tabelisse; kui teda seal pole, teatatakse veast. Järgnevas on süntaksigrammatika ja puuläbija grammatika tekstid.
grammar p_algol;
options {
output=AST;
ASTLabelType=CommonTree; // type of $stat.tree ref etc...
}
tokens {
IF='IF';
THEN = 'THEN';
ELSE = 'ELSE';
ENDIF = 'ENDIF';
FOR = 'FOR';
DO = 'DO';
UNTIL = 'UNTIL';
ENDLOOP = 'ENDLOOP';
PRINT = 'PRINT';
RV = 'RV';
}
prog : (NEWLINE!)* prog1 ;
prog1: ( statement (NEWLINE!)* )+ ;
statement
: omist SEMICOLON!
| if_lause SEMICOLON!
| tsykkel SEMICOLON!
| print_lause SEMICOLON!
| blokk
;
omist: ID '='^ avald //-> ^('=' ID expr)
//->
;
avald: yksl (('+'^|'-'^) yksl)*
;
yksl
: tegur ('*'^ tegur)*
;
tegur : atom (ASTE^ atom)? ;
atom : INT
| ID
| '('! avald ')'! ;
if_lause : IF^ tingimus then_osa
;
then_osa :
THEN^ then_laused else_osa
;
tingimus:
avald (SUUREM^|VAIKSEM^|VORDNE^) avald
;
then_laused:
statement
;
else_osa :
ELSE^ statement ENDIF!
| ENDIF!
;
tsykkel : FOR^ rajad DO! prog ENDLOOP! ;
rajad : algv UNTIL^ loppv;
algv : omist;
loppv : avald;
blokk : '{'! statement* '}'!
;
print_lause: PRINT^ pavald;
pavald : ID | TEKST | RV ;
ID : LET(LET|NUM)* ;
INT : NUM+;
fragment NUM : '0'..'9' ;
fragment LET : ('a'..'z'|'A'..'Z') ;
SEMICOLON : ';';
SUUREM : '>';
VAIKSEM : '<';
VORDNE : '==';
ASTE : '^' ;
TEKST :'"' (LET|NUM|' '|'-'|'_')* '"';
NEWLINE:'\r'? '\n' ;
WS : (' '|'\t')+ {skip();} ;
tree grammar Eval;
options {
tokenVocab=p_algol;
ASTLabelType=CommonTree;
}
@header {
import java.util.TreeMap;
import java.util.HashSet;
}
@members {
public static int i = 0; // variables counter
public static int ln = 0; // line number
public static int label = 0; // label number
public static int n = 0;
public static String tsykli_id = "";
public static String id="";
public static java.util.Map codeLine = new java.util.TreeMap();
public static java.util.Set nimed = new java.util.HashSet();
public static String getTempVariable()
{
String var = "T"+(i++);
nimed.add(var);
return(var);
};
public static int getLineNumber()
{
return (++ln);
};
public static java.util.Map out(int Nr,String s)
{codeLine.put(new Integer(Nr), new String(s));
return codeLine;
};
public static String getExpr(String var1, String left, String op, String right)
{String lns="";
if (op.equals("+")){
lns = "mov("+left+", eax);\nadd("+right+", eax);\nmov(eax,"+ var1 +");";}
else if ("-".equals(op)) {
lns = "mov("+left+", eax);\nsub"+right+", eax);\nmov(eax,"+ var1 +");";}
else if ("*".equals(op)) {
lns = "mov("+left+", eax);\nmul"+right+", eax);\nmov(eax,"+ var1 +");";}
else if ("/".equals(op)) {
lns = "mov("+left+", eax);\ncdq();\nidiv("+right+", edx:eax);\nmov(eax,"+ var1 +");";}
return(lns);
};
public static String getomist(String left, String right)
{return ( "mov("+right+", "+left+");"); };
}
prog : prog1 ;
prog1: statement+ ;
statement: omist
| if_lause
| tsykkel
| print_lause
;
omist: ^('=' ID e=avald)
{id=$ID.text;
nimed.add(id);
out(getLineNumber(),getomist(id,e));}
;
avald returns [String s] :
^('+' a=avald b=avald) { s=getTempVariable();
out(getLineNumber(),getExpr(s,a,"+",b));}
| ^('-' a=avald b=avald) { s=getTempVariable();
out(getLineNumber(),getExpr(s,a,"-",b));}
| ^('*' a=avald b=avald) { s=getTempVariable();
out(getLineNumber(),getExpr(s,a,"*",b));}
| ID
{s = $ID.text;
try
{nimed.contains(s);}
catch (Exception e) {
System.err.println("Identifikaator pole vaartust "+s+ e);}
}
| INT {s = (String)($INT.text);}
;
if_lause :
^(IF nr=tingimus lnr=then_osa)
{out(nr, "jmp L"+lnr+";");}
;
then_osa returns [String lnr] :
^(THEN nr=then_statements lnr1=else_osa)
{if (lnr1=="")
{lnr=Integer.toString(ln+1);}
else
{out(nr, "jmp L"+lnr1+";");
lnr = Integer.toString(nr+1);}
}
;
else_osa returns [String lnr=""]:
^(ELSE prog1)
{lnr=Integer.toString(ln+1);}
|
;
then_statements returns [int nr]:
prog1
{nr=getLineNumber();}
;
tingimus returns [int nr] :
^(SUUREM e1=avald e2=avald)
{n=getLineNumber();
out(n,"if ("+e1+"+>"+e2+") then \njmp L"+(n+2)+";\nendif;");
nr=getLineNumber();}
|^(VAIKSEM e1=avald e2=avald)
{n=getLineNumber();
out(n,"if ("+e1+"<"+e2+") then\n jmp L"+(n+2)+";\nendif;");
nr=getLineNumber(); }
|^(VORDNE e1=avald e2=avald)
{n=getLineNumber();
out(n,"if ("+e1+"=="+e2+") then\n jmp L"+(n+2)+";\nendif;");
nr=getLineNumber();}
;
tsykkel :
^(FOR nr=rajad prog1)
{
n=getLineNumber();
out(n,"add(1,"+tsykli_id+");");//tsyklimuutuja suurendamine
n=getLineNumber();
out(n, "jmp L"+(nr-1)+";");
out(nr, "jmp L"+(n+1)+";"); //tsyklist valja ! (nr+1 jaoks jättis rajad rea!)
}
;
rajad returns [int nr=0] :
^(UNTIL id=algv e=avald)
{
n=getLineNumber();
out(n, "if ("+id+"<="+e+") then\n jmp L"+(n+2)+";\nendif;");
nr=getLineNumber(); // tsyklist valja viiva GOTO-jaoks ruum
}
;
algv returns [String var=""] :
^('=' id=ID e=avald)
{
var = $ID.text;
tsykli_id=var;
nimed.add(var); //tsyklimuutuja lisatakse nimede tabelisse!
out(getLineNumber(), getomist(var,e)); //tsyklimuutuja algvaartustamine
}
;
print_lause :
^(PRINT out_str=pavald)
{ out(getLineNumber(),"stdout.put("+out_str+");");
}
;
pavald returns [String s=""]:
TEKST
{s = (" "+$TEKST.text+" ") ;}
| ID {s=$ID.text+":10"; }
| RV {s ="nl";}
;
Veidi muutub peaprogramm Generate - ta loeb töö algul nimede tabelist nimed identifikaatorid ja lisab igale identifikaatorile deklaratsiooni (siin deklareeritakse nad kõik 32-bitisteks täisarvudeks); peale selle lisab programm transleeritud tekstile esimese ja viimase rea. Kuna HLA märgendid peavad olema kirjutatud identifikaatoritega analoogiliselt (algama kas tähe või allkriipsuga _), on teksti väljastamisel (peaprogrammis Generate) reanumbrite ette lisatud täht L (Label):
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import java.util.Iterator;
import java.io.*;
public class Generate {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
p_algolLexer lexer = new p_algolLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
p_algolParser parser = new p_algolParser(tokens);
p_algolParser.prog_return r = parser.prog();
// walk resulting tree
CommonTree t = (CommonTree)r.getTree();
CommonTreeNodeStream nodes = new CommonTreeNodeStream(t);
Eval walker = new Eval(nodes);
walker.prog();
int n = walker.codeLine.size();
System.out.println("program proge;");
System.out.println("#include( \"stdlib.hhf\" );");
System.out.println("static");
for (Iterator i = walker.nimed.iterator(); i.hasNext(); )
{ System.out.println(i.next()+": int32;"); }
System.out.println("begin proge;");
for (Iterator it= walker.codeLine.keySet().iterator(); it.hasNext( ); ) {
Object ckey = it.next();
System.out.println("L"+ckey+":\t "+ walker.codeLine.get(ckey));
}
System.out.println("L"+Integer.toString(n+1)+":\t end proge;");
}
}
Selle sisendiga genereeritud translaator teisendab Pisi-Algoli programmi
F0 = 1;järgnevaks HLA assemblerteksiks:
F1 = 1;
FOR I = 1 UNTIL 40 DO
F2 = F0 + F1;
F0 = F1;
F1 = F2;
PRINT I;
PRINT "-s arv on ";
PRINT F1;
PRINT RV;
ENDLOOP;
program proge;
#include( "stdlib.hhf" );
static
I: int32;
F0: int32;
F1: int32;
T0: int32;
F2: int32;
begin proge;
L1: mov(1, F0);
L2: mov(1, I);
L3: if (I<=40) then
jmp L5;
endif;
L4: jmp L15;
L5: mov(F0, eax);
add(F1, eax);
mov(eax,T0);
L6: mov(T0, F2);
L7: mov(F1, F0);
L8: mov(F2, F1);
L9: stdout.put(I:10);
L10: stdout.put(" -s Fibonacci arv on " );
L11: stdout.put(F1:10);
L12: stdout.put(nl);
L13: add(1,I);
L14: jmp L3;
L15: end proge;
Kui see tekst salvestada näiteks failis fib.hla ja transleerida HLA translaatoriga:
HLA fib.hla
saame transleeritud faili fib.exe käivitamisel täpselt samasuguse Fibonacci arvude loetelu, nagu kõigi teiste siin vaadeldud keeltes koostatud programmidega, s.t. meil on nüüd töötav Pisi-Algol-i translaator:1 -s Fibonacci arv on 1
2 -s Fibonacci arv on 1
3 -s Fibonacci arv on 2
...
39 -s Fibonacci arv on 102334155
40 -s Fibonacci arv on 165580141
. Loomulikult on see veel väga algeline (puudub sisend, puuduvad reaalarvud jne jne), kuid seda kõike on juba lihtsam teha ehk nagu paarkümmend aastat tagasi öeldi - edasi on kõik "Дело техники!"
Viimasest näitest on ka näha, et "tagant mitmeharulise", s.t. mitmesse väljundkeelde transleeriva translaatori (nagu on Microsoft .net ja gcc translaatorite süsteemid) tegemine on suhteliselt lihtne, kui "esimene ots", s.t. analüüs kuni AST-i saamiseni on juba tehtud (AST on vahekeel) ja need keeled on sama tüüpi, s.t. imperatiivsed - piisab vaid väljundit genereerivate funktsioonide muutmisest. Seda ideed on Antlr-is edasi arendatud nn tekstimustrite (string templates) kasutuselevõtuga. Tekstimustrid on eraldi failid, milles on kirjas translaatori "tagumise otsa" keel, s.t. see, mis genereeritakse AST-ist. See võib olla C++, Python-i, Java VM assemblerkäskude genereerimine (tekst), kuid võib olla ka üsna keerukas süsteem, kus loetakse eraldi failides (või serveritel) olevatest tabelitest, formateeritakse HTML-i ja genereeritakse veebisaidi kasutajaliides (vt näiteks Amazon-i ülikomplitseeritud lehekülgi); Antlr-i tekstimustrite abil on juba aastaid ülal peetud näiteks saiti http://www.jguru.com.
a = b = 10*c + 14. Täienda Pisi-Algolit operaatoritega ++, -- (postfix-kujul, s.t. lubatud oleks näiteks a++, a--, kuid mitte ++a, --a).
programm Fiboja see nimi muutub ka transleeritud HLA-programmi nimeks.
F0 = 1;
...
end Fibo
break;
breakif( boolean_expression );