Avaldiste ja omistamiste arvutamine Antlar3-ga

Sisendfailis on igal real kas reaalarvudega opereeriv aritmeetiline avaldis või sellise avaldise omistamine muutujale; väärtustatud muutujat võib järgnevates avaldistes juba kasutada (s.t. see on mäluga kalkulaator). Sellise sisendi süntaksi võib kirjeldada näiteks grammatikaga:

 sisend  (rida) +
rida avaldis
| ID = avaldis
| RV
avaldis yksl
|avald ((+ | -) yksliige )*
yksliige tegur ((* ) tegur)*
tegur ARV
|ID
ARV ('0'..'9')+
ID ('a'..'z'|'A'..'Z')+
RV ('\r''\n'|'\n'|'\r')

Antlr3 jaoks tuleb see sisendit kirjeldav grammatika (koos Javas kirjeldatud semantiliste tegevustega, mis arvutavad väljundi - avaldiste väärtused ja omistamiste tulemused) esitada kujul:

grammar r_expr;

@header {
import java.util.HashMap; //nimede tabeli jaoks
}

@members {
/* nimede tabel: muutuja nimi -> vaartus */
HashMap nimed = new HashMap();
}

sisend: rida+ ;

rida: avald RV {System.out.println($avald.value);}
| ID '=' avald RV
{nimed.put($ID.text, new Double($avald.value));
System.out.println($ID.text + '=' + $avald.value);}
| RV
;

avald returns [double value]
: e=yksl {$value = $e.value;}
( '+' e=yksl {$value += $e.value;}
| '-' e=yksl {$value -= $e.value;}
)*
;

yksl returns [double value]
: e=tegur {$value = $e.value;} ('*' e=tegur {$value *= $e.value;})*
;

tegur returns [double value]
: ARV {$value = Double.parseDouble($ARV.text);}
| ID
{
Double v = (Double)nimed.get($ID.text);
if ( v!=null ) $value = v.doubleValue();
else System.err.println("undefined variable "+$ID.text);
}
| '(' avald ')' {$value = $avald.value;}
;

ID : ('a'..'z'|'A'..'Z')+ ;
fragment INT : '0'..'9'+ ;
ARV : INT('.'INT)? ;
RV : '\r'? '\n' ;
WS : (' '|'\t')+ {skip();} ;

vooskeemKui see grammatikafail r_expr.g .on programmis ANTLRWorks silutud (seda saab kontrollida käsuga "Grammar - Check Grammar"), võib lasta genereerida lekseri (leksikaanalüsaatori) ja parseri koodi käsuga "Generate Code" - see loob .java failid ExprLexer.java, ExprParser.java. Neile (samasse kataloogi) tuleb lisada translaatori loomise peaprogramm Main.java (seda on juba lihtsam teha UltraEdit-is). Kuna genereeritud lekser ja parser saavad oma nime grammatikafaili nimest (siin r_expr), siis peab kontrollima, et lekseri ja parseri nimed oleks õieti, vastasel juhul Java vaid kaebab, et ei leia sisendvoogu (input stream). Kui testimine toimub UltraEdit-is või mõnenes teises ümbruses, kus on lihtne kasutada DOS-i voosuunamist < , tuleb sisendvoo näitamiseks kasutada ANTLRInputStream(System.in) (nagu siin tekstis); kui sisend tuleb failist, tuleb kasutada ANTLRFileStream(args[0])) ja peaprogrammi käivitamisel näidata selle argumendina faili nimi:

    import org.antlr.runtime.*;
public class r_Main {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
r_exprLexer lexer = new r_exprLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
r_exprParser parser = new r_exprParser(tokens);
parser.sisend();
}
}
Kõik java-failid tuleb käsuga
javac *.java
kompileerida. Loome ka testfaili, näiteks sellise:
(2+3.14)*4.2
a=5.7-2*2
b=2*a
2.3*b+3.2*a
ja käivitame loodud translaatori, andes talle sisendiks selle faili (see peab UltraEdit-is olema aktiivses aknas):
java r_Main <%f
Tulemuseks saame
21.588000000000005
a=1.7000000000000002
b=3.4000000000000004
13.260000000000002
Nüüd võib hüüda HURRAAA !!!
Ülesandeid:
1. Täienda ülalesitatud kalkulaatorit nii, et järjest oleks lubatud mitu omistamist. a = b = 2*3.14

2. Eelmises küsimuses vaadeldud hulgiomistamise süntaks on mõnes programmeerimiskeeles a,b,c = 2*3.14 - täienda ülalesitatud kalkulaatorit ka sellise süntaksi jaoks.

3. Täienda avaldist - selles peaks saama ka astendada

4. Lisa ka stringioperatsioonid. Kuidas saaks kontrollida, et omistamises vasakul oleva muutuja ja paremal olev avaldis on sama tüüpi või et stringe ja reaalarve ei kasutataks samas avaldises?.


Küsimused, probleemid: ©2004-2013 Jaak Henno