Kihagyás

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

1
2
3
4
5
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

1
2
3
4
5
6
7
8
9
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

1
2
3
4
5
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: 2022-01-10 11:51:41