Infix kifejezések: AST kiértékelés
Feladat (f05)
Írjunk nyelvtant a számológéphez, amely soronként egy-egy kifejezést vár.
A kifejezésben értelmezett
- a 4 matematikai alapművelet,
- a hatványozás (
^
),
- a zárójelezés,
- az előjelváltás (
-
) és megtartás (+
),
- mindezek megfelelő prioritással kezelve,
- az
abs()
függvény egy paraméterrel,
- a
min()
és max()
függvények legalább egy paraméterrel, és
- az
M
mint memóriarekeszben tárolt érték (melynek kezdetben 0 az értéke).
A számológép soronként olvas és értékel ki.
- Egy sorban egy kifejezés szerepel.
- Ha a sor
M
=
tokenekkel lezdődik, akkor az =
után szerplő kifejezés értékét el kell menteni a memóriarekeszbe.
- Az üres sor megengedett.
- Egy sorban a
#
utáni rész komment, eldobható.
- A sorvége jelen kívül az egyéb whitespace karakterek bárhol megengedettek.
Készítsünk egy ANTLR4 nyelvtant, ami megvalósítja a számológépet.
Példa bemenet
input.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | 42
24 + 18
6 + 12 + 24
6 * 7
6 + 6 * 6
6 * (3 + 4)
84 / 2
12 + (3 * (25 / 5)) - 9 + (2*3*4)
abs ( -42 )
max ( -42, +42 )
min(42.0,63.222,77.07)
6 + 2*6 + 2^2*6
M = (5 + 18 / 9 - 1) ^ 2 + abs( -12 / 2)
M * 3 / (1 - -2)
# Komment
M = M / 2 / 3
M * (M - 1)
|
Az előző órán felépítettünk egy AST-t.
Folytassuk a munkát és értékeljük is ki!
Számológép: AST kiértékelése
Calculator.g4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 | grammar Calculator;
options {
language = Java;
}
@members {
public static void main(String[] args) throws Exception {
CalculatorLexer lex = new CalculatorLexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream (lex);
CalculatorParser parser = new CalculatorParser(tokens);
ast.Program p = new ast.Program();
parser.start(p);
p.evaluate();
}
}
start [ ast.Program p ]
: ((line[p] { $p.addLine($line.node); } )? COMMENT? LF)* EOF
;
line [ ast.Program p ] returns [ ast.Line node ]
: MEMORY '=' expr { $node = new ast.Line($p, $expr.node, true); }
| expr { $node = new ast.Line($p, $expr.node, false); }
;
expr returns [ ast.Expression node ]
: fstop=addop { $node = $fstop.node; } (OPADD nxtop=addop {
if ("+".equals($OPADD.text)) $node = new ast.Add($node, $nxtop.node);
else $node = new ast.Sub($node, $nxtop.node);
})*
;
addop returns [ ast.Expression node ]
: fstop=mulop { $node = $fstop.node; } (OPMUL nxtop=mulop {
if ("*".equals($OPMUL.text)) $node = new ast.Mul($node, $nxtop.node);
else $node = new ast.Div($node, $nxtop.node);
})*
;
mulop returns [ ast.Expression node ]
: fstop=fct { $node = $fstop.node; } (OPPWR nxtop=mulop { $node = new ast.Pwr($node, $nxtop.node); })?
;
fct returns [ ast.Expression node ]
: SZAM { $node = new ast.Const($SZAM.text); }
| '(' expr { $node = $expr.node; } ')'
| 'abs' '(' expr ')' { $node = new ast.Abs($expr.node); }
| OPMINMAX '(' fstop=expr { $node = $fstop.node; } (',' nxtop=expr {
if ("min".equals($OPMINMAX.text)) $node = new ast.Min($node, $nxtop.node);
else $node = new ast.Max($node, $nxtop.node);
}) * ')'
| OPADD fct { $node = "-".equals($OPADD.text) ? new ast.SignM($fct.node) : new ast.SignP($fct.node); }
| MEMORY { $node = new ast.Memory(); }
;
LF : '\n' ;
WS : [ \t\r]+ ->skip ;
SZAM : [0-9]+('.' [0-9]+)? ;
OPADD : '+' | '-' ;
OPMUL : '*' | '/' ;
OPPWR : '^' ;
OPMINMAX : 'min' | 'max' ;
MEMORY : 'M' ;
COMMENT : '#' (~[\n])* ;
|
Ahhoz, hogy ez működjön először is vegyük a kifejezések ősosztályát, és tegyünk bele egy kiértékelő metódust.
Ez most megkapja, hogy melyik programban, milyen környezetben kell kértékelni a kifejezést.
Erre a memória értéke miatt van szükség, ami ugye a programhoz tartozó adat,
de a kifejezésekben (egészen pontosan a Memory
típusú AST levélben) is hozzá kell férnünk.
(Meg a Line
-ban is, de az egyrészt nem kifejezés, másrészt tudja magáról, melyik programban van.)
A memória tárolását ebben a speciális helyzetben sokféleképpen meg tudnánk oldani,
de gondolva a bővíthetőségre -- esetlegesen több tárhelyre, változókra, függvényekre --,
ez a megoldás hosszabb távon is használhatónak tűnik (persze nem az egyetlen jó módszer).
A kifejezések AST node-jainak ősosztálya
Expression.java
| package ast;
public abstract class Expression {
public abstract double evaluate(Program prog);
}
|
Ezek alapján a kiértékelés a következőképpen valósítható meg.
Mint említettük, a Program
-nak lesz egy tárolója (memory slot-ja), a Line
pedig intézi, ha értéket adtunk ennek.
A ''Főprogram'' és a sorok AST node-jai
Program.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | package ast;
import java.util.*;
public class Program {
private double memory = 0.0;
private List<Line> lines = new ArrayList<Line>();
public void addLine(Line l) {
lines.add(l);
}
public void evaluate() {
for (Line l: lines) {
l.evaluate();
}
}
public void setMemory(double value) {
memory = value;
}
public double getMemory() {
return memory;
}
}
|
Line.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | package ast;
public class Line {
private boolean assign;
private Expression expr;
private Program program;
public Line(Program p, Expression e, boolean a) {
this.program = p;
this.expr = e;
this.assign = a;
}
public void evaluate() {
double value = expr.evaluate(this.program);
if (this.assign) {
program.setMemory(value);
}
System.out.println(value);
}
}
|
A Const
egyszerűen visszatér az általa reprezentált értékkel, a Memory
pedig a program memóriájában tárolt aktuális értékkel.
''Levelek'', 0 operandusú kifejezések
Const.java
| package ast;
public class Const extends Expression {
private double value;
public Const(String token) {
this.value = Double.parseDouble(token);
}
public double evaluate(Program prog) {
return value;
}
}
|
Memory.java
| package ast;
public class Memory extends Expression {
public Memory() {
}
public double evaluate(Program prog) {
return prog.getMemory();
}
}
|
Az abszolútérték illetve az előjelek kiértékelése.
Unáris kifejezések node-jai
Abs.java
| package ast;
public class Abs extends Expression {
private Expression exp;
public Abs(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return Math.abs(exp.evaluate(prog));
}
}
|
SignM.java
| package ast;
public class SignM extends Expression {
private Expression exp;
public SignM(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return -exp.evaluate(prog);
}
}
|
SignP.java
| package ast;
public class SignP extends Expression {
private Expression exp;
public SignP(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return exp.evaluate(prog);
}
}
|
És végül a műveletek.
Bináris kifejezések node-jai
Add.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Add extends Expression {
private Expression lhs;
private Expression rhs;
public Add(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) + rhs.evaluate(prog);
}
}
|
Div.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Div extends Expression {
private Expression lhs;
private Expression rhs;
public Div(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) / rhs.evaluate(prog);
}
}
|
Max.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Max extends Expression {
private Expression lhs;
private Expression rhs;
public Max(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return Math.max(lhs.evaluate(prog), rhs.evaluate(prog));
}
}
|
Min.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Min extends Expression {
private Expression lhs;
private Expression rhs;
public Min(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return Math.min(lhs.evaluate(prog), rhs.evaluate(prog));
}
}
|
Mul.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Mul extends Expression {
private Expression lhs;
private Expression rhs;
public Mul(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) * rhs.evaluate(prog);
}
}
|
Pwr.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Pwr extends Expression {
private Expression lhs;
private Expression rhs;
public Pwr(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return Math.pow(lhs.evaluate(prog), rhs.evaluate(prog));
}
}
|
Sub.java
1
2
3
4
5
6
7
8
9
10
11
12
13 | package ast;
public class Sub extends Expression {
private Expression lhs;
private Expression rhs;
public Sub(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) - rhs.evaluate(prog);
}
}
|
Ezzel megvagyunk.
A következő feladat az lenne, hogy a felépített AST-ből generáljunk ki "forráskódot",
ami ismét lefordítva az eredetivel ekvivalens módon működik.
Ha az elemző második parancssori paramétere a --generate
, akkor ne kiértékelje a programunkat,
hanem elemzés után AST-ből generálja vissza a forráskódját.
Forráskód generálás
Ezt viszonylag könnyen megtehetjük a node-ok toString()
metódusainak felülírásával,
ahol is mindenki a saját dolgainak közvetlen, és az alá tartozó dolgok közvetett kigenerálásáért felel.
Számológép: Forráskód generálása
Calculator.g4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 | grammar Calculator;
options {
language = Java;
}
@members {
public static void main(String[] args) throws Exception {
CalculatorLexer lex = new CalculatorLexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream (lex);
CalculatorParser parser = new CalculatorParser(tokens);
ast.Program p = new ast.Program();
parser.start(p);
if (args.length > 1 && "--generate".equals(args[1])) {
System.out.println(p);
} else {
p.evaluate();
}
}
}
start [ ast.Program p ]
: ((line[p] { $p.addLine($line.node); } )? COMMENT? LF)* EOF
;
line [ ast.Program p ] returns [ ast.Line node ]
: MEMORY '=' expr { $node = new ast.Line($p, $expr.node, true); }
| expr { $node = new ast.Line($p, $expr.node, false); }
;
expr returns [ ast.Expression node ]
: fstop=addop { $node = $fstop.node; } (OPADD nxtop=addop {
if ("+".equals($OPADD.text)) $node = new ast.Add($node, $nxtop.node);
else $node = new ast.Sub($node, $nxtop.node);
})*
;
addop returns [ ast.Expression node ]
: fstop=mulop { $node = $fstop.node; } (OPMUL nxtop=mulop {
if ("*".equals($OPMUL.text)) $node = new ast.Mul($node, $nxtop.node);
else $node = new ast.Div($node, $nxtop.node);
})*
;
mulop returns [ ast.Expression node ]
: fstop=fct { $node = $fstop.node; } (OPPWR nxtop=mulop { $node = new ast.Pwr($node, $nxtop.node); })?
;
fct returns [ ast.Expression node ]
: SZAM { $node = new ast.Const($SZAM.text); }
| '(' expr { $node = new ast.Parens($expr.node); } ')'
| 'abs' '(' expr ')' { $node = new ast.Abs($expr.node); }
| OPMINMAX '(' fstop=expr { $node = ("min".equals($OPMINMAX.text)) ? new ast.Min($fstop.node) : new ast.Max($fstop.node); }
(',' nxtop=expr { ((ast.MultiArgExpression)$node).addArgument($nxtop.node); }) * ')'
| OPADD fct { $node = "-".equals($OPADD.text) ? new ast.SignM($fct.node) : new ast.SignP($fct.node); }
| MEMORY { $node = new ast.Memory(); }
;
LF : '\n' ;
WS : [ \t\r]+ ->skip ;
SZAM : [0-9]+('.' [0-9]+)? ;
OPADD : '+' | '-' ;
OPMUL : '*' | '/' ;
OPPWR : '^' ;
OPMINMAX : 'min' | 'max' ;
MEMORY : 'M' ;
COMMENT : '#' (~[\n])* ;
|
Mivel itt már nem csak a szemantikusan helyes működés a cél, hanem a forráskód (ha nem is teljesen pontos) visszanyerése,
az AST-ben (és az építésben) néhány dolgot érdemes megváltoztatni.
Számok pontossága
Érdemes lehet az eredeti literált (is) eltárolni, különben vagy a forráskód fog rondán kinézni,
vagy nem minden esetben érjük el az eredeti pontosságot.
A min
és max
operátorok
Szép és jó, hogy a kiértékeléshez binárisan építettük fel ezeket, de a kódgenerálásnál így vagy csúnya lesz a kód,
vagy nagyon komplex lesz a generálás.
Talán jobb lenne, ha itt listákkal dolgoznánk.
Zárójelezés
Eddig nem volt rá szükség, mert a kifejezés AST-jének felépítése, szerkezete feleslegessé tette a dolgot.
Most viszont visszageneráljuk a kódot, ebben pedig szükség lesz az AST-ben zárójelekre.
Alternatíva, hogy mindenhova generálunk zárójelet, de az elég hülyén nézne ki (pl. (((1 + (2 * 3)) - 4) + 5)
).
Ahhoz pedig, hogy automatikusan tudjuk generálni a zárójeleket csak a megfelelő helyekre, vagy nagyon bonyolult logika,
vagy a nyelvtani elemzés szintjeit/szabályait tükröző AST építés kellene.
Egyik sem olyan jó megoldás.
A fentiek alapján az elemző a következőképpen módosul:
Számológép: Forráskód visszagenerálása
Calculator.g4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 | grammar Calculator;
options {
language = Java;
}
@members {
public static void main(String[] args) throws Exception {
CalculatorLexer lex = new CalculatorLexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream (lex);
CalculatorParser parser = new CalculatorParser(tokens);
ast.Program p = new ast.Program();
parser.start(p);
if (args.length > 1 && "--generate".equals(args[1])) {
System.out.println(p);
} else {
p.evaluate();
}
}
}
start [ ast.Program p ]
: ((line[p] { $p.addLine($line.node); } )? COMMENT? LF)* EOF
;
line [ ast.Program p ] returns [ ast.Line node ]
: MEMORY '=' expr { $node = new ast.Line($p, $expr.node, true); }
| expr { $node = new ast.Line($p, $expr.node, false); }
;
expr returns [ ast.Expression node ]
: fstop=addop { $node = $fstop.node; } (OPADD nxtop=addop {
if ("+".equals($OPADD.text)) $node = new ast.Add($node, $nxtop.node);
else $node = new ast.Sub($node, $nxtop.node);
})*
;
addop returns [ ast.Expression node ]
: fstop=mulop { $node = $fstop.node; } (OPMUL nxtop=mulop {
if ("*".equals($OPMUL.text)) $node = new ast.Mul($node, $nxtop.node);
else $node = new ast.Div($node, $nxtop.node);
})*
;
mulop returns [ ast.Expression node ]
: fstop=fct { $node = $fstop.node; } (OPPWR nxtop=mulop { $node = new ast.Pwr($node, $nxtop.node); })?
;
fct returns [ ast.Expression node ]
: SZAM { $node = new ast.Const($SZAM.text); }
| '(' expr { $node = new ast.Parens($expr.node); } ')'
| 'abs' '(' expr ')' { $node = new ast.Abs($expr.node); }
| OPMINMAX '(' fstop=expr { $node = ("min".equals($OPMINMAX.text)) ? new ast.Min($fstop.node) : new ast.Max($fstop.node); }
(',' nxtop=expr { ((ast.MultiArgExpression)$node).addArgument($nxtop.node); }) * ')'
| OPADD fct { $node = "-".equals($OPADD.text) ? new ast.SignM($fct.node) : new ast.SignP($fct.node); }
| MEMORY { $node = new ast.Memory(); }
;
LF : '\n' ;
WS : [ \t\r]+ ->skip ;
SZAM : [0-9]+('.' [0-9]+)? ;
OPADD : '+' | '-' ;
OPMUL : '*' | '/' ;
OPPWR : '^' ;
OPMINMAX : 'min' | 'max' ;
MEMORY : 'M' ;
COMMENT : '#' (~[\n])* ;
|
Az AST elemei pedig a következőképpen:
A ''Főprogram'' és a sorok AST node-jai
Program.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | package ast;
import java.util.*;
public class Program {
private double memory = 0.0;
private List<Line> lines = new ArrayList<Line>();
public void addLine(Line l) {
lines.add(l);
}
public void evaluate() {
for (Line l: lines) {
l.evaluate();
}
}
public void setMemory(double value) {
memory = value;
}
public double getMemory() {
return memory;
}
public String toString() {
StringBuffer prg = new StringBuffer("## Generated source\n\n");
for (Line l: lines) {
prg.append(l);
}
return prg.toString();
}
}
|
Line.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | package ast;
public class Line {
private boolean assign;
private Expression expr;
private Program program;
public Line(Program p, Expression e, boolean a) {
this.program = p;
this.expr = e;
this.assign = a;
}
public void evaluate() {
double value = expr.evaluate(this.program);
if (this.assign) {
program.setMemory(value);
}
System.out.println(value);
}
public String toString() {
return (this.assign ? "M = " : "") + expr + "\n";
}
}
|
A kifejezések AST node-jainak ősosztálya
Expression.java
| package ast;
public abstract class Expression {
public abstract double evaluate(Program prog);
}
|
''Levelek'', 0 operandusú kifejezések
Const.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
public class Const extends Expression {
private double value;
private String literal;
public Const(String token) {
this.literal = token;
this.value = Double.parseDouble(token);
}
public double evaluate(Program prog) {
return value;
}
public String toString() {
return literal;
}
}
|
Memory.java
1
2
3
4
5
6
7
8
9
10
11
12 | package ast;
public class Memory extends Expression {
public Memory() {
}
public double evaluate(Program prog) {
return prog.getMemory();
}
public String toString() {
return "M";
}
}
|
Unáris kifejezések node-jai
Abs.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | package ast;
public class Abs extends Expression {
private Expression exp;
public Abs(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return Math.abs(exp.evaluate(prog));
}
public String toString() {
return "abs(" + exp + ")";
}
}
|
Parens.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | package ast;
public class Parens extends Expression {
private Expression exp;
public Parens(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return exp.evaluate(prog);
}
public String toString() {
return "(" + exp + ")";
}
}
|
SignM.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | package ast;
public class SignM extends Expression {
private Expression exp;
public SignM(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return -exp.evaluate(prog);
}
public String toString() {
return "-" + exp;
}
}
|
SignP.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | package ast;
public class SignP extends Expression {
private Expression exp;
public SignP(Expression exp) {
this.exp = exp;
}
public double evaluate(Program prog) {
return exp.evaluate(prog);
}
public String toString() {
return "+" + exp;
}
}
|
Bináris kifejezések node-jai
Add.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
public class Add extends Expression {
private Expression lhs;
private Expression rhs;
public Add(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) + rhs.evaluate(prog);
}
public String toString() {
return lhs + " + " + rhs;
}
}
|
Div.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
public class Div extends Expression {
private Expression lhs;
private Expression rhs;
public Div(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) / rhs.evaluate(prog);
}
public String toString() {
return lhs + " / " + rhs;
}
}
|
Mul.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
public class Mul extends Expression {
private Expression lhs;
private Expression rhs;
public Mul(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) * rhs.evaluate(prog);
}
public String toString() {
return lhs + " * " + rhs;
}
}
|
Pwr.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
public class Pwr extends Expression {
private Expression lhs;
private Expression rhs;
public Pwr(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return Math.pow(lhs.evaluate(prog), rhs.evaluate(prog));
}
public String toString() {
return lhs + " ^ " + rhs;
}
}
|
Sub.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
public class Sub extends Expression {
private Expression lhs;
private Expression rhs;
public Sub(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public double evaluate(Program prog) {
return lhs.evaluate(prog) - rhs.evaluate(prog);
}
public String toString() {
return lhs + " - " + rhs;
}
}
|
Többargumentumú kifejezések node-jai
Az absztrakt ősosztály:
MultiArgExpression.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | package ast;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public abstract class MultiArgExpression extends Expression {
protected List<Expression> arguments;
public MultiArgExpression(Expression first) {
arguments = new ArrayList<Expression>();
arguments.add(first);
}
public void addArgument(Expression arg) {
arguments.add(arg);
}
public String toString() {
StringBuffer str = new StringBuffer("(");
Iterator<Expression> args = arguments.iterator();
str.append(args.next());
while (args.hasNext()) {
str.append(", ");
str.append(args.next());
}
str.append(")");
return str.toString();
}
}
|
És a konkrét AST node-ok típusai:
Max.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | package ast;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Max extends MultiArgExpression {
public Max(Expression first) {
super(first);
}
public double evaluate(Program prog) {
Iterator<Expression> args = arguments.iterator();
double retval = args.next().evaluate(prog);
while (args.hasNext()) {
retval = Math.max(retval, args.next().evaluate(prog));
}
return retval;
}
public String toString() {
return "max" + super.toString();
}
}
|
Min.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | package ast;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Min extends MultiArgExpression {
public Min(Expression first) {
super(first);
}
public double evaluate(Program prog) {
Iterator<Expression> args = arguments.iterator();
double retval = args.next().evaluate(prog);
while (args.hasNext()) {
retval = Math.min(retval, args.next().evaluate(prog));
}
return retval;
}
public String toString() {
return "min" + super.toString();
}
}
|
Utolsó frissítés:
2023-02-02 13:09:36