Függvények kezelése
Feladat (f06)
Írjunk nyelvtant egy egyszerű programnyelvhez.
A programnyelv egyszerű kifejezéseket értékel ki, amelyekben é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
operátor,
- a függvényhívás nulla vagy több bemenő módú argumentummal, és
- az angol ábécé betűjével kezdődő, a továbbiakban aláhúzást, számot és/vagy az angol ábécé betűit tartalmazó azonosító mint változó (amiknek kezdetben 0 az értéke).
A számológép soronként olvas és értékel ki.
- Egy sorban egy kifejezés vagy függvénydefiníció szerepel.
- Ha a sor egy azonosítóval és az
=
tokennel lezdődik, akkor az =
után szerplő kifejezés értékét el kell menteni a megfelelő memóriarekeszbe.
- A függvénydefiníció a
function
tokennel kezdődik, majd függvénynév, pereméterlista, returns
és a kifejezés.
- 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 | function answer() returns 42
answer()
function add(x, y) returns x + y
add (24, 18)
6 + 12 + 24
6 * 7
6 + 6 * 6
6 * (3 + 4)
84 / 2
12 + (3 * (25 / 5)) - 9 + (2*3*4)
6 + 2*6 + 2^2*6
A = add ( (5 + 18 / 9 - 1) ^ 2 , abs -12 / 2 )
A * 3 / (1 - -2)
# Komment
M = A / 2 / 3
M * (M - 1)
|
Ki lehet indulni az előző órai megoldásból is, de használható egy másik AST megvalósítás is:
A nyelvtan
Functions.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
68
69
70
71
72
73
74
75
76
77 | grammar Functions;
options {
language = Java;
}
@header {
import java.util.ArrayList;
import java.util.List;
}
@members {
public static void main(String[] args) throws Exception {
FunctionsLexer lex = new FunctionsLexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream (lex);
FunctionsParser parser = new FunctionsParser(tokens);
parser.start(args.length > 1 && "--generate".equals(args[1]));
}
}
start [ boolean genSrc ]
@init{ ast.Program p = new ast.Program(); }
@after{ if (genSrc) {
System.out.println(p);
} else {
p.evaluate();
}
}
: ((line[p] { p.addLine($line.node); } )? COMMENT? LF)* EOF
;
line [ ast.Program p ] returns [ ast.Line node ]
: expr { $node = new ast.Line($p, $expr.node); }
| MEM '=' expr { $node = new ast.Line($p, $expr.node, $MEM.text); }
;
expr returns [ ast.Expression node ]
: fstop=addop { $node = $fstop.node; } (OPADD nxtop=addop {
$node = new ast.Binary($OPADD.text, $node, $nxtop.node);
})*
;
addop returns [ ast.Expression node ]
: fstop=mulop { $node = $fstop.node; } (OPMUL nxtop=mulop {
$node = new ast.Binary($OPMUL.text, $node, $nxtop.node);
})*
;
mulop returns [ ast.Expression node ]
: fstop=fct { $node = $fstop.node; } (OPPWR nxtop=mulop {
$node = new ast.Binary($OPPWR.text, $node, $nxtop.node);
})?
;
fct returns [ ast.Expression node ]
: SZAM { $node = new ast.Const($SZAM.text); }
| LPAR expr { $node = new ast.Parens($expr.node); } RPAR
| OPADD fct { $node = new ast.Unary($OPADD.text, $fct.node); }
| OPABS LPAR expr { $node = new ast.Unary($OPABS.text, $expr.node); } RPAR
| MEM { $node = new ast.Memory($MEM.text); }
| OPMINMAX LPAR fstop=expr { $node = new ast.MultiArg($OPMINMAX.text, $fstop.node); }
(OPLST nxtop=expr { ((ast.MultiArg)$node).addArgument($nxtop.node); }) * RPAR
;
LF : '\n' ;
WS : [ \t\r]+ ->skip ;
SZAM : [0-9]+('.' [0-9]+)? ;
OPADD : '+' | '-' ;
OPMUL : '*' | '/' ;
OPPWR : '^' ;
OPABS : 'abs' ;
OPMINMAX : 'min' | 'max' ;
OPLST : ',' ;
LPAR : '(' ;
RPAR : ')' ;
MEM : 'M' ;
COMMENT : '#' (~[\n])* ->skip ;
|
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
30 | package ast;
import java.util.ArrayList;
import java.util.List;
public class Program {
private List<Line> lines = new ArrayList<Line>();
private double memory;
public void addLine(Line l) {
lines.add(l);
}
public void setMemory(double val) {
memory = val;
}
public double getMemory() {
return memory;
}
public void evaluate() {
for (Line l: lines) {
l.evaluate();
}
}
public String toString() {
StringBuilder str = new StringBuilder();
for (Line l: lines) {
str.append(l.toString());
}
return str.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
23
24
25
26
27
28
29 | package ast;
public class Line {
private String assignee = null;
private Expression expr = null;
private Program program = null;
public Line(Program p, Expression e) {
this.program = p;
this.expr = e;
}
public Line(Program p, Expression e, String a) {
this.program = p;
this.expr = e;
this.assignee = a;
}
public void evaluate() {
double value = expr.evaluate(this.program);
if (this.assignee != null) {
program.setMemory(value);
}
System.out.println(value);
}
public String toString() {
if (assignee != null) {
return assignee + " = " + expr.toString() + "\n";
}
return expr.toString() + "\n";
}
}
|
A kifejezések AST node-jainak ősosztálya
Expression.java
| package ast;
public abstract class Expression {
public abstract double evaluate(Program p);
}
|
''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 p) {
return value;
}
public String toString() {
return literal;
}
}
|
Memory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | package ast;
public class Memory extends Expression {
private String id;
public Memory(String id) {
this.id = id;
}
public double evaluate(Program p) {
return p.getMemory();
}
public String toString() {
return id;
}
}
|
Unáris kifejezések node-jai
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 p) {
return exp.evaluate(p);
}
public String toString() {
return "(" + exp.toString() + ")";
}
}
|
Unary.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 | package ast;
public class Unary extends Expression {
private enum UnaryOperator {
PLS("+"), NEG("-"), ABS("abs");
private String text;
private UnaryOperator(String text) {
this.text = text;
}
public static UnaryOperator getUnaryOperator(String text) {
for (UnaryOperator b : UnaryOperator.values()) {
if (b.toString().equals(text)) {
return b;
}
}
return null;
}
public String toString() {
return this.text;
}
}
private UnaryOperator op = null;
private Expression node;
public Unary(String op, Expression e) {
this.op = UnaryOperator.getUnaryOperator(op);
this.node = e;
}
public double evaluate(Program p) {
double val = this.node.evaluate(p);
switch (this.op) {
case PLS: return val;
case NEG: return -val;
case ABS: return (val < 0) ? -val : val;
}
return 0.0;
}
public String toString() {
if (this.op == UnaryOperator.ABS) {
return this.op.toString() + "(" + this.node.toString() + ")";
} else {
return this.op.toString() + this.node.toString();
}
}
}
|
Bináris kifejezések node-jai
Binary.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 | package ast;
public class Binary extends Expression {
private enum BinaryOperator {
ADD("+"), SUB("-"), MUL("*"), DIV("/"), PWR("^");
private String text;
private BinaryOperator(String text) {
this.text = text;
}
public static BinaryOperator getBinaryOperator(String text) {
for (BinaryOperator b : BinaryOperator.values()) {
if (b.toString().equals(text)) {
return b;
}
}
return null;
}
public String toString() {
return this.text;
}
}
private BinaryOperator op = null;
private Expression lhsNode = null;
private Expression rhsNode = null;
public Binary(String op, Expression lhs, Expression rhs) {
this.op = BinaryOperator.getBinaryOperator(op);
this.lhsNode = lhs;
this.rhsNode = rhs;
}
public double evaluate(Program p) {
double lhs = this.lhsNode.evaluate(p);
double rhs = this.rhsNode.evaluate(p);
switch (this.op) {
case ADD: return lhs + rhs;
case SUB: return lhs - rhs;
case MUL: return lhs * rhs;
case DIV: return lhs / rhs;
case PWR: return Math.pow(lhs, rhs);
}
return 0.0;
}
public String toString() {
return lhsNode.toString() + " " + op.toString() + " " + rhsNode.toString();
}
}
|
Többargumentumú kifejezések node-jai
MultiArg.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
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 | package ast;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class MultiArg extends Expression {
private enum MultiArgOperator {
MIN("min"), MAX("max");
private String text;
private MultiArgOperator(String text) {
this.text = text;
}
public static MultiArgOperator getMultiArgOperator(String text) {
for (MultiArgOperator b : MultiArgOperator.values()) {
if (b.toString().equals(text)) {
return b;
}
}
return null;
}
public String toString() {
return this.text;
}
}
private MultiArgOperator op = null;
private List<Expression> arguments;
public MultiArg(String op, Expression first) {
this.op = MultiArgOperator.getMultiArgOperator(op);
arguments = new ArrayList<Expression>();
arguments.add(first);
}
public void addArgument(Expression arg) {
arguments.add(arg);
}
public double evaluate(Program prog) {
Iterator<Expression> args = arguments.iterator();
double retval = args.next().evaluate(prog);
while (args.hasNext()) {
switch (this.op) {
case MIN:
retval = Math.min(retval, args.next().evaluate(prog));
break;
case MAX:
retval = Math.max(retval, args.next().evaluate(prog));
break;
}
}
return retval;
}
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();
}
}
|
A nyelvtant kicsit át kell alakítani.
Egyrészt a szimpla memória helyett most már azonosítókat fogunk használni (az AST-ben ezt már előkészítettük).
Másrészt az abs
operátor lesz, tehát a eltűnik a kötelező zárójel, és összevonható az előjel operátorokkal.
Harmadrészt a min
és max
eltűnnek, helyettük viszont megjelenik a függvényhívás, külön argumentumlistával.
Ehhez kapcsolódóan pedig bejön a függvénydeklaráció lehetősége, formális paraméterlistával (ami tulajdonképpen azonosítók sorozata), visszatérési értéket számoló kifejezéssel.
A nyelvtan
Functions.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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 | grammar Functions;
options {
language = Java;
}
@header {
import java.util.ArrayList;
import java.util.List;
}
@members {
public static void main(String[] args) throws Exception {
FunctionsLexer lex = new FunctionsLexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream (lex);
FunctionsParser parser = new FunctionsParser(tokens);
parser.start(args.length > 1 && "--generate".equals(args[1]));
}
}
start [ boolean genSrc ]
@init{ ast.Program p = new ast.Program(); }
@after{ if (genSrc) {
System.out.println(p);
} else {
p.evaluate();
}
}
: ((line[p] { p.addLine($line.node); } )? LF)* EOF
;
line [ ast.Program p ] returns [ ast.Line node ]
: expr { $node = new ast.Line($p, $expr.node); }
| ID '=' expr { $node = new ast.Line($p, $expr.node, $ID.text); }
| function { $p.addFunction($function.node); $node = new ast.Line($p, $function.node); }
;
function returns [ ast.Function node ]
: KW_FUNC fname=ID LPAR parlist RPAR KW_RET expr {
$node = new ast.Function($fname.text, $parlist.list, $expr.node);
}
;
parlist returns [ List<String> list ]
: { $list = new ArrayList<String>(); }
( fstpar=ID { $list.add($fstpar.text); }
( OPLST nxtpar=ID { $list.add($nxtpar.text); } )*
)?
;
expr returns [ ast.Expression node ]
: fstop=addop { $node = $fstop.node; } (OPADD nxtop=addop {
$node = new ast.Binary($OPADD.text, $node, $nxtop.node);
})*
;
addop returns [ ast.Expression node ]
: fstop=mulop { $node = $fstop.node; } (OPMUL nxtop=mulop {
$node = new ast.Binary($OPMUL.text, $node, $nxtop.node);
})*
;
mulop returns [ ast.Expression node ]
: fstop=fct { $node = $fstop.node; } (OPPWR nxtop=mulop {
$node = new ast.Binary($OPPWR.text, $node, $nxtop.node);
})?
;
fct returns [ ast.Expression node ]
: SZAM { $node = new ast.Const($SZAM.text); }
| LPAR expr { $node = new ast.Parens($expr.node); } RPAR
| op=(OPADD|OPABS) fct { $node = new ast.Unary($op.text, $fct.node); }
| ID { $node = new ast.Memory($ID.text); }
| ID LPAR arglist RPAR { $node = new ast.FunctionCall($ID.text, $arglist.list); }
;
arglist returns [ List<ast.Expression> list ]
: { $list = new ArrayList<ast.Expression>(); }
( fstarg=expr { $list.add($fstarg.node); }
( OPLST nxtarg=expr { $list.add($nxtarg.node); } )*
)?
;
LF : '\n' ;
WS : [ \t\r]+ ->skip ;
KW_FUNC : 'function' ;
KW_RET : 'returns' ;
SZAM : [0-9]+('.' [0-9]+)? ;
OPADD : '+' | '-' ;
OPMUL : '*' | '/' ;
OPPWR : '^' ;
OPABS : 'abs' ;
OPLST : ',' ;
LPAR : '(' ;
RPAR : ')' ;
ID : [A-Za-z][_0-9A-Za-z]* ;
COMMENT : '#' (~[\n])* ->skip ;
|
Az AST programot és sort reprezentáló node-jaiban két fontosabb változás lesz.
Az egyik, hogy megjelennek a függvények (függvénydeklarációk), amiket külön, kereshető módon is nyilván kell tartanunk.
A másik, hogy a függvények paraméterlistájában deklarált paraméterek lokális azonosítóként fognak működni, tehát nem elég a globális változókat nyilvántartani.
(Ezt egyelőre nem bonyolítjuk túl, mert a nyelvünk még gyakorlatilag alkalmatlan a rekurzióra: csak végtelen rekurziót lehetne csinálni benne.)
A ''Főprogram'', a sorok és a függvények 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
30
31
32
33
34
35
36
37
38
39 | package ast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Program {
private List<Line> lines = new ArrayList<Line>();
private Map<String, Function> functions = new HashMap<String, Function>();
private Map<String, Double> variables = new HashMap<String, Double>();
public void addLine(Line l) {
lines.add(l);
}
public void addFunction(Function f) {
functions.put(f.getFname(), f);
}
public Function getFunction(String name) {
return functions.get(name);
}
public void setVariable(String name, double val) {
variables.put(name, new Double(val));
}
public double getVariable(String name) {
return variables.get(name).doubleValue();
}
public void evaluate() {
for (Line l: lines) {
l.evaluate();
}
}
public String toString() {
StringBuilder str = new StringBuilder();
for (Line l: lines) {
str.append(l.toString());
}
return str.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
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | package ast;
public class Line {
private String assignee = null;
private Expression expr = null;
private Function fndecl = null;
private Program program = null;
public Line(Program p, Expression e) {
this.program = p;
this.expr = e;
}
public Line(Program p, Expression e, String a) {
this.program = p;
this.expr = e;
this.assignee = a;
}
public Line(Program p, Function f) {
this.program = p;
this.fndecl = f;
}
public void evaluate() {
if (expr == null) {
return;
}
double value = expr.evaluate(this.program, null);
if (this.assignee != null) {
program.setVariable(this.assignee, value);
}
System.out.println(value);
}
public String toString() {
if (fndecl != null) {
return fndecl.toString() + "\n";
}
if (assignee != null) {
return assignee + " = " + expr.toString() + "\n";
}
return expr.toString() + "\n";
}
}
|
Function.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
30
31
32
33
34
35
36
37
38
39
40
41
42 | package ast;
import java.util.Iterator;
import java.util.List;
public class Function {
private String fname;
private List<String> pnames;
private Expression exp;
public Function(String fname, List<String> pnames, Expression exp) {
this.fname = fname;
this.pnames = pnames;
this.exp = exp;
}
public String getFname() {
return fname;
}
public List<String> getParameterNames() {
return pnames;
}
public Expression getExpr() {
return exp;
}
public String toString() {
StringBuilder str = new StringBuilder();
str.append("function ");
str.append(fname);
str.append("(");
Iterator<String> parit = pnames.iterator();
if (parit.hasNext()) {
str.append(parit.next());
while (parit.hasNext()) {
str.append(", ");
str.append(parit.next());
}
}
str.append(") returns ");
str.append(exp.toString());
return str.toString();
}
}
|
Az előbb említett lokális azonosítók miatt a kifejezések kiértékeléséhez már nem egy, hanem két garnitúra változóra lesz szükség.
A kifejezések AST node-jainak ősosztálya
Expression.java
| package ast;
import java.util.Map;
public abstract class Expression {
public abstract double evaluate(Program p, Map<String, Double> locals);
}
|
A konstans csak formailag változik (lásd fentebb), a memóriaérték keresése viszont érdemben:
ha valamit nem találunk a lokális változók között (vagy nincsenek lokális változóink, mert nem függvényben vagyunk), akkor megnézzük a globális változók között.
''Levelek'', 0 operandusú kifejezések
Const.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | package ast;
import java.util.Map;
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 p, Map<String, Double> locals) {
return this.value;
}
public String toString() {
return literal;
}
}
|
Memory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | package ast;
import java.util.Map;
public class Memory extends Expression {
private String id;
public Memory(String id) {
this.id = id;
}
public double evaluate(Program p, Map<String, Double> locals) {
if (locals != null && locals.containsKey(this.id)) {
return locals.get(this.id).doubleValue();
}
return p.getVariable(this.id);
}
public String toString() {
return id;
}
}
|
A zárójelezés szintén csak formailag változik, az abs
-nál viszont már nincs szükség a zárójelekre.
Unáris kifejezések node-jai
Parens.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | package ast;
import java.util.Map;
public class Parens extends Expression {
private Expression exp;
public Parens(Expression exp) {
this.exp = exp;
}
public double evaluate(Program p, Map<String, Double> locals) {
return exp.evaluate(p, locals);
}
public String toString() {
return "(" + exp.toString() + ")";
}
}
|
Unary.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | package ast;
import java.util.Map;
public class Unary extends Expression {
private enum UnaryOperator {
PLS("+"), NEG("-"), ABS("abs");
private String text;
private UnaryOperator(String text) {
this.text = text;
}
public static UnaryOperator getUnaryOperator(String text) {
for (UnaryOperator b : UnaryOperator.values()) {
if (b.toString().equals(text)) {
return b;
}
}
return null;
}
public String toString() {
return this.text;
}
}
private UnaryOperator op = null;
private Expression node;
public Unary(String op, Expression e) {
this.op = UnaryOperator.getUnaryOperator(op);
this.node = e;
}
public double evaluate(Program p, Map<String, Double> locals) {
double val = this.node.evaluate(p, locals);
switch (this.op) {
case PLS: return val;
case NEG: return -val;
case ABS: return (val < 0) ? -val : val;
}
return 0.0;
}
public String toString() {
return this.op.toString() + ((this.op == UnaryOperator.ABS) ? " " : "") + this.node.toString();
}
}
|
A bináris kifejezéseken sem kell sokat alakítani.
Bináris kifejezések node-jai
Binary.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 | package ast;
import java.util.Map;
public class Binary extends Expression {
private enum BinaryOperator {
ADD("+"), SUB("-"), MUL("*"), DIV("/"), PWR("^");
private String text;
private BinaryOperator(String text) {
this.text = text;
}
public static BinaryOperator getBinaryOperator(String text) {
for (BinaryOperator b : BinaryOperator.values()) {
if (b.toString().equals(text)) {
return b;
}
}
return null;
}
public String toString() {
return this.text;
}
}
private BinaryOperator op = null;
private Expression lhsNode = null;
private Expression rhsNode = null;
public Binary(String op, Expression lhs, Expression rhs) {
this.op = BinaryOperator.getBinaryOperator(op);
this.lhsNode = lhs;
this.rhsNode = rhs;
}
public double evaluate(Program p, Map<String, Double> locals) {
double lhs = this.lhsNode.evaluate(p, locals);
double rhs = this.rhsNode.evaluate(p, locals);
switch (this.op) {
case ADD: return lhs + rhs;
case SUB: return lhs - rhs;
case MUL: return lhs * rhs;
case DIV: return lhs / rhs;
case PWR: return Math.pow(lhs, rhs);
}
return 0.0;
}
public String toString() {
return lhsNode.toString() + " " + op.toString() + " " + rhsNode.toString();
}
}
|
A többargumentumú kifejezések viszont eltűnnek, helyettük bejön a függvényhívás.
Kiértékeléskor először sorban kiértékeljük az argumentumokat és értéket adunk a megfelelő lokális változóknak, majd kiértékeljük a függvényhívást.
Függvényhívás kifejezések node-jai
FunctionCall.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
30
31
32
33
34
35
36
37
38
39
40
41
42 | package ast;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class FunctionCall extends Expression {
private String fname;
private List<Expression> args;
public FunctionCall(String fname, List<Expression> args) {
this.fname = fname;
this.args = args;
}
public double evaluate(Program p, Map<String, Double> locals) {
Function f = p.getFunction(this.fname);
Map<String, Double> params = new HashMap<String, Double>();
Iterator<String> parameters = f.getParameterNames().iterator();
Iterator<Expression> arguments = args.iterator();
while (parameters.hasNext() && arguments.hasNext()) {
String pname = parameters.next();
Expression e = arguments.next();
params.put(pname, new Double(e.evaluate(p, locals)));
}
return f.getExpr().evaluate(p, params);
}
public String toString() {
StringBuilder str = new StringBuilder();
str.append(fname);
str.append("(");
Iterator<Expression> argit = args.iterator();
if (argit.hasNext()) {
str.append(argit.next().toString());
while (argit.hasNext()) {
str.append(", ");
str.append(argit.next().toString());
}
}
str.append(")");
return str.toString();
}
}
|
Utolsó frissítés:
2023-02-02 13:09:36