├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
├── java
│ ├── appel
│ │ ├── ch01
│ │ │ ├── AssignStm.java
│ │ │ ├── CompoundStm.java
│ │ │ ├── EseqExp.java
│ │ │ ├── Exp.java
│ │ │ ├── ExpList.java
│ │ │ ├── IdExp.java
│ │ │ ├── IntAndTable.java
│ │ │ ├── Interp.java
│ │ │ ├── LastExpList.java
│ │ │ ├── NumExp.java
│ │ │ ├── OpExp.java
│ │ │ ├── PairExpList.java
│ │ │ ├── PrintStm.java
│ │ │ ├── Stm.java
│ │ │ └── Table.java
│ │ └── ch07
│ │ │ ├── AstIRTransform.java
│ │ │ ├── Atom.java
│ │ │ ├── Binop.java
│ │ │ ├── Env.java
│ │ │ ├── Exp.java
│ │ │ ├── FunClass.java
│ │ │ ├── FunInfo.java
│ │ │ ├── FunLocal.java
│ │ │ ├── IRException.java
│ │ │ ├── IRState.java
│ │ │ ├── Prog.java
│ │ │ ├── Relop.java
│ │ │ ├── Symbol.java
│ │ │ ├── SymbolSet.java
│ │ │ ├── Type.java
│ │ │ ├── TypeDef.java
│ │ │ └── Unop.java
│ └── util
│ │ ├── FieldMTable.java
│ │ └── Table.java
└── sablecc
│ ├── ch02.grammar
│ └── ch03.grammar
└── test
├── java
├── appel
│ ├── ch01
│ │ └── TestInterp.java
│ ├── ch02
│ │ └── TestLexer.java
│ └── ch03
│ │ ├── ASTCleaner.java
│ │ ├── ExprEvaluator.java
│ │ ├── ParseFailed.java
│ │ ├── TestLexer.java
│ │ └── TestParser.java
└── util
│ ├── TestFieldMTable.java
│ └── TestTable.java
└── resources
└── log4testng.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | .classpath
3 | .settings
4 | /target
5 | /test-output
6 | /tiger
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project will contain implementations for the fjava language from [Jason Hickey's CS134b class](http://main.metaprl.org/jyh/classes/cs134/cs134b/2002/), a modified version of MiniJava from Andrew Appel's [Modern Compiler Implementation in Java](http://www.cs.princeton.edu/~appel/modern/java/). Differences include the removal declaration modifiers (public, static, etc.) and the addition of higher-order functions and a completely different IR. See [lab #2](http://main.metaprl.org/jyh/classes/cs134/cs134b/2002/labs/lab2/lab2.pdf) for a complete list of differences between the languages.
2 |
3 | CI can be found on [CloudBees](https://jlee.ci.cloudbees.com/job/appel-exercises/).
4 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | appel
5 | appel-exercises
6 | jar
7 | 1-SNAPSHOT
8 | Appel Exercises
9 | Exercises from Appel's Modern Compiler Implementation in Java
10 | https://github.com/jonjlee/appel-exercises
11 |
12 |
13 | https://github.com/jonjlee/appel-exercises
14 | scm:git:git@github.com:jonjlee/appel-exercises.git
15 | scm:git:git@github.com:jonjlee/appel-exercises.git
16 |
17 |
18 |
19 |
20 |
21 | org.mockito
22 | mockito-all
23 | 1.9.0
24 | test
25 |
26 |
27 | org.testng
28 | testng
29 | 6.1.1
30 | test
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.apache.maven.plugins
38 | maven-compiler-plugin
39 | 2.3.2
40 |
41 | 1.6
42 | 1.6
43 |
44 |
45 |
46 |
47 | org.codehaus.mojo
48 | sablecc-maven-plugin
49 |
50 |
51 | generate-sources
52 |
53 | generate
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/AssignStm.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class AssignStm implements Stm {
4 | String id; Exp exp;
5 | AssignStm(String i, Exp e) {id=i; exp=e;}
6 |
7 | @Override public Table eval(Table t) {
8 | IntAndTable expVal = exp.eval(t);
9 | return new Table(id, expVal.i, expVal.t);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/CompoundStm.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class CompoundStm implements Stm {
4 | Stm stm1, stm2;
5 | CompoundStm(Stm s1, Stm s2) {stm1=s1; stm2=s2;}
6 |
7 | @Override public Table eval(Table t) {
8 | Table stm1Ret= stm1.eval(t);
9 | Table stm2Ret = stm2.eval(stm1Ret);
10 | return stm2Ret;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/EseqExp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class EseqExp implements Exp {
4 | Stm stm; Exp exp;
5 | EseqExp(Stm s, Exp e) {stm=s; exp=e;}
6 |
7 | @Override public IntAndTable eval(Table t) {
8 | Table stmRet = stm.eval(t);
9 | IntAndTable expVal = exp.eval(stmRet);
10 | return expVal;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/Exp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public interface Exp {
4 | IntAndTable eval(Table t);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/ExpList.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class ExpList {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/IdExp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class IdExp implements Exp {
4 | String id;
5 | IdExp(String i) {id=i;}
6 |
7 | @Override public IntAndTable eval(Table t) {
8 | return new IntAndTable(lookup(t, id), t);
9 | }
10 |
11 | private int lookup(Table t, String id) {
12 | if (t == null) {
13 | throw new IllegalArgumentException("Variable " + id + " not found in table");
14 | }
15 | if (t.id.equals(id)) {
16 | return t.value;
17 | }
18 | return lookup(t.tail, id);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/IntAndTable.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class IntAndTable {
4 | int i; Table t;
5 | public IntAndTable(int ii, Table tt) {i=ii; t=tt;}
6 | }
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/Interp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 |
4 | public class Interp {
5 |
6 | /** Return number of expressions in an ExpList */
7 | public static int numargs(ExpList exps) {
8 | if (exps instanceof LastExpList) {
9 | return 1;
10 | }
11 | return 1 + numargs(((PairExpList) exps).tail);
12 | }
13 |
14 | /** Helper method to creates an ExpList from the arg list */
15 | public static ExpList explist(Exp firstArg, Exp... otherArgs) {
16 | if (otherArgs.length == 0) {
17 | return new LastExpList(firstArg);
18 | }
19 | ExpList tail = new LastExpList(otherArgs[otherArgs.length-1]);
20 | for (int i = otherArgs.length-2; i >= 0; i--) {
21 | tail = new PairExpList(otherArgs[i], tail);
22 | }
23 | return new PairExpList(firstArg, tail);
24 | }
25 |
26 | /** Return the maximum number of args provided to any print statement occurring in s */
27 | public static int maxargs(Stm s) {
28 | if (s instanceof PrintStm) {
29 | PrintStm printStm = (PrintStm) s;
30 | return Math.max(numargs(printStm.exps), maxargs(printStm.exps));
31 | } else if (s instanceof CompoundStm) {
32 | CompoundStm cs = (CompoundStm) s;
33 | return Math.max(maxargs(cs.stm1), maxargs(cs.stm2));
34 | } else if (s instanceof AssignStm) {
35 | return maxargs(((AssignStm) s).exp);
36 | }
37 | return 0;
38 | }
39 |
40 | /** maxarg helper for recursing into ExpLists, which can contain Exp objects that contain statements */
41 | private static int maxargs(ExpList exps) {
42 | if (exps instanceof LastExpList) {
43 | return maxargs(((LastExpList) exps).head);
44 | }
45 | PairExpList pairExpList = (PairExpList) exps;
46 | return Math.max(maxargs(pairExpList.head), maxargs(pairExpList.tail));
47 | }
48 |
49 | /** maxarg helper for recursing into Exp objects, two subclasses of which can contain statements */
50 | private static int maxargs(Exp e) {
51 | if (e instanceof EseqExp) {
52 | return maxargs(((EseqExp) e).stm);
53 | } else if (e instanceof OpExp) {
54 | OpExp opExp = (OpExp) e;
55 | return Math.max(maxargs(opExp.left), maxargs(opExp.right));
56 | }
57 | return 0;
58 | }
59 |
60 | /** "Interprets" an program given an AST composed of our limited node types */
61 | public static void interp(Stm s) {
62 | s.eval(null);
63 | }
64 | }
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/LastExpList.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class LastExpList extends ExpList {
4 |
5 | Exp head;
6 | public LastExpList(Exp h) {head=h;}
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/NumExp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class NumExp implements Exp {
4 | int num;
5 | NumExp(int n) {num=n;}
6 |
7 | @Override public IntAndTable eval(Table t) {
8 | return new IntAndTable(num, t);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/OpExp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class OpExp implements Exp {
4 | Exp left, right; int oper;
5 | final static int Plus=1,Minus=2,Times=3,Div=4;
6 | OpExp(Exp l, int o, Exp r) {left=l; oper=o; right=r;}
7 |
8 | @Override public IntAndTable eval(Table t) {
9 | IntAndTable exp1Val = left.eval(t);
10 | IntAndTable exp2Val = right.eval(exp1Val.t);
11 | if (oper == OpExp.Plus) {
12 | return new IntAndTable(exp1Val.i + exp2Val.i, exp2Val.t);
13 | } else if (oper == OpExp.Minus) {
14 | return new IntAndTable(exp1Val.i - exp2Val.i, exp2Val.t);
15 | } else if (oper == OpExp.Times) {
16 | return new IntAndTable(exp1Val.i * exp2Val.i, exp2Val.t);
17 | } else if (oper == OpExp.Div) {
18 | return new IntAndTable(exp1Val.i / exp2Val.i, exp2Val.t);
19 | }
20 | throw new UnsupportedOperationException("Unknown operator " + oper);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/PairExpList.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class PairExpList extends ExpList {
4 | Exp head; ExpList tail;
5 | public PairExpList(Exp h, ExpList t) {head=h; tail=t;}
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/PrintStm.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class PrintStm implements Stm {
4 | ExpList exps;
5 | PrintStm(ExpList e) {exps=e;}
6 |
7 | @Override public Table eval(Table t) {
8 | return interpPrint(exps, t);
9 | }
10 |
11 | private Table interpPrint(ExpList exps, Table t) {
12 | if (exps instanceof LastExpList) {
13 | IntAndTable expVal = ((LastExpList) exps).head.eval(t);
14 | System.out.print(expVal.i);
15 | return expVal.t;
16 | } else {
17 | PairExpList pairExpList = (PairExpList) exps;
18 | IntAndTable expVal = pairExpList.head.eval(t);
19 | System.out.print(expVal.i);
20 | return interpPrint(pairExpList.tail, expVal.t);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/Stm.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public interface Stm {
4 | Table eval(Table t);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch01/Table.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | public class Table {
4 | String id; int value; Table tail;
5 | Table(String i, int v, Table t) {id=i; value=v; tail=t;}
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/AstIRTransform.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public class AstIRTransform {
4 | public static Exp transform() {
5 | return null;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Atom.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public abstract class Atom {
4 | public static class Unit extends Atom {};
5 | public static class Nil extends Atom {};
6 |
7 | public static class Bool extends Atom {
8 | final boolean v;
9 | public Bool(boolean v) { this.v = v; }
10 | };
11 |
12 | public static class Char extends Atom {
13 | final char v;
14 | public Char(char v) { this.v = v; }
15 | };
16 |
17 | public static class Int extends Atom {
18 | final int v;
19 | public Int(int v) { this.v = v; }
20 | };
21 |
22 | public static class Float extends Atom {
23 | final float v;
24 | public Float(float v) { this.v = v; }
25 | };
26 |
27 | public static class Var extends Atom {
28 | final Symbol.Var name;
29 | public Var(Symbol.Var name) { this.name = name; }
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Binop.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public enum Binop {
4 | // integers
5 | AddIntOp,
6 | SubIntOp,
7 | MulIntOp,
8 | DivIntOp,
9 | RemIntOp,
10 | AndIntOp,
11 | OrIntOp,
12 | LslIntOp, // Logical shift left <<
13 | LsrIntOp, // Logical shift right >>
14 | AsrIntOp, // Arithmetic shift right >>>
15 | XorIntOp,
16 |
17 | // floats
18 | AddFloatOp,
19 | SubFloatOp,
20 | MulFloatOp,
21 | DivFloatOp,
22 | RemFloatOp
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Env.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import java.util.Map;
4 | import appel.ch07.Symbol.Var;
5 | import util.Table;
6 |
7 | public class Env {
8 |
9 | public static final Table BaseTypes;
10 | public static final Env Empty;
11 |
12 | static {
13 | BaseTypes = Table. create()
14 | .put(new Symbol.TypeVar("void", 0), Type.Unit)
15 | .put(new Symbol.TypeVar("boolean", 0), Type.Bool)
16 | .put(new Symbol.TypeVar("char", 0), Type.Char)
17 | .put(new Symbol.TypeVar("int", 0), Type.Int)
18 | .put(new Symbol.TypeVar("float", 0), Type.Float)
19 | .put(new Symbol.TypeVar("String", 0), Type.String);
20 |
21 | Empty = new Env(
22 | Table. create(),
23 | BaseTypes,
24 | Table. create(),
25 | Table.> create(),
26 | null,
27 | null,
28 | null,
29 | IRState.objectType);
30 | }
31 |
32 | public final Table types;
33 | public final Table base;
34 | public final Table vars;
35 | public final Table> funs;
36 | public final Type ret;
37 | public final Map cls;
38 | public final Symbol.Var brk;
39 | public final Type obj;
40 | public Env(Table types, Table base, Table vars, Table> funs,
41 | Type ret, Map cls, Var brk, Type obj) {
42 | this.types = types;
43 | this.base = base;
44 | this.vars = vars;
45 | this.funs = funs;
46 | this.ret = ret;
47 | this.cls = cls;
48 | this.brk = brk;
49 | this.obj = obj;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Exp.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import appel.ch07.Symbol.InternalVar;
4 | import appel.ch07.Symbol.TypeVar;
5 | import appel.ch07.Symbol.Var;
6 |
7 | public abstract class Exp {
8 |
9 | public static class LetFuns extends Exp {
10 | public final FunLocal[] funs;
11 | public final Exp exp;
12 | public LetFuns(FunLocal[] funs, Exp exp) {
13 | this.funs = funs;
14 | this.exp = exp;
15 | }
16 | };
17 |
18 | /** Simple constants and arithmetic.
19 | * let v = a in e
20 | * let v = unop a in e
21 | * let v = a1 binop a2 in e
22 | * let v = a1 relop a2 in e
23 | */
24 | public static class LetAtom extends Exp {
25 | public final Symbol.Var v;
26 | public final Type type;
27 | public final Atom atom;
28 | public final Exp exp;
29 | public LetAtom(Var v, Type type, Atom atom, Exp exp) {
30 | this.v = v;
31 | this.type = type;
32 | this.atom = atom;
33 | this.exp = exp;
34 | }
35 | };
36 | public static class LetUnop extends Exp {
37 | public final Symbol.Var v;
38 | public final Type type;
39 | public final Unop op;
40 | public final Atom atom;
41 | public final Exp exp;
42 | public LetUnop(Var v, Type type, Unop op, Atom atom, Exp exp) {
43 | this.v = v;
44 | this.type = type;
45 | this.op = op;
46 | this.atom = atom;
47 | this.exp = exp;
48 | }
49 | };
50 | public static class LetBinop extends Exp {
51 | public final Symbol.Var v;
52 | public final Type type;
53 | public final Binop op;
54 | public final Atom left;
55 | public final Atom right;
56 | public final Exp exp;
57 | public LetBinop(Var v, Type type, Binop op, Atom left, Atom right, Exp exp) {
58 | this.v = v;
59 | this.type = type;
60 | this.op = op;
61 | this.left = left;
62 | this.right = right;
63 | this.exp = exp;
64 | }
65 | };
66 | public static class LetRelop extends Exp {
67 | public final Symbol.Var v;
68 | public final Type type;
69 | public final Relop op;
70 | public final Atom left;
71 | public final Atom right;
72 | public final Exp exp;
73 | public LetRelop(Var v, Type type, Relop op, Atom left, Atom right, Exp exp) {
74 | this.v = v;
75 | this.type = type;
76 | this.op = op;
77 | this.left = left;
78 | this.right = right;
79 | this.exp = exp;
80 | }
81 | };
82 |
83 | /** Functions
84 | * let v1 : ty = v2(a1, ..., aN) in exp
85 | */
86 | public static class LetApply extends Exp {
87 | public final Symbol.Var v;
88 | public final Type type;
89 | public final Symbol.Var fun;
90 | public final Atom[] args;
91 | public final Exp exp;
92 | public LetApply(Var v, Type type, Var fun, Atom[] args, Exp exp) {
93 | this.v = v;
94 | this.type = type;
95 | this.fun = fun;
96 | this.args = args;
97 | this.exp = exp;
98 | }
99 | };
100 |
101 | /** The self-pointer is passed explicitly when calling a method:
102 | * let v1 : ty = v2[a_this](a1, ..., aN) in exp *)
103 | */
104 | public static class LetApplyMethod extends Exp {
105 | public final Symbol.Var v;
106 | public final Type type;
107 | public final Symbol.Var fun;
108 | public final Atom thisAtom;
109 | public final Atom[] args;
110 | public final Exp exp;
111 | public LetApplyMethod(Var v, Type type, Var fun, Atom thisAtom, Atom[] args, Exp exp) {
112 | this.v = v;
113 | this.type = type;
114 | this.fun = fun;
115 | this.thisAtom = thisAtom;
116 | this.args = args;
117 | this.exp = exp;
118 | }
119 | };
120 |
121 | /** External functions need to have their types specified explicitly.
122 | * let v1 : ty1 = External_fun["f_name", ext_fun_ty](a1, ..., aN) in exp
123 | */
124 | public static class LetExt extends Exp {
125 | public final Symbol.Var v;
126 | public final Type type;
127 | public final String fun;
128 | public final Type extType;
129 | public final Atom[] args;
130 | public final Exp exp;
131 | public LetExt(Var v, Type type, String fun, Type extType, Atom[] args, Exp exp) {
132 | this.v = v;
133 | this.type = type;
134 | this.fun = fun;
135 | this.extType = extType;
136 | this.args = args;
137 | this.exp = exp;
138 | }
139 | };
140 |
141 | /** v(a1, ..., aN) */
142 | public static class TailCall extends Exp {
143 | public final Symbol.Var fun;
144 | public final Atom[] args;
145 | public TailCall(Var fun, Atom[] args) {
146 | this.fun = fun;
147 | this.args = args;
148 | }
149 | };
150 |
151 | /** Return a value from the current function */
152 | public static class Return extends Exp {
153 | public final Atom atom;
154 | public Return(Atom atom) {
155 | this.atom = atom;
156 | }
157 | };
158 |
159 | /** Abort with an error message */
160 | public static class Abort extends Exp {
161 | public final String message;
162 | public Abort(String message) {
163 | this.message = message;
164 | }
165 | };
166 |
167 | /** Conditional */
168 | public static class IfThenElse extends Exp {
169 | public final Atom atom;
170 | public final Exp ifExp;
171 | public final Exp elseExp;
172 | public IfThenElse(Atom atom, Exp ifExp, Exp elseExp) {
173 | this.atom = atom;
174 | this.ifExp = ifExp;
175 | this.elseExp = elseExp;
176 | }
177 | };
178 |
179 | /** IfType (a, ty_name, v, e1, e2):
180 | *
181 | * if a instanceof ty_name then
182 | * let v : ty_name = a in e1
183 | * else
184 | * e2
185 | */
186 | public static class IfType extends Exp {
187 | public final Atom atom;
188 | public final Symbol.TypeVar typeName;
189 | public final Symbol.Var v;
190 | public final Exp ifExp;
191 | public final Exp elseExp;
192 | public IfType(Atom atom, TypeVar type, Var v, Exp ifExp, Exp elseExp) {
193 | this.atom = atom;
194 | this.typeName = type;
195 | this.v = v;
196 | this.ifExp = ifExp;
197 | this.elseExp = elseExp;
198 | }
199 | };
200 |
201 | /** Variable definitions.
202 | * LetVar variables are mutable; they may be modified by assignment.
203 | *
204 | * let v : ty = a in e
205 | * v : ty <- a; e
206 | */
207 | public static class LetVar extends Exp {
208 | public final Symbol.Var v;
209 | public final Type type;
210 | public final Atom atom;
211 | public final Exp exp;
212 | public LetVar(Var v, Type type, Atom atom, Exp exp) {
213 | this.v = v;
214 | this.type = type;
215 | this.atom = atom;
216 | this.exp = exp;
217 | }
218 | };
219 | public static class SetVar extends Exp {
220 | public final Symbol.Var v;
221 | public final Type type;
222 | public final Atom atom;
223 | public final Exp exp;
224 | public SetVar(Var v, Type type, Atom atom, Exp exp) {
225 | this.v = v;
226 | this.type = type;
227 | this.atom = atom;
228 | this.exp = exp;
229 | }
230 | };
231 |
232 | /** Array/pointer operations
233 | * a1[a2] : ty <- a3; e
234 | * let v : ty = a1[a2] in e
235 | */
236 | public static class SetSubscript extends Exp {
237 | public final Atom atom;
238 | public final Atom index;
239 | public final Type type;
240 | public final Atom value;
241 | public final Exp exp;
242 | public SetSubscript(Atom atom, Atom index, Type type, Atom value, Exp exp) {
243 | this.atom = atom;
244 | this.index = index;
245 | this.type = type;
246 | this.value = value;
247 | this.exp = exp;
248 | }
249 | };
250 | public static class LetSubscript extends Exp {
251 | public final Symbol.Var v;
252 | public final Type type;
253 | public final Atom atom;
254 | public final Atom index;
255 | public final Exp exp;
256 | public LetSubscript(Var v, Type type, Atom atom, Atom index, Exp exp) {
257 | this.v = v;
258 | this.type = type;
259 | this.atom = atom;
260 | this.index = index;
261 | this.exp = exp;
262 | }
263 |
264 | };
265 |
266 | /** Record projection
267 | * a1.var : ty <- a2; e
268 | * let v1 : ty = a.v2 in e
269 | */
270 | public static class SetProject extends Exp {
271 | public final Atom atom;
272 | public final Symbol.InternalVar field;
273 | public final Type type;
274 | public final Atom value;
275 | public final Exp exp;
276 | public SetProject(Atom atom, InternalVar field, Type type, Atom value, Exp exp) {
277 | this.atom = atom;
278 | this.field = field;
279 | this.type = type;
280 | this.value = value;
281 | this.exp = exp;
282 | }
283 | };
284 | public static class LetProject extends Exp {
285 | public final Symbol.Var v;
286 | public final Type type;
287 | public final Atom atom;
288 | public final Symbol.InternalVar field;
289 | public final Exp exp;
290 | public LetProject(Var v, Type type, Atom atom, InternalVar field, Exp exp) {
291 | this.v = v;
292 | this.type = type;
293 | this.atom = atom;
294 | this.field = field;
295 | this.exp = exp;
296 | }
297 | };
298 |
299 | /** Heap allocation.
300 | *
301 | * String allocation:
302 | * let v = "s" in e
303 | * Array allocation:
304 | * a1,...,aN are the dimensions, a is the initial value
305 | * let v : ty array = new array[a1]...[aN] of a in e
306 | * Object allocation:
307 | * let v1 : ty = new v2 in e
308 | */
309 | public static class LetString extends Exp {
310 | public final Symbol.Var v;
311 | public final String s;
312 | public final Exp exp;
313 | public LetString(Var v, String s, Exp exp) {
314 | this.v = v;
315 | this.s = s;
316 | this.exp = exp;
317 | }
318 | };
319 | public static class LetArray extends Exp {
320 | public final Symbol.Var v;
321 | public final Type type;
322 | public final Atom[] dimensions;
323 | public final Atom elementType;
324 | public final Exp exp;
325 | public LetArray(Var v, Type type, Atom[] dimensions, Atom elementType, Exp exp) {
326 | this.v = v;
327 | this.type = type;
328 | this.dimensions = dimensions;
329 | this.elementType = elementType;
330 | this.exp = exp;
331 | }
332 | };
333 | public static class LetObject extends Exp {
334 | public final Symbol.Var v;
335 | public final Type type;
336 | public final Symbol.TypeVar objectType;
337 | public final Exp exp;
338 | public LetObject(Var v, Type type, TypeVar objectType, Exp exp) {
339 | this.v = v;
340 | this.type = type;
341 | this.objectType = objectType;
342 | this.exp = exp;
343 | }
344 | };
345 | }
346 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/FunClass.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public enum FunClass {
4 | Global,
5 | Method,
6 | Label
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/FunInfo.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import appel.ch07.Symbol.Var;
4 |
5 | /**
6 | * A function definition has:
7 | * 1. a function classification (global, method, or local)
8 | * 2. the function type
9 | * 3. then formal parameters
10 | * 4. the body
11 | */
12 | public class FunInfo {
13 | public final FunClass funClass;
14 | public final Type type;
15 | public final Symbol.Var[] args;
16 | public final Exp exp;
17 | public FunInfo(FunClass funClass, Type type, Var[] args, Exp exp) {
18 | this.funClass = funClass;
19 | this.type = type;
20 | this.args = args;
21 | this.exp = exp;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/FunLocal.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import appel.ch07.Symbol.Var;
4 |
5 | public class FunLocal {
6 | public final Symbol.Var var;
7 | public final Type type;
8 | public final Symbol.Var[] args;
9 | public final Exp exp;
10 | public FunLocal(Var var, Type type, Var[] args, Exp exp) {
11 | this.var = var;
12 | this.type = type;
13 | this.args = args;
14 | this.exp = exp;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/IRException.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import java.util.Map;
4 | import appel.ch07.Symbol.Var;
5 |
6 | @SuppressWarnings({"serial", "unused"})
7 | public abstract class IRException extends RuntimeException {
8 |
9 | IRException(String message, int line, int pos) {
10 | super(message);
11 | }
12 |
13 | public static class UnboundVar extends IRException {
14 | private final Symbol.Var var;
15 | public UnboundVar(int line, int pos, Var var) {
16 | super("Unbound variable: " + var, line, pos);
17 | this.var = var;
18 | }
19 | }
20 | public static class UnboundType extends IRException {
21 | private final Symbol.Var type;
22 | public UnboundType(int line, int pos, Var type) {
23 | super("Unbound type: " + type, line, pos);
24 | this.type = type;
25 | }
26 | }
27 | public static class UnboundClass extends IRException {
28 | private final Symbol.Var clss;
29 | public UnboundClass(int line, int pos, Var clss) {
30 | super("Unbound class: " + clss, line, pos);
31 | this.clss = clss;
32 | }
33 | }
34 | public static class UnboundLabel extends IRException {
35 | private final Symbol.Var lbl;
36 | public UnboundLabel(int line, int pos, Var lbl) {
37 | super("Unbound label: " + lbl, line, pos);
38 | this.lbl = lbl;
39 | }
40 | }
41 | public static class NotImplemented extends IRException {
42 | NotImplemented(String message, int line, int pos) {
43 | super("Not Implemented: " + message, line, pos);
44 | }
45 | }
46 | public static class InconvertibleTypes extends IRException {
47 | InconvertibleTypes(String message, int line, int pos) {
48 | super(message, line, pos);
49 | }
50 | }
51 | public static class TypeError extends IRException {
52 | TypeError(String message, int line, int pos) {
53 | super(message, line, pos);
54 | }
55 | }
56 | public static class TypeMismatch extends IRException {
57 | private final Type type1, type2;
58 | public TypeMismatch(String message, int line, int pos, Type type1, Type type2) {
59 | super(message, line, pos);
60 | this.type1 = type1;
61 | this.type2 = type2;
62 | }
63 | }
64 | public static class ArityMismatch extends IRException {
65 | private final int arity1, arity2;
66 | public ArityMismatch(String message, int line, int pos, int arity1, int arity2) {
67 | super(message, line, pos);
68 | this.arity1 = arity1;
69 | this.arity2 = arity2;
70 | }
71 | }
72 | public static class StringError extends IRException {
73 | private final String s;
74 |
75 | public StringError(String message, int line, int pos, String s) {
76 | super(message, line, pos);
77 | this.s = s;
78 | }
79 | }
80 | public static class StringIntError extends IRException {
81 | private final String s;
82 | private final int i;
83 | public StringIntError(String message, int line, int pos, String s, int i) {
84 | super(message, line, pos);
85 | this.s = s;
86 | this.i = i;
87 | }
88 | }
89 | public static class StringVarError extends IRException {
90 | private final String s;
91 | private final Symbol.Var var;
92 | public StringVarError(String message, int line, int pos, String s, Var var) {
93 | super(message, line, pos);
94 | this.s = s;
95 | this.var = var;
96 | }
97 | }
98 | public static class StringTypeError extends IRException {
99 | private final String s;
100 | private final Type type;
101 | public StringTypeError(String message, int line, int pos, String s, Type type) {
102 | super(message, line, pos);
103 | this.s = s;
104 | this.type = type;
105 | }
106 | }
107 | public static class StringVarVarError extends IRException {
108 | private final String s;
109 | private final Symbol.Var var1;
110 | private final Symbol.Var var2;
111 | public StringVarVarError(String message, int line, int pos, String s, Var var1, Var var2) {
112 | super(message, line, pos);
113 | this.s = s;
114 | this.var1 = var1;
115 | this.var2 = var2;
116 | }
117 | }
118 | public static class OverloadError extends IRException {
119 | private final Symbol.Var var;
120 | private final Type[] classes;
121 | private final Map args;
122 | public OverloadError(String message, int line, int pos, Var var, Type[] classes, Map args) {
123 | super(message, line, pos);
124 | this.var = var;
125 | this.classes = classes;
126 | this.args = args;
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/IRState.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public class IRState {
4 | public static final Symbol.Var thisVar = new Symbol.Var("this", 0);
5 | public static final Symbol.Var superVar = new Symbol.Var("super", 0);
6 |
7 | public static final Atom.Var thisAtom = new Atom.Var(thisVar);
8 | public static final Atom.Var superAtom = new Atom.Var(superVar);
9 |
10 | public static final String objectName = "FjObject";
11 | public static final Symbol.TypeVar objectVar = new Symbol.TypeVar(objectName, 0);
12 | public static final Type objectType = new Type.Object(objectVar);
13 | public static final Symbol.Var objectInitVar = new Symbol.Var("FjObject$init", 0);
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Prog.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import util.Table;
4 | import appel.ch07.Symbol.Var;
5 |
6 | public class Prog {
7 | public final Table types;
8 | public final SymbolSet absTypes;
9 | public final Table funs;
10 | public final Symbol.Var main;
11 | public final Symbol.Var object;
12 | public Prog(Table types, SymbolSet absTypes, Table funs, Var main, Var object) {
13 | this.types = types;
14 | this.absTypes = absTypes;
15 | this.funs = funs;
16 | this.main = main;
17 | this.object = object;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Relop.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public enum Relop {
4 | // integers
5 | EqIntOp,
6 | NeqIntOp,
7 | LeIntOp,
8 | LtIntOp,
9 | GtIntOp,
10 | GeIntOp,
11 |
12 | // floats
13 | EqFloatOp,
14 | NeqFloatOp,
15 | LeFloatOp,
16 | LtFloatOp,
17 | GtFloatOp,
18 | GeFloatOp,
19 |
20 | // bools
21 | EqBoolOp,
22 | NeqBoolOp
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Symbol.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 | public abstract class Symbol implements Comparable {
6 |
7 | public static final AtomicInteger counter = new AtomicInteger(0);
8 |
9 | public final String name;
10 | public final int index;
11 |
12 | public Symbol(String name) {
13 | this(name, counter.getAndIncrement());
14 | }
15 |
16 | public Symbol(String name, int index) {
17 | this.name = name;
18 | this.index = index;
19 | }
20 |
21 | @Override public int compareTo(Symbol o) {
22 | int ret = name.compareTo(o.name);
23 | if (ret == 0) {
24 | ret = index - o.index;
25 | }
26 | return ret;
27 | }
28 |
29 | @Override public boolean equals(Object obj) {
30 | if (obj instanceof Symbol) { return compareTo((Symbol) obj) == 0; }
31 | return false;
32 | }
33 |
34 | @Override public int hashCode() {
35 | return name.hashCode() + 31 * index;
36 | }
37 |
38 | @Override public String toString() {
39 | if (index <= 0) { return name; }
40 | return name + "_" + index;
41 | }
42 |
43 | public static class Var extends Symbol {
44 | public Var(String name) { super(name); }
45 | public Var(String name, int index) { super(name, index); }
46 | };
47 | public static class InternalVar extends Symbol {
48 | public InternalVar(String name) { super(name); }
49 | public InternalVar(String name, int index) { super(name, index); }
50 | };
51 | public static class ExternalVar extends Symbol {
52 | public ExternalVar(String name) { super(name); }
53 | public ExternalVar(String name, int index) { super(name, index); }
54 | };
55 | public static class TypeVar extends Symbol {
56 | public TypeVar(String name) { super(name); }
57 | public TypeVar(String name, int index) { super(name, index); }
58 | };
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/SymbolSet.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import util.Table;
4 |
5 | public class SymbolSet {
6 |
7 | private final Table map;
8 |
9 | public static SymbolSet create() {
10 | return new SymbolSet(Table. create());
11 | }
12 |
13 | SymbolSet(Table map) {
14 | this.map = map;
15 | }
16 |
17 | public SymbolSet put(Symbol sym) {
18 | return new SymbolSet(map.put(sym, null));
19 | }
20 |
21 | public boolean contains(Symbol sym) {
22 | return map.contains(sym);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Type.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 |
4 | public class Type {
5 |
6 | public static final Type Unit = new Type();
7 | public static final Type Nil = new Type();
8 | public static final Type Any = new Type();
9 | public static final Type Bool = new Type();
10 | public static final Type Char = new Type();
11 | public static final Type String = new Type();
12 | public static final Type Int = new Type();
13 | public static final Type Float = new Type();
14 |
15 | public static class Array extends Type {
16 | public final Type elementType;
17 | public Array(Type elementType) { this.elementType = elementType; }
18 | };
19 |
20 | public static class Fun extends Type {
21 | public final Type[] args;
22 | public final Type ret;
23 | public Fun(Type[] args, Type ret) {
24 | this.args = args;
25 | this.ret = ret;
26 | }
27 | };
28 |
29 | public static class Method extends Type {
30 | public final Type thisType;
31 | public final Type[] args;
32 | public final Type ret;
33 | public Method(Type thisType, Type[] args, Type ret) {
34 | this.thisType = thisType;
35 | this.args = args;
36 | this.ret = ret;
37 | }
38 | };
39 |
40 | public static class Object extends Type {
41 | public final Symbol.TypeVar name;
42 | public Object(Symbol.TypeVar name) { this.name = name; }
43 | };
44 |
45 | private Type() {}
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/TypeDef.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | import java.util.Map;
4 | import util.FieldMTable;
5 | import appel.ch07.Symbol.InternalVar;
6 | import appel.ch07.Symbol.Var;
7 |
8 | public class TypeDef {
9 | public final Symbol.Var[] parents;
10 | public final Map constructors;
11 | public final FieldMTable methods;
12 | public final FieldMTable fields;
13 | /** Builds declaration without class info; used to build initial dummy environment */
14 | public TypeDef() {
15 | parents = null;
16 | constructors = null;
17 | methods = null;
18 | fields = null;
19 | }
20 | public TypeDef(Var[] parents, Map constructors, FieldMTable methods, FieldMTable fields) {
21 | this.parents = parents;
22 | this.constructors = constructors;
23 | this.methods = methods;
24 | this.fields = fields;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/appel/ch07/Unop.java:
--------------------------------------------------------------------------------
1 | package appel.ch07;
2 |
3 | public enum Unop {
4 | // arithmetic
5 | UMinusIntOp,
6 | UMinusFloatOp,
7 | UNotIntOp,
8 | UNotBoolOp,
9 |
10 | // numeric coercions
11 | UIntOfFloat,
12 | UFloatOfInt,
13 | UCharOfInt,
14 | UIntOfChar
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/util/FieldMTable.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.Comparator;
6 | import java.util.HashMap;
7 | import java.util.LinkedList;
8 | import java.util.List;
9 | import java.util.Map;
10 | import appel.ch07.Symbol;
11 |
12 | public class FieldMTable {
13 |
14 | final int count;
15 | final Table>> table;
16 | private Map> internalTable = null;
17 |
18 | public static FieldMTable create() {
19 | return new FieldMTable();
20 | }
21 |
22 | FieldMTable() {
23 | this(Table.>> create(), 0);
24 | }
25 |
26 | FieldMTable(Table>> table, int count) {
27 | this.table = table;
28 | this.count = count;
29 | }
30 |
31 | public FieldMTable add(Symbol external, Symbol internal, T x) {
32 | // Create new entry
33 | Entry entry = new Entry(internal, x, count);
34 |
35 | // Add to existing list if external symbol already exists, otherwise create a new list
36 | List> entries = table.get(external);
37 | if (entries == null) {
38 | entries = Collections.singletonList(entry);
39 | } else {
40 | entries = new LinkedList>(entries);
41 | entries.add(entry);
42 | }
43 |
44 | return new FieldMTable(table.put(external, entries), count+1);
45 | }
46 |
47 | public boolean contains(Symbol external) {
48 | return table.contains(external);
49 | }
50 |
51 | /** Returns a list of Entry objects with the internal names/values stored for a given external name.
52 | * Returns null if external symbol is not in the table. */
53 | public List> get(Symbol external) {
54 | return table.get(external);
55 | }
56 |
57 | /** Returns an Entry object with the external name and value stored for the given internal name.
58 | * Returns null if internal symbol is not in the table. */
59 | public Entry getInternal(Symbol internal) {
60 | return getInternalTable().get(internal);
61 | }
62 |
63 | /** Returns a list of the external symbols in insertion order */
64 | public List keys() {
65 | List>>> entries = table.toList();
66 | Collections.sort(entries, new Comparator>>>() {
67 | public int compare(Table.Entry>> o1, Table.Entry>> o2) {
68 | Entry max1 = Collections.max(o1.value, new EntryComparator());
69 | Entry max2 = Collections.max(o2.value, new EntryComparator());
70 | return max1.index - max2.index;
71 | }
72 | });
73 |
74 | List ret = new ArrayList(entries.size());
75 | for (Table.Entry>> entry : entries) {
76 | ret.add(entry.key);
77 | }
78 | return ret;
79 | }
80 |
81 | /** Returns a list of the internal symbols in insertion order */
82 | public List internals() {
83 | List>>> entries = table.toList();
84 | List> internals = new ArrayList>();
85 |
86 | for (Table.Entry>> entry : entries) {
87 | internals.addAll(entry.value);
88 | }
89 |
90 | Collections.sort(internals, new EntryComparator());
91 |
92 | List ret = new ArrayList(internals.size());
93 | for (Entry entry : internals) {
94 | ret.add(entry.symbol);
95 | }
96 | return ret;
97 | }
98 |
99 | /** Returns symbols in insertion order in the format "[external_sym{internal_sym}=value; ...]*/
100 | @Override public String toString() {
101 | StringBuilder s = new StringBuilder("[");
102 | List externals = keys();
103 | for (Symbol external : externals) {
104 | List> internals = get(external);
105 | for (Entry entry : internals) {
106 | if (s.length() > 1) {
107 | s.append("; ");
108 | }
109 | s.append(external).append("{").append(entry.symbol).append("}=").append(entry.value);
110 | }
111 | }
112 | return s.append("]").toString();
113 | }
114 |
115 | // Returns a map of internal symbol -> (external symbol, value)
116 | private Map> getInternalTable() {
117 | if (internalTable == null) {
118 | // Iterate table [external -> (internal, value) list] and build new map [internal -> (external, value)]
119 | internalTable = new HashMap>();
120 | List>>> entries = table.toList();
121 | for (Table.Entry>> entry : entries) {
122 | for (Entry internals : entry.value) {
123 | internalTable.put(internals.symbol, new Entry(entry.key /* external symbol */, internals.value, internals.index));
124 | }
125 | }
126 | }
127 | return internalTable;
128 | }
129 |
130 | public static class Entry {
131 | public final Symbol symbol;
132 | public final T value;
133 | protected final int index;
134 | private Entry(Symbol symbol, T value, int index) {
135 | this.symbol = symbol;
136 | this.value = value;
137 | this.index = index;
138 | }
139 | @Override public String toString() {
140 | return symbol + "," + value;
141 | }
142 | }
143 |
144 | public static class EntryComparator implements Comparator> {
145 | public int compare(Entry> o1, Entry> o2) {
146 | return o1.index - o2.index;
147 | }
148 |
149 | }
150 | }
--------------------------------------------------------------------------------
/src/main/java/util/Table.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | /** Functional implementation of Red-Black Trees. */
7 | public abstract class Table, V> {
8 | /** Create an empty map */
9 | public static , V> Table create() { return new RBLeaf(); }
10 |
11 | /** Returns a new RBMap that includes the given key/value */
12 | public Table put(K k, V v){ return ins(k,v).makeBlack(); }
13 | /** Is the given key in this map? */
14 | public abstract boolean contains(K k);
15 | /** Return the value in this map that matches the given key */
16 | public abstract V get(K k);
17 | /** Return the number of elements in the map */
18 | public abstract int size();
19 | /** Return the entries in this map in sorted, ascending order */
20 | public abstract List> toList();
21 |
22 | public abstract int hashCode();
23 | public abstract boolean equals(Object o);
24 |
25 | // "Private" methods for internal use only
26 | abstract boolean isBlack();
27 | abstract boolean isRed();
28 | abstract RBNode asNode();
29 | abstract Table makeBlack();
30 | abstract Table makeRed();
31 | abstract Table ins(K k, V v);
32 | abstract Table left();
33 | abstract Table right();
34 |
35 | boolean isBlackNode(){ return !isLeaf() && isBlack(); }
36 | boolean isRedNode(){ return !isLeaf() && isRed(); }
37 |
38 | // Is this node a leaf?
39 | abstract boolean isLeaf();
40 | // Return a new node without the given element
41 | abstract Table replace(K k, V v);
42 |
43 | // Create a new internal node
44 | protected static , V> RBNode node(Color c, K k, V d, Table l,Table r) { return new RBNode(c,k, d,l,r); }
45 |
46 | // A little messy... but better than keeping track of parents ;)
47 | protected static , V> Table balance(K key, V dat, Table l, Table r){
48 | if(l.isRedNode() && r.isRedNode())
49 | return node(Color.RED, key, dat, l.makeBlack(), r.makeBlack());
50 | if(l.isRedNode()){
51 | RBNode L = l.asNode();
52 | if(L.left.isRedNode()){
53 | RBNode LL = L.left.asNode();
54 | return node(Color.RED, L.key, L.value,
55 | node(Color.BLACK,LL.key,LL.value,LL.left,LL.right),
56 | node(Color.BLACK,key,dat,L.right,r));
57 | }
58 | if(L.right.isRedNode()){
59 | RBNode LR = L.right.asNode();
60 | return node(Color.RED, LR.key, LR.value,
61 | node(Color.BLACK,L.key,L.value,L.left,LR.left),
62 | node(Color.BLACK,key,dat,LR.right,r));
63 | }
64 | }
65 | if(r.isRedNode()){
66 | RBNode R = r.asNode();
67 | if(R.left.isRedNode()){
68 | RBNode RL = R.left.asNode();
69 | return node(Color.RED, RL.key, RL.value,
70 | node(Color.BLACK,key,dat,l,RL.left),
71 | node(Color.BLACK,R.key,R.value,RL.right,R.right));
72 | }
73 | if(R.right.isRedNode()){
74 | RBNode RR = R.right.asNode();
75 | return node(Color.RED, R.key, R.value,
76 | node(Color.BLACK,key,dat,l,R.left),
77 | node(Color.BLACK,RR.key,RR.value,RR.left,RR.right));
78 | }
79 | }
80 | return node(Color.BLACK,key,dat,l,r);
81 | }
82 | protected static , V> Table balleft(Table l, K key, V y, Table r){
83 | if(l.isRedNode()) return node(Color.RED,key,y,l.makeBlack(),r);
84 | if(r.isBlackNode()) return balance(key,y,l,r.makeRed());
85 | RBNode right = r.asNode();
86 | RBNode rtlt = right.left.asNode();
87 | return node(Color.RED,rtlt.key,rtlt.value,
88 | node(Color.BLACK,key,y,l,rtlt.left),
89 | balance(right.key,right.value,rtlt.right,right.right.makeRed()));
90 | }
91 | protected static , V> Table balright(Table l, K key, V y, Table r){
92 | if(r.isRedNode()) return node(Color.RED,key,y,l,r.makeBlack());
93 | if(l.isBlackNode()) return balance(key,y,l.makeRed(),r);
94 | RBNode left = l.asNode();
95 | RBNode ltrt = left.right.asNode();
96 | return node(Color.RED,ltrt.key,ltrt.value,
97 | balance(left.key,left.value,left.left.makeRed(),ltrt.left),
98 | node(Color.BLACK,key,y,ltrt.right,r));
99 | }
100 | protected static , V> Table append(Table l, Table r){
101 | if(l.isLeaf()) return r;
102 | if(r.isLeaf()) return l;
103 | RBNode left = l.asNode(),
104 | right = r.asNode();
105 | if(left.color.equals(right.color)){
106 | Color c = left.color;
107 | Table rtlt = append(left.right,right.left);
108 | if(rtlt.isRedNode()){
109 | RBNode rln = rtlt.asNode();
110 | return node(Color.RED,rln.key,rln.value,
111 | node(c,left.key,left.value,left.left,rln.left),
112 | node(c,right.key,right.value,rln.right,right.right));
113 | }
114 | if(c.isRed())
115 | return node(Color.RED,left.key,left.value,left.left,
116 | node(Color.RED,right.key,right.value,rtlt,right.right));
117 | return balleft(left.left,left.key,left.value,
118 | node(Color.BLACK,right.key,right.value,rtlt,right.right));
119 | }
120 | if(right.isRed())
121 | return node(Color.RED,right.key,right.value,append(left,right.left),right.right);
122 | return node(Color.RED,left.key,left.value,left.left,append(left.right,right));
123 | }
124 |
125 | public static class Entry {
126 | final public K key;
127 | final public V value;
128 | public Entry(K key, V value) {
129 | this.key = key;
130 | this.value = value;
131 | }
132 | }
133 | }
134 |
135 | enum Color {
136 | RED,
137 | BLACK;
138 |
139 | public boolean isRed() { return RED.equals(this); }
140 | public boolean isBlack() { return BLACK.equals(this); }
141 | }
142 |
143 | /** Represents a Node of the tree, e.g., the non-empty tree */
144 | class RBNode,V> extends Table {
145 |
146 | protected final Color color;
147 | protected final K key;
148 | protected final V value;
149 | protected final Table left;
150 | protected final Table right;
151 | private final int hash;
152 |
153 | public RBNode(Color color, K key, V value, Table left, Table right) {
154 | this.color = color;
155 | this.key = key;
156 | this.value = value;
157 | this.left = left;
158 | this.right = right;
159 | hash = key.hashCode() + (value == null ? 0 : value.hashCode()) + left.hashCode() + right.hashCode();
160 | }
161 |
162 | public boolean contains(K k) {
163 | int c = k.compareTo(key);
164 | if (c < 0)
165 | return left.contains(k);
166 | if (c > 0)
167 | return right.contains(k);
168 | return true;
169 | }
170 |
171 | public V get(K x) {
172 | int c = x.compareTo(key);
173 | if (c < 0)
174 | return left.get(x);
175 | if (c > 0)
176 | return right.get(x);
177 | return value;
178 | }
179 |
180 | public List> toList() {
181 | List> l = left.toList();
182 | l.add(new Table.Entry(key, value));
183 | l.addAll(right.toList());
184 | return l;
185 | }
186 |
187 | public int size() { return 1 + left.size() + right.size(); }
188 |
189 | public String toString() { return "(node " + color + " " + value + " " + left + " " + right + ")"; }
190 | public int hashCode() { return hash; }
191 | public boolean equals(Object o) {
192 | if (!(o instanceof RBNode))
193 | return false;
194 | RBNode,?> oo = (RBNode,?>) o;
195 | return color.equals(oo.color) && key.equals(oo.key) && value.equals(oo.value) && left.equals(oo.left) && right.equals(oo.right);
196 | }
197 |
198 | Table ins(K k, V v) {
199 | int c = k.compareTo(key);
200 | if (c == 0)
201 | return node(color, k, v, left, right);
202 | if (color.isBlack()) {
203 | if (c < 0)
204 | return balance(key, value, left.ins(k,v), right);
205 | return balance(key, value, left, right.ins(k,v));
206 | } else {
207 | if (c < 0)
208 | return node(Color.RED, key, value, left.ins(k,v), right);
209 | return node(Color.RED, key, value, left, right.ins(k,v));
210 | }
211 | }
212 |
213 | Table replace(K k, V v) {
214 | int c = k.compareTo(key);
215 | if (c == 0)
216 | return node(color, k, v, left, right);
217 | if (c < 0)
218 | return node(color, key, value, left.replace(k,v), right);
219 | return node(color, key, value, left, right.replace(k,v));
220 | }
221 |
222 | Color getColor() { return color; }
223 | V getValue() { return value; }
224 | Table left() { return left; }
225 | Table right() { return right; }
226 | boolean isLeaf() { return false; }
227 |
228 | boolean isBlack() { return color.isBlack(); }
229 | boolean isRed() { return color.isRed(); }
230 | Table makeBlack() { return node(Color.BLACK, key, value, left, right); }
231 | Table makeRed() { return node(Color.RED, key, value, left, right); }
232 | RBNode asNode() { return this; }
233 | }
234 |
235 | /** Represents a Leaf of the tree, e.g., the empty tree */
236 | class RBLeaf, V> extends Table{
237 |
238 | public boolean contains(K x) { return false; }
239 | public V get(K x) { return null; }
240 | public List> toList() { return new LinkedList>(); }
241 | public int size() { return 0; }
242 |
243 | public String toString() { return ""; }
244 | public int hashCode() { return 82351; }
245 | public boolean equals(Object o) { return (o instanceof RBLeaf); }
246 |
247 | boolean isLeaf() { return true; }
248 | boolean isBlack() { return true; }
249 | boolean isRed() { return false; }
250 | Table left() { return null; }
251 | Table right() { return null; }
252 |
253 | RBNode asNode() { throw new IllegalStateException("Leaf cannot be turned into a node"); }
254 | Table makeBlack() { return this; }
255 | Table makeRed() { throw new IllegalStateException("Leaf cannot be red"); }
256 |
257 | Table ins(K k, V v) { return node(Color.RED, k, v, this, this); }
258 | Table replace(K k, V v) { return node(Color.RED, k, v, this, this); }
259 | }
260 |
261 |
--------------------------------------------------------------------------------
/src/main/sablecc/ch02.grammar:
--------------------------------------------------------------------------------
1 | Package appel.ch02;
2 |
3 | Helpers
4 | any = [0..0xffff];
5 | eol = (10 | 13 | 10 13);
6 | letter = [['a'..'z']+['A'..'Z']];
7 | alpha = letter | '_';
8 | digit = ['0'..'9'];
9 | alphanum = alpha | digit;
10 | nonzero = ['1'..'9'] digit*;
11 | star = '*';
12 | nonstar = [any - star];
13 | quote = '"';
14 | nonquote = [any - quote];
15 | exponent = ('e' | 'E') ('+' | '-')? nonzero?;
16 | escapedchar = '\\' | '\' ''' | '\"' | '\t' | '\n';
17 |
18 | Tokens
19 | comma = ',';
20 | colon = ':';
21 | semicolon = ';';
22 | lparen = '(';
23 | rparen = ')';
24 | lbrack = '[';
25 | rbrack = ']';
26 | lbrace = '{';
27 | rbrace = '}';
28 | bang = '!';
29 | dot = '.';
30 | plus = '+';
31 | minus = '-';
32 | times = '*';
33 | divide = '/';
34 | mod = '%';
35 | eq = '==';
36 | neq = '!=';
37 | lt = '<';
38 | le = '<=';
39 | gt = '>';
40 | ge = '>=';
41 | and = '&&';
42 | or = '||';
43 | assign = '=';
44 | inc = '++';
45 | dec = '--';
46 | pluseq = '+=';
47 | minuseq = '-=';
48 | timeseq = '*=';
49 | divideeq = '/=';
50 | modeq = '%=';
51 |
52 | if = 'if';
53 | else = 'else';
54 | for = 'for';
55 | do = 'do';
56 | while = 'while';
57 | break = 'break';
58 | new = 'new';
59 | throw = 'throw';
60 | try = 'try';
61 | catch = 'catch';
62 | finally = 'finally';
63 | static = 'static';
64 | public = 'public';
65 | protected = 'protected';
66 | private = 'private';
67 | class = 'class';
68 | interface = 'interface';
69 | extends = 'extends';
70 | implements = 'implements';
71 | this = 'this';
72 | instanceof = 'instanceof';
73 | return = 'return';
74 | println = 'System.out.println';
75 | length = 'length';
76 |
77 | void = 'void';
78 | int = 'int';
79 | boolean = 'boolean';
80 | stringtype = 'String';
81 | true = 'true';
82 | false = 'false';
83 | null = 'null';
84 |
85 | decimal = '0' | nonzero;
86 | octal = '0' ['0'..'7']*;
87 | hex = '0x' [digit + [['a'..'f'] + ['A'..'F']]]+;
88 | float =
89 | (digit+ '.' digit* exponent? ) |
90 | (digit* '.' digit+ exponent? ) |
91 | (digit+ exponent);
92 |
93 | char = ''' (escapedchar | any) ''';
94 | string = '"' nonquote* (escapedchar+ nonquote*)* '"';
95 |
96 | id = alpha alphanum*;
97 |
98 | whitespace = (' ' | '\t' | eol)+;
99 | comment =
100 | ('//' [any - [10 + 13]]* eol?) |
101 | ('/*' nonstar* (star+ [any - '/'] nonstar*)* star* '*/');
102 |
103 | Ignored Tokens
104 | whitespace,
105 | comment;
--------------------------------------------------------------------------------
/src/main/sablecc/ch03.grammar:
--------------------------------------------------------------------------------
1 | Package appel.ch03;
2 |
3 | Helpers
4 | any = [0..0xffff];
5 | eol = (10 | 13 | 10 13);
6 | letter = [['a'..'z']+['A'..'Z']];
7 | alpha = letter | '_';
8 | digit = ['0'..'9'];
9 | alphanum = alpha | digit;
10 | nonzero = ['1'..'9'] digit*;
11 | star = '*';
12 | nonstar = [any - star];
13 | nonquote = [any - '"'];
14 | exponent = ('e' | 'E') ('+' | '-')? nonzero?;
15 | escapedchar = '\\' | '\' ''' | '\"' | '\t' | '\n';
16 |
17 | Tokens
18 | // Symbols
19 | comma = ','; colon = ':'; semi = ';'; bang = '!'; dot = '.';
20 | lparen = '('; rparen = ')'; lbrack = '['; rbrack = ']'; lbrace = '{'; rbrace = '}';
21 | plus = '+'; minus = '-'; times = '*'; divide = '/'; mod = '%';
22 | eq = '='; pluseq = '+='; minuseq = '-='; timeseq = '*='; divideeq = '/='; modeq = '%=';
23 | eqeq = '=='; neq = '!='; lt = '<'; le = '<='; gt = '>'; ge = '>=';
24 | andand = '&&'; oror = '||';
25 | plusplus = '++'; minusminus = '--';
26 |
27 | // Reserved
28 | if = 'if'; else = 'else';
29 | for = 'for'; do = 'do'; while = 'while'; break = 'break';
30 | new = 'new'; throw = 'throw'; try = 'try'; catch = 'catch'; finally = 'finally';
31 | classkeyword = 'class'; interface = 'interface'; extends = 'extends'; implements = 'implements';
32 | this = 'this'; instanceof = 'instanceof';
33 | return = 'return'; true = 'true'; false = 'false'; null = 'null';
34 | void = 'void'; type_int = 'int'; type_float = 'float'; type_boolean = 'boolean'; type_char = 'char'; type_string = 'String';
35 |
36 | // Numbers
37 | integer = '0' | nonzero;
38 | octal = '0' ['0'..'7']*;
39 | hex = '0x' [digit + [['a'..'f'] + ['A'..'F']]]+;
40 | float
41 | = (digit+ '.' digit* exponent? )
42 | | (digit* '.' digit+ exponent? )
43 | | (digit+ exponent);
44 |
45 | // Strings
46 | char = ''' (escapedchar | any) ''';
47 | string = '"' nonquote* (escapedchar+ nonquote*)* '"';
48 |
49 | // Identifier
50 | id = alpha alphanum*;
51 |
52 | // Whitespace/comments
53 | whitespace = (' ' | '\t' | eol)+;
54 | comment
55 | = ('//' [any - [10 + 13]]* eol?)
56 | | ('/*' nonstar* (star+ [any - '/'] nonstar*)* star* '*/');
57 |
58 | Ignored Tokens
59 | whitespace,
60 | comment;
61 |
62 | Productions
63 | // -------------------------------------------------------
64 | // Top-level program (i.e. a .java file)
65 | // -------------------------------------------------------
66 | prog {-> expr}
67 | = class_or_interface_def+ {-> New expr.seq([class_or_interface_def.expr])};
68 | class_or_interface_def {-> expr}
69 | = {class} class_def {-> class_def.expr}
70 | | {interface} interface_def {-> interface_def.expr};
71 |
72 | // -------------------------------------------------------
73 | // Classes and interfaces
74 | // -------------------------------------------------------
75 | interface_def {-> expr}
76 | = interface id lbrace member_decl* rbrace {-> New expr.def(New def.type(New type.interface(id, [member_decl.def])))};
77 | class_def {-> expr}
78 | = classkeyword id extends_type? implements_types? lbrace member_def* rbrace
79 | {-> New expr.def(New def.class(id, extends_type.type, [implements_types.type], [member_def.def]))};
80 | extends_type {-> type} = extends id {-> New type.id(id)};
81 | implements_types {-> type*}
82 | = {single} implements id {-> [New type.id(id)]}
83 | | {multi} id comma implements_types {-> [New type.id(id), implements_types.type]};
84 |
85 | // -------------------------------------------------------
86 | // Declarations
87 | // -------------------------------------------------------
88 | member_decl {-> def}
89 | = fun_decl semi {-> fun_decl.def};
90 |
91 | // Function declaration, e.g. void f(int g, void h(int))
92 | fun_decl {-> def} = fun_return_type_spec id lparen fun_formals? rparen {-> New def.fun(id, fun_return_type_spec.type, [fun_formals.formal], [New expr.none()])};
93 |
94 | // Function parameters (formals) are a list of _named_ variables and functions,
95 | // e.g. "int g, void h(int)"
96 | fun_formals {-> formal*}
97 | = {single} fun_formal {-> [fun_formal.formal]}
98 | | {multi} fun_formal comma fun_formals {-> [fun_formal.formal, fun_formals.formal]};
99 | fun_formal {-> formal}
100 | = {fun} fun_return_type_spec id lparen fun_type_params? rparen {-> New formal.fun(id, fun_return_type_spec.type, [fun_type_params.type])}
101 | | {var} var_type_spec id array_dimension* {-> New formal.var_or_array(var_type_spec.type, id, [array_dimension.expr])};
102 |
103 | // Variable type and function return type specifications. Variable types can be
104 | // suffixed with empty array dimensions, e.g. int[] a. Functions return void.
105 | fun_return_type_spec {-> type}
106 | = {void} void {-> New type.void()}
107 | | {non_void} var_type_spec {-> var_type_spec.type};
108 | var_type_spec {-> type}
109 | = {single} var_type {-> var_type.type}
110 | | {array} var_type array_dimension+ {-> New type.array_with_size(var_type.type, [array_dimension.expr])};
111 | array_dimension {-> expr} = lbrack rbrack {-> New expr.none()};
112 |
113 | // A variable of type function may have parameters. These parameters are _not_
114 | // named (like formals) and if the parameter is also of type function, it is
115 | // not surrounded by parens (like var_type), e.g. "void(int)"
116 | fun_type_params {-> type*}
117 | = {single} fun_type_param {-> [fun_type_param.type]}
118 | | {multi} fun_type_param comma fun_type_params {-> [fun_type_param.type, fun_type_params.type]};
119 | fun_type_param {-> type}
120 | = {fun} fun_return_type_spec lparen fun_type_params? rparen {-> New type.fun(fun_return_type_spec.type, [fun_type_params.type])}
121 | | {var} var_type_spec {-> var_type_spec.type};
122 |
123 | // Non-array variable types. These can include function types which are function
124 | // signatures surrounded by parens, e.g. "(void(int))"
125 | var_type {-> type}
126 | = {int} type_int {-> New type.int()}
127 | | {float} type_float {-> New type.float()}
128 | | {bool} type_boolean {-> New type.boolean()}
129 | | {char} type_char {-> New type.char()}
130 | | {string} type_string {-> New type.string()}
131 | | {class} id {-> New type.id(id)}
132 | | {fun} [lp1]:lparen fun_return_type_spec id [lp2]:lparen fun_type_params? [rp2]:rparen [rp1]:rparen
133 | {-> New type.fun(fun_return_type_spec.type, [fun_type_params.type])};
134 |
135 | // -------------------------------------------------------
136 | // Definitions
137 | // -------------------------------------------------------
138 | member_def {-> def}
139 | = {vars} var_list_def semi {-> var_list_def.def}
140 | | {fun} fun_def {-> fun_def.def}
141 | | {constructor} constructor_def {-> constructor_def.def};
142 | var_list_def {-> def}
143 | = var_type_spec [vars]:var_list_items {-> New def.vars_or_arrays(var_type_spec.type, [vars.var_and_init])};
144 | var_list_items {-> var_and_init*}
145 | = {single} id array_dimension* var_init? {-> [New var_and_init(id, [array_dimension.expr], var_init.expr)]}
146 | | {multi} id array_dimension* var_init? comma [rest]:var_list_items {-> [New var_and_init(id, [array_dimension.expr], var_init.expr), rest.var_and_init]};
147 | var_init {-> expr}
148 | = eq expr {-> expr.expr};
149 | fun_def {-> def}
150 | = fun_return_type_spec id lparen fun_formals? rparen lbrace stmt* rbrace {-> New def.fun(id, fun_return_type_spec.type, [fun_formals.formal], [stmt.expr])};
151 | constructor_def {-> def}
152 | = id lparen fun_formals? rparen lbrace stmt* rbrace {-> New def.const(id, [fun_formals.formal], [stmt.expr])};
153 |
154 | // -------------------------------------------------------
155 | // Expressions (ordered by precedence)
156 | // -------------------------------------------------------
157 |
158 | // 1. A single value, parens groups, new expressions, projections, subscripts, fun apply
159 | value {-> expr}
160 | = {non_literal} [val]:non_literal_value {-> val.expr}
161 | | {literal} [val]:literal_value {-> val.expr};
162 | // 2.. ++, --, -, !
163 | pre_unary_expr {-> expr}
164 | = {neg} minus [expr]:pre_unary_expr {-> New expr.unop(expr.expr, New unop.neg())}
165 | | {not} bang [expr]:pre_unary_expr {-> New expr.unop(expr.expr, New unop.not())}
166 | | {pre_dec} minusminus [expr]:pre_unary_expr {-> New expr.uarithop(expr.expr, New uarithop.pre_dec())}
167 | | {pre_inc} plusplus [expr]:pre_unary_expr {-> New expr.uarithop(expr.expr, New uarithop.pre_inc())}
168 | | {simple} [expr]:value {-> expr.expr};
169 | post_unary_expr {-> expr}
170 | = {post_dec} [expr]:post_unary_expr minusminus {-> New expr.uarithop(expr.expr, New uarithop.post_dec())}
171 | | {post_inc} [expr]:post_unary_expr plusplus {-> New expr.uarithop(expr.expr, New uarithop.post_inc())}
172 | | {simple} [expr]:pre_unary_expr {-> expr.expr};
173 | // 3. *, /, %
174 | mult_expr {-> expr}
175 | = {times} [left]:mult_expr times [right]:post_unary_expr {-> New expr.binop(left.expr, New binop.times(), right.expr)}
176 | | {divide} [left]:mult_expr divide [right]:post_unary_expr {-> New expr.binop(left.expr, New binop.divide(), right.expr)}
177 | | {mod} [left]:mult_expr mod [right]:post_unary_expr {-> New expr.binop(left.expr, New binop.mod(), right.expr)}
178 | | {simple} [expr]:post_unary_expr {-> expr.expr};
179 | // 4. +, -
180 | add_expr {-> expr}
181 | = {plus} [left]:add_expr plus [right]:mult_expr {-> New expr.binop(left.expr, New binop.plus(), right.expr)}
182 | | {minus} [left]:add_expr minus [right]:mult_expr {-> New expr.binop(left.expr, New binop.minus(), right.expr)}
183 | | {simple} [expr]:mult_expr {-> expr.expr};
184 | // 5. <, >, <=, >=
185 | ineq_expr {-> expr}
186 | = {gt} [left]:ineq_expr gt [right]:add_expr {-> New expr.binop(left.expr, New binop.gt(), right.expr)}
187 | | {lt} [left]:ineq_expr lt [right]:add_expr {-> New expr.binop(left.expr, New binop.lt(), right.expr)}
188 | | {le} [left]:ineq_expr le [right]:add_expr {-> New expr.binop(left.expr, New binop.le(), right.expr)}
189 | | {ge} [left]:ineq_expr ge [right]:add_expr {-> New expr.binop(left.expr, New binop.ge(), right.expr)}
190 | | {simple} [expr]:add_expr {-> expr.expr};
191 | // 6. instanceof
192 | instanceof_expr {-> expr}
193 | = [left]:instanceof_expr instanceof [right]:ineq_expr {-> New expr.instanceof(left.expr, right.expr)}
194 | | {simple} [expr]:ineq_expr {-> expr.expr};
195 | // 7. ==, !=
196 | eq_expr {-> expr}
197 | = {eq} [left]:eq_expr eqeq [right]:instanceof_expr {-> New expr.binop(left.expr, New binop.eq(), right.expr)}
198 | | {neq} [left]:eq_expr neq [right]:instanceof_expr {-> New expr.binop(left.expr, New binop.neq(), right.expr)}
199 | | {simple} [expr]:instanceof_expr {-> expr.expr};
200 | // 8. &&
201 | and_expr {-> expr}
202 | = [left]:and_expr andand [right]:eq_expr {-> New expr.boolop(left.expr, New boolop.and(), right.expr)}
203 | | {simple} [expr]:eq_expr {-> expr.expr};
204 | // 9. ||
205 | or_expr {-> expr}
206 | = [left]:or_expr oror [right]:and_expr {-> New expr.boolop(left.expr, New boolop.or(), right.expr)}
207 | | {simple} [expr]:and_expr {-> expr.expr};
208 | // 10. assignment
209 | expr {-> expr}
210 | = {assign} [left]:or_expr eq [right]:expr {-> New expr.assign(left.expr, New binop.none(), right.expr)}
211 | | {binop_assign} [left]:or_expr [op]:binop_eq [right]:expr {-> New expr.assign(left.expr, op.binop, right.expr)}
212 | | {simple} [expr]:or_expr {-> expr.expr};
213 |
214 | // Expression helpers
215 | binop_eq {-> binop}
216 | = {plus} pluseq {-> New binop.plus()}
217 | | {minus} minuseq {-> New binop.minus()}
218 | | {times} timeseq {-> New binop.times()}
219 | | {divide} divideeq {-> New binop.divide()}
220 | | {mod} modeq {-> New binop.mod()};
221 | literal_value {-> expr}
222 | = {char} char {-> New expr.char(char)}
223 | | {int} integer {-> New expr.number(New number.decimal(integer))}
224 | | {octal} octal {-> New expr.number(New number.octal(octal))}
225 | | {hex} hex {-> New expr.number(New number.hex(hex))}
226 | | {float} float {-> New expr.number(New number.float(float))}
227 | | {string} string {-> New expr.string(string)}
228 | | {null} null {-> New expr.null()}
229 | | {true} true {-> New expr.bool(New bool.true())}
230 | | {false} false {-> New expr.bool(New bool.false())};
231 | non_literal_value {-> expr}
232 | = {var} id {-> New expr.var(id)}
233 | | {paren} paren_expr {-> paren_expr.expr}
234 | | {new} new_expr {-> new_expr.expr}
235 | | {apply} apply_expr {-> apply_expr.expr}
236 | | {subscript} subscript_expr {-> subscript_expr.expr}
237 | | {project} project_expr {-> project_expr.expr};
238 | paren_expr {-> expr}
239 | = lparen expr rparen {-> expr.expr};
240 | apply_expr {-> expr}
241 | = [expr]:non_literal_value lparen arg_list? rparen {-> New expr.apply(expr.expr, [arg_list.expr])};
242 | arg_list {-> expr*}
243 | = {single} expr {-> [expr.expr]}
244 | | {multi} expr comma [tail]:arg_list {-> [expr.expr, tail.expr]};
245 | project_expr {-> expr}
246 | = {single} [expr]:project_value dot id {-> New expr.project(expr.expr, id)}
247 | | {multi} [expr]:project_expr dot id {-> New expr.project(expr.expr, id)};
248 | project_value {-> expr}
249 | = {var} id {-> New expr.var(id)}
250 | | {paren} paren_expr {-> paren_expr.expr}
251 | | {new} new_expr {-> new_expr.expr}
252 | | {apply} apply_expr {-> apply_expr.expr}
253 | | {subscript} subscript_expr {-> subscript_expr.expr}
254 | | {string} string {-> New expr.string(string)};
255 | subscript_expr {-> expr}
256 | = [expr]:subscript_value [index]:array_index_or_size {-> New expr.subscript(expr.expr, index.expr)};
257 | subscript_value {-> expr}
258 | = {paren} paren_expr {-> paren_expr.expr}
259 | | {apply} apply_expr {-> apply_expr.expr}
260 | | {subscript} subscript_expr {-> subscript_expr.expr}
261 | | {project} project_expr {-> project_expr.expr}
262 | | {var} id {-> New expr.var(id)};
263 | array_index_or_size {-> expr}
264 | = lbrack expr rbrack {-> expr.expr};
265 | new_expr {-> expr}
266 | = {object} new id lparen [args]:arg_list? rparen {-> New expr.new_const(id, [args.expr])}
267 | | {array} new var_type array_index_or_size+ array_dimension* {-> New expr.new_array(var_type.type, [array_index_or_size.expr, array_dimension.expr])};
268 |
269 | // -------------------------------------------------------
270 | // Statements
271 | // -------------------------------------------------------
272 | stmt {-> expr}
273 | = {if_then} if lparen [cond]:expr rparen [then_stmt]:stmt {-> New expr.if(cond.expr, then_stmt.expr, New expr.none())}
274 | | {if_then_else} if lparen expr rparen [then_stmt]:matched_if_stmt else [else_stmt]:stmt {-> New expr.if(expr.expr, then_stmt.expr, else_stmt.expr)}
275 | | {for} for lparen [init]:arg_list? [semi1]:semi [cond]:expr? [semi2]:semi [inc]:arg_list? rparen stmt {-> New expr.for([init.expr], cond.expr, [inc.expr], stmt.expr)}
276 | | {while} while lparen [cond]:expr rparen stmt {-> New expr.while(cond.expr, stmt.expr)}
277 | | {simple} unconditional_stmt {-> unconditional_stmt.expr};
278 | matched_if_stmt {-> expr}
279 | = {if_then_else} if lparen expr rparen [then_stmt]:matched_if_stmt else [else_stmt]:matched_if_stmt {-> New expr.if(expr.expr, then_stmt.expr, else_stmt.expr)}
280 | | {for} for lparen [init]:arg_list? [semi1]:semi [cond]:expr? [semi2]:semi [inc]:arg_list? rparen [stmt]:matched_if_stmt {-> New expr.for([init.expr], cond.expr, [inc.expr], stmt.expr)}
281 | | {while} while lparen [cond]:expr rparen [stmt]:matched_if_stmt {-> New expr.while(cond.expr, stmt.expr)}
282 | | {simple} unconditional_stmt {-> unconditional_stmt.expr};
283 | unconditional_stmt {-> expr}
284 | = {vars} var_list_def semi {-> New expr.def(var_list_def.def)}
285 | | {fun_decl} fun_decl semi {-> New expr.def(fun_decl.def)}
286 | | {fun} fun_def {-> New expr.def(fun_def.def)}
287 | | {expr} expr semi {-> expr.expr}
288 | | {return} return expr semi {-> New expr.return(expr.expr)}
289 | | {break} break semi {-> New expr.break()}
290 | | {try} try lbrace stmt* rbrace catch_block+ {-> New expr.try([New expr.seq([stmt.expr])], [catch_block.catch_block])}
291 | | {block} lbrace stmt* rbrace {-> New expr.seq([stmt.expr])}
292 | | {empty} semi {-> New expr.none()};
293 | catch_block {-> catch_block}
294 | = catch lparen [type]:id [var]:id rparen lbrace stmt* rbrace {-> New catch_block(type, var, [stmt.expr])};
295 |
296 |
297 | Abstract Syntax Tree
298 | expr
299 | = {none}
300 | | {null}
301 | | {bool} bool
302 | | {char} char
303 | | {number} number
304 | | {string} string
305 | | {var} id
306 | | {uarithop} expr [op]:uarithop
307 | | {unop} expr [op]:unop
308 | | {binop} [left]:expr [op]:binop [right]:expr
309 | | {boolop} [left]:expr [op]:boolop [right]:expr
310 | | {subscript} expr [index]:expr
311 | | {project} expr [member]:id
312 | | {apply} expr [args]:expr*
313 | | {assign} [left]:expr [op]:binop [right]:expr
314 | | {if} [cond]:expr [then]:expr [else]:expr
315 | | {for} [init]:expr* [cond]:expr? [inc]:expr* expr
316 | | {while} [cond]:expr expr
317 | | {seq} expr*
318 | | {return} expr
319 | | {break}
320 | | {new_const} id [args]:expr*
321 | | {new_array} [type]:type [dims]:expr*
322 | | {instanceof} [left]:expr [right]:expr
323 | | {try} expr* catch_block+
324 | | {def} def;
325 | def
326 | = {var_or_array} type id [dims]:expr* expr? // ASTCleaner replaces AVarOrArrayDef with AVarDef
327 | | {vars_or_arrays} type [vars]:var_and_init+ // ASTCleaner replaces AVarsDef with AVarsDef([AVarDef])
328 | | {var} type id expr?
329 | | {vars} def*
330 | | {fun} id [return]:type formal* expr*
331 | | {const} id formal* expr*
332 | | {class} id [extends]:type? [implements]:type* def*
333 | | {type} type;
334 | formal
335 | = {var_or_array} type id [dims]:expr* // ASTCleaner replaces AVarsDef with AVarsDef([AVarDef])
336 | | {var} type id
337 | | {fun} id [return]:type [args]:type*;
338 | type
339 | = {void} | {boolean} | {int} | {float} | {char} | {string} | {id} id | {array} type
340 | | {array_with_size} type [dims]:expr+ // ASTCleaner replaces AArrayWithSizeType with AArrayType
341 | | {fun} [return]:type [args]:type*
342 | | {interface} id [members]:def*;
343 | bool = {true} | {false};
344 | number = {decimal} integer | {octal} octal | {hex} hex | {float} float;
345 | uarithop = {pre_dec} | {pre_inc} | {post_dec} | {post_inc};
346 | unop = {neg} | {not};
347 | binop = {plus} | {minus} | {times} | {divide} | {mod} | {eq} | {neq} | {lt} | {gt} | {le} | {ge} | {none};
348 | boolop = {and} | {or};
349 | var_and_init = id [dims]:expr* expr?;
350 | catch_block = [type]:id [var]:id expr*;
351 |
--------------------------------------------------------------------------------
/src/test/java/appel/ch01/TestInterp.java:
--------------------------------------------------------------------------------
1 | package appel.ch01;
2 |
3 | import static appel.ch01.Interp.*;
4 | import static org.testng.Assert.*;
5 |
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.PrintStream;
8 |
9 | import org.testng.annotations.BeforeClass;
10 | import org.testng.annotations.BeforeMethod;
11 | import org.testng.annotations.Test;
12 |
13 | public class TestInterp {
14 |
15 | private NumExp zero = new NumExp(0);
16 | private NumExp one = new NumExp(1);
17 | private PrintStm simplePrintStm = print(zero);
18 | private Stm prog =
19 | new CompoundStm(new AssignStm("a", new OpExp(new NumExp(5), OpExp.Plus, new NumExp(3))), // a=5+3
20 | new CompoundStm(new AssignStm("b", new EseqExp( // b=( print(a, a-1), 10*a )
21 | new PrintStm(new PairExpList(new IdExp("a"), new LastExpList(new OpExp(new IdExp("a"), OpExp.Minus, new NumExp(1))))),
22 | new OpExp(new NumExp(10), OpExp.Times, new IdExp("a"))
23 | )),
24 | new PrintStm(new LastExpList(new IdExp("b"))))); // print(b)
25 | // --> 8780
26 | private Stm complexProg =
27 | // x = 5 --> ; x=5
28 | // print(( print(x), x = x+5, print(x), y = x-1, y )) --> 5109; x=10, y=9
29 | // print(y) --> 9 ;
30 | // print( (5+( y = y+1, y )-12) * 10 / (1+1) ) --> 15 ; y=10
31 | // z = ( x = x+1, y=x+1, y+1) --> ; x=11, y=12, z=13
32 | // print(x, y, z) --> 111213
33 | new CompoundStm(new AssignStm("x", new NumExp(5)),
34 | new CompoundStm(print(
35 | new EseqExp(print(new IdExp("x")),
36 | new EseqExp(new AssignStm("x", new OpExp(new IdExp("x"), OpExp.Plus, new NumExp(5))),
37 | new EseqExp(print(new IdExp("x")),
38 | new EseqExp(new AssignStm("y", new OpExp(new IdExp("x"), OpExp.Minus, new NumExp(1))),
39 | new IdExp("y")))))),
40 | new CompoundStm(print(new IdExp("y")),
41 | new CompoundStm(print(
42 | new OpExp(
43 | new OpExp(
44 | new OpExp(
45 | new OpExp(new NumExp(5), OpExp.Plus, new EseqExp(new AssignStm("y", new OpExp(new IdExp("y"), OpExp.Plus, new NumExp(1))), new IdExp("y"))),
46 | OpExp.Minus,
47 | new NumExp(12)),
48 | OpExp.Times,
49 | new NumExp(10)),
50 | OpExp.Div,
51 | new OpExp(one, OpExp.Plus, one))),
52 | new CompoundStm(new AssignStm("z",
53 | new EseqExp(new AssignStm("x", new OpExp(new IdExp("x"), OpExp.Plus, one)),
54 | new EseqExp(new AssignStm("y", new OpExp(new IdExp("x"), OpExp.Plus, one)),
55 | new OpExp(new IdExp("y"), OpExp.Plus, one)))),
56 | print(new IdExp("x"), new IdExp("y"), new IdExp("z")))))));
57 |
58 | // numargs() tests
59 | @Test public void numargsWithOneArg() {
60 | assertEquals(numargs(new LastExpList(zero)), 1);
61 | }
62 | @Test public void numargsWithMultipleArgs() {
63 | assertEquals(numargs(new PairExpList(zero, new PairExpList(zero, new LastExpList(zero)))), 3);
64 | }
65 |
66 | // explist() tests
67 | @Test public void explistSingleElementList() {
68 | assertEquals(numargs(explist(zero)), 1);
69 | }
70 | @Test public void explistMultiElementList() {
71 | assertEquals(numargs(explist(zero, zero, zero)), 3);
72 | }
73 | @Test public void explistAcceptsArrayAsVarargs() {
74 | assertEquals(numargs(explist(zero, new NumExp[] { zero, zero })), 3);
75 | }
76 |
77 | // maxargs() tests
78 | @Test public void maxargsSimplePrintStatement() {
79 | assertEquals(maxargs(simplePrintStm), 1);
80 | }
81 | @Test public void maxargsPrintStatementWithMultipleArgs() {
82 | Stm s = print(zero, zero, zero);
83 | assertEquals(maxargs(s), 3);
84 | }
85 | @Test public void maxargsCompoundStatement() {
86 | Stm s = new CompoundStm(simplePrintStm, new AssignStm("x", zero));
87 | assertEquals(maxargs(s), 1);
88 | }
89 | @Test public void maxargsESeq() {
90 | Stm s = print(new EseqExp(print(zero, zero), zero));
91 | assertEquals(maxargs(s), 2);
92 | }
93 | @Test public void maxargsOpExp() {
94 | Stm s = print(new OpExp(new EseqExp(print(zero, zero, zero), new IdExp("x")), 0, zero));
95 | assertEquals(maxargs(s), 3);
96 | }
97 | @Test public void maxargsAssignStm() {
98 | Stm s = new AssignStm("x", new EseqExp(print(zero, zero, zero), zero));
99 | assertEquals(maxargs(s), 3);
100 | }
101 | @Test public void maxargsLongProgram() {
102 | assertEquals(maxargs(prog), 2);
103 | }
104 |
105 | // interp() tests
106 | ByteArrayOutputStream out = new ByteArrayOutputStream();
107 | @BeforeClass public void replaceSystemOut() {
108 | System.setOut(new PrintStream(out));
109 | }
110 | @BeforeMethod public void resetOutputStream() {
111 | out.reset();
112 | }
113 | @Test public void interpSimplePrint() {
114 | interp(print(zero));
115 | assertEquals(out.toString(), "0");
116 | }
117 | @Test public void interpMultiArgPrint() {
118 | interp(print(zero, zero, zero));
119 | assertEquals(out.toString(), "000");
120 | }
121 | @Test public void interpCompoundPrints() {
122 | Stm s = new CompoundStm(simplePrintStm, new CompoundStm(simplePrintStm, simplePrintStm));
123 | interp(s);
124 | assertEquals(out.toString(), "000");
125 | }
126 | @Test public void interpAssignment() {
127 | Stm s = new CompoundStm(new AssignStm("x", zero), print(new IdExp("x"), one));
128 | interp(s);
129 | assertEquals(out.toString(), "01");
130 | }
131 | @Test public void interpESeq() {
132 | Stm s = new CompoundStm(new AssignStm("x", one), print(new EseqExp(print(zero, zero), new IdExp("x"))));
133 | interp(s);
134 | assertEquals(out.toString(), "001");
135 | }
136 | @Test public void interpOpExp() {
137 | Stm s = new CompoundStm(
138 | new AssignStm("x", one),
139 | print(
140 | new OpExp(new IdExp("x"), OpExp.Plus, one),
141 | new OpExp(new IdExp("x"), OpExp.Minus, one),
142 | new OpExp(new IdExp("x"), OpExp.Times, new NumExp(3)),
143 | new OpExp(new IdExp("x"), OpExp.Div, one)));
144 | interp(s);
145 | assertEquals(out.toString(), "2031");
146 | }
147 | @Test public void interpProg() {
148 | interp(prog);
149 | assertEquals(out.toString(), "8780");
150 | }
151 | @Test public void interpComplexProg() {
152 | interp(complexProg);
153 | assertEquals(out.toString(), "5109915111213");
154 | }
155 |
156 | // helpers
157 | private PrintStm print(Exp head, Exp... tail) {
158 | return new PrintStm(explist(head, tail));
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/test/java/appel/ch02/TestLexer.java:
--------------------------------------------------------------------------------
1 | package appel.ch02;
2 |
3 | import static org.testng.Assert.*;
4 |
5 | import java.io.PushbackReader;
6 | import java.io.StringReader;
7 |
8 | import org.testng.annotations.Test;
9 |
10 | import appel.ch02.lexer.Lexer;
11 | import appel.ch02.node.*;
12 |
13 | @Test
14 | public class TestLexer {
15 |
16 | public void symbols() {
17 | testTokens(",:;()[]{}", TComma.class, TColon.class, TSemicolon.class, TLparen.class, TRparen.class, TLbrack.class, TRbrack.class, TLbrace.class, TRbrace.class);
18 | testTokens("+-*/%", TPlus.class, TMinus.class, TTimes.class, TDivide.class, TMod.class);
19 | testTokens("== != > >= < <= && ||", TEq.class, TNeq.class, TGt.class, TGe.class, TLt.class, TLe.class, TAnd.class, TOr.class);
20 | testTokens("= += -= *= /= %=", TAssign.class, TPluseq.class, TMinuseq.class, TTimeseq.class, TDivideeq.class, TModeq.class);
21 | testTokens("! ++ --", TBang.class, TInc.class, TDec.class);
22 | }
23 |
24 | public void reserved() {
25 | testTokens("System.out.println length", TPrintln.class, TLength.class);
26 | testTokens("if else", TIf.class, TElse.class);
27 | testTokens("for do while break", TFor.class, TDo.class, TWhile.class, TBreak.class);
28 | testTokens("try catch finally throw finally", TTry.class, TCatch.class, TFinally.class, TThrow.class, TFinally.class);
29 | testTokens("public protected private static new", TPublic.class, TProtected.class, TPrivate.class, TStatic.class, TNew.class);
30 | testTokens("interface class extends implements this instanceof", TInterface.class, TClass.class, TExtends.class, TImplements.class, TThis.class, TInstanceof.class);
31 | testTokens("return true false null", TReturn.class, TTrue.class, TFalse.class, TNull.class);
32 | testTokens("void int boolean String", TVoid.class, TInt.class, TBoolean.class, TStringtype.class);
33 | }
34 |
35 | public void numbers() {
36 | testTokens("0 1 10", TDecimal.class, TDecimal.class, TDecimal.class);
37 | testTokens("01 07", TOctal.class, TOctal.class);
38 | testTokens("0x0 0x0111 0xAF1", THex.class, THex.class, THex.class);
39 | testTokens("1. 1.0 0.1 .1 .111 1.111", TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class);
40 | testTokens("1e1 1e+1 1e-1 1.e1 1.e+1 1.e-1 .1e10 0.1e10 1.5e10 1.0e+10", TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class);
41 | }
42 |
43 | public void strings() {
44 | testTokens("' ' 'x' '\\n' '\\t' '\\''", TChar.class, TChar.class, TChar.class, TChar.class, TChar.class);
45 | testTokens("\"\" \"string\" \"\\\"\" \"\\\"\\\"\" \"\\\"abc\\\"\\\"\\\"\"", TString.class, TString.class, TString.class, TString.class, TString.class);
46 | }
47 |
48 | public void lineComments() {
49 | testTokens("//", TComment.class);
50 | testTokens("//\n", TComment.class);
51 | testTokens("// ", TComment.class);
52 | testTokens("// this is a comment", TComment.class);
53 | testTokens( "// mutli \n// lines", TComment.class, TComment.class);
54 | testTokens("x // inline", TId.class, TComment.class);
55 | }
56 |
57 | public void comments() {
58 | testTokens("/**/", TComment.class);
59 | testTokens("/* */", TComment.class);
60 | testTokens("/*a*/", TComment.class);
61 | testTokens("/***/", TComment.class);
62 | testTokens("/*\n*/", TComment.class);
63 | testTokens("/* one line */", TComment.class);
64 | testTokens("/* multi\nline */", TComment.class);
65 | testTokens("/* if while */", TComment.class);
66 | testTokens("/* contains * */", TComment.class);
67 | testTokens("/* contains * /*/", TComment.class);
68 | testTokens("/* contains ****/", TComment.class);
69 | }
70 |
71 | private Lexer initLexer(String input) {
72 | Lexer lex = new Lexer(new PushbackReader(new StringReader(input)));
73 | return lex;
74 | }
75 |
76 | private void testTokens(String input, Class>... expectedTokens) {
77 | Lexer lex = initLexer(input);
78 | Class> tokClass;
79 | int i = 0;
80 |
81 | try {
82 | for (i = 0; i < expectedTokens.length; i++) {
83 | // Get next token, skipping whitespace unless explicitly specified in expected token list
84 | do {
85 | tokClass = lex.next().getClass();
86 | } while (tokClass == TWhitespace.class && expectedTokens[i] != TWhitespace.class);
87 | assertEquals(tokClass, expectedTokens[i], "Token " + i + " of '" + input + "' ");
88 | }
89 | // skip over any trailing whitespace
90 | do {
91 | tokClass = lex.next().getClass();
92 | } while (tokClass == TWhitespace.class);
93 |
94 | // and assert that the whole string was consumed
95 | assertEquals(tokClass, EOF.class);
96 | } catch (Exception e) {
97 | fail("Unable to get token " + i + " from " + input, e);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/java/appel/ch03/ASTCleaner.java:
--------------------------------------------------------------------------------
1 | package appel.ch03;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | import appel.ch03.analysis.DepthFirstAdapter;
7 | import appel.ch03.node.*;
8 |
9 | public class ASTCleaner extends DepthFirstAdapter {
10 | @Override public void outAVarOrArrayDef(AVarOrArrayDef node) {
11 | int dimensions = getDimensions(node.getDims());
12 | if (dimensions > 0) {
13 | node.replaceBy(new AVarDef(convertToArray(node.getType(), dimensions), node.getId(), node.getExpr()));
14 | }
15 | }
16 | @Override public void outAVarsOrArraysDef(AVarsOrArraysDef node) {
17 | PType type = node.getType();
18 | List defs = new LinkedList();
19 | for (PVarAndInit varAndInit : node.getVars()) {
20 | defs.add(convertToVarDef((PType) type.clone(), varAndInit));
21 | }
22 | node.replaceBy(new AVarsDef(defs));
23 | }
24 | @Override public void outAArrayWithSizeType(AArrayWithSizeType node) {
25 | int dimensions = getDimensions(node.getDims());
26 | if (dimensions > 0) {
27 | node.replaceBy(convertToArray(node.getType(), dimensions));
28 | }
29 | }
30 | @Override public void outAVarOrArrayFormal(AVarOrArrayFormal node) {
31 | int dimensions = getDimensions(node.getDims());
32 | if (dimensions > 0) {
33 | node.replaceBy(convertToArray(node.getType(), dimensions));
34 | }
35 | }
36 | private int getDimensions(LinkedList> dimensions) {
37 | return (dimensions == null) ? 0 : dimensions.size();
38 | }
39 | private PType convertToArray(PType type, int dimensions) {
40 | for (int i = 0; i < dimensions; i++) {
41 | type = new AArrayType(type);
42 | }
43 | return type;
44 | }
45 | private AVarDef convertToVarDef(PType type, PVarAndInit pVarAndInit) {
46 | AVarAndInit varAndInit = (AVarAndInit) pVarAndInit;
47 | type = convertToArray(type, getDimensions(varAndInit.getDims()));
48 | return new AVarDef(type, varAndInit.getId(), varAndInit.getExpr());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/java/appel/ch03/ExprEvaluator.java:
--------------------------------------------------------------------------------
1 | package appel.ch03;
2 |
3 | import java.io.IOException;
4 | import java.io.PushbackReader;
5 | import java.io.StringReader;
6 |
7 | import appel.ch03.analysis.AnalysisAdapter;
8 | import appel.ch03.lexer.Lexer;
9 | import appel.ch03.lexer.LexerException;
10 | import appel.ch03.node.*;
11 | import appel.ch03.parser.Parser;
12 | import appel.ch03.parser.ParserException;
13 |
14 | public class ExprEvaluator extends AnalysisAdapter {
15 |
16 | Value currentResult;
17 |
18 | public static int evalInt(String expr) throws ParserException, LexerException, IOException {
19 | PExpr node = parse(expr);
20 | return new ExprEvaluator().eval(node).getInt();
21 | }
22 |
23 | public static boolean evalBool(String expr) throws ParserException, LexerException, IOException {
24 | PExpr node = parse(expr);
25 | return new ExprEvaluator().eval(node).getBool();
26 | }
27 |
28 | private static PExpr parse(String expr) {
29 | try {
30 | String input = "class _{void _(){"
31 | + expr + ";}}";
32 | Lexer l = new Lexer(new PushbackReader(new StringReader(input)));
33 | Parser p = new Parser(l);
34 | Start s = p.parse();
35 | return ((AFunDef) ((AClassDef) ((ADefExpr) ((ASeqExpr) s.getPExpr()).getExpr().get(0)).getDef()).getDef().get(0)).getExpr().get(0);
36 | } catch (Exception e) {
37 | throw new RuntimeException(e);
38 | }
39 | }
40 |
41 | private void visit(Node node) {
42 | if (node != null) {
43 | node.apply(this);
44 | }
45 | }
46 |
47 | public Value eval(Node node) {
48 | this.currentResult = null;
49 | visit(node);
50 | Value result = this.currentResult;
51 | this.currentResult = null;
52 | return result;
53 | }
54 |
55 | @Override public void caseABinopExpr(ABinopExpr node) {
56 | PBinop op = node.getOp();
57 | Value left = eval(node.getLeft()), right = eval(node.getRight());
58 | if (op instanceof APlusBinop) {
59 | this.currentResult = new IntValue(left.getInt() + right.getInt());
60 | } else if (op instanceof AMinusBinop) {
61 | this.currentResult = new IntValue(left.getInt() - right.getInt());
62 | } else if (op instanceof ATimesBinop) {
63 | this.currentResult = new IntValue(left.getInt() * right.getInt());
64 | } else if (op instanceof ADivideBinop) {
65 | this.currentResult = new IntValue(left.getInt() / right.getInt());
66 | } else if (op instanceof AModBinop) {
67 | this.currentResult = new IntValue(left.getInt() % right.getInt());
68 | } else if (op instanceof AEqBinop) {
69 | if (left.getType() == Type.BOOL) {
70 | this.currentResult = new BoolValue(left.getBool() == right.getBool());
71 | } else {
72 | this.currentResult = new BoolValue(left.getInt() == right.getInt());
73 | }
74 | } else if (op instanceof ANeqBinop) {
75 | if (left.getType() == Type.BOOL) {
76 | this.currentResult = new BoolValue(left.getBool() != right.getBool());
77 | } else {
78 | this.currentResult = new BoolValue(left.getInt() != right.getInt());
79 | }
80 | } else if (op instanceof AGtBinop) {
81 | this.currentResult = new BoolValue(left.getInt() > right.getInt());
82 | } else if (op instanceof ALtBinop) {
83 | this.currentResult = new BoolValue(left.getInt() < right.getInt());
84 | } else if (op instanceof AGeBinop) {
85 | this.currentResult = new BoolValue(left.getInt() >= right.getInt());
86 | } else if (op instanceof ALeBinop) {
87 | this.currentResult = new BoolValue(left.getInt() <= right.getInt());
88 | }
89 | }
90 |
91 | @Override public void caseABoolopExpr(ABoolopExpr node) {
92 | PBoolop op = node.getOp();
93 | Value left = eval(node.getLeft()), right = eval(node.getRight());
94 | if (op instanceof AAndBoolop) {
95 | this.currentResult = new BoolValue(left.getBool() && right.getBool());
96 | } else if (op instanceof AOrBoolop) {
97 | this.currentResult = new BoolValue(left.getBool() || right.getBool());
98 | }
99 | }
100 |
101 | @Override public void caseAUarithopExpr(AUarithopExpr node) {
102 | PUarithop op = node.getOp();
103 | Value expr = eval(node.getExpr());
104 | if (op instanceof APreIncUarithop) {
105 | this.currentResult = new IntValue(expr.getInt()+1);
106 | } else if (op instanceof APreDecUarithop) {
107 | this.currentResult = new IntValue(expr.getInt()-1);
108 | } else if (op instanceof APostIncUarithop) {
109 | this.currentResult = expr;
110 | } else if (op instanceof APostDecUarithop) {
111 | this.currentResult = expr;
112 | }
113 | }
114 |
115 | @Override public void caseAUnopExpr(AUnopExpr node) {
116 | PUnop op = node.getOp();
117 | Value expr = eval(node.getExpr());
118 | if (op instanceof ANegUnop) {
119 | this.currentResult = new IntValue(-expr.getInt());
120 | } else if (op instanceof ANotUnop) {
121 | this.currentResult = new BoolValue(!expr.getBool());
122 | }
123 | }
124 |
125 | @Override public void caseABoolExpr(ABoolExpr node) {
126 | this.currentResult = eval(node.getBool());
127 | }
128 |
129 | @Override public void caseATrueBool(ATrueBool node) {
130 | this.currentResult = new BoolValue(true);
131 | }
132 | @Override public void caseAFalseBool(AFalseBool node) {
133 | this.currentResult = new BoolValue(false);
134 | }
135 |
136 | @Override public void caseANumberExpr(ANumberExpr node) {
137 | this.currentResult = eval(node.getNumber());
138 | }
139 |
140 | @Override public void caseADecimalNumber(ADecimalNumber node) {
141 | this.currentResult = new IntValue(Integer.parseInt(node.getInteger().getText()));
142 | }
143 |
144 | @Override public void caseAOctalNumber(AOctalNumber node) {
145 | this.currentResult = new IntValue(Integer.decode(node.getOctal().getText()));
146 | }
147 |
148 | @Override public void caseAHexNumber(AHexNumber node) {
149 | this.currentResult = new IntValue(Integer.decode(node.getHex().getText()));
150 | }
151 |
152 | public static enum Type { BOOL, INT, STRING };
153 | public static abstract class Value {
154 | public abstract Type getType();
155 | public int getInt() { throw new IllegalArgumentException(); }
156 | public boolean getBool() { throw new IllegalArgumentException(); }
157 | public String getString() { throw new IllegalArgumentException(); }
158 | }
159 |
160 | public static class IntValue extends Value {
161 | private final int value;
162 | public IntValue(int value) { this.value = value; }
163 | @Override public String toString() { return "" + this.value; }
164 | @Override public Type getType() { return Type.INT; }
165 | @Override public int getInt() { return value; }
166 | }
167 |
168 | public static class BoolValue extends Value {
169 | private final boolean value;
170 | BoolValue(boolean value) { this.value = value; }
171 | @Override public String toString() { return "" + this.value; }
172 | @Override public Type getType() { return Type.BOOL; }
173 | @Override public boolean getBool() { return value; }
174 | }
175 |
176 | public static class StringValue extends Value {
177 | private final String value;
178 | StringValue(String value) { this.value = value; }
179 | @Override public String toString() { return this.value; }
180 | @Override public Type getType() { return Type.BOOL; }
181 | @Override public String getString() { return value; }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/test/java/appel/ch03/ParseFailed.java:
--------------------------------------------------------------------------------
1 | package appel.ch03;
2 |
3 | import appel.ch03.node.Switch;
4 | import appel.ch03.node.Token;
5 | import appel.ch03.parser.ParserException;
6 |
7 | class ParseFailed extends Token {
8 | public String input;
9 | public Exception e;
10 | public ParseFailed(String input, Exception e) {
11 | this.input = input;
12 | this.e = e;
13 | }
14 | public String getMessage() {
15 | String indent = " ";
16 | StringBuilder s = new StringBuilder("\n").append(indent).append(e.getMessage()).append(":\n");
17 | s.append(indent).append("1 . 10 . 20 . 30 . 40 . 50 . 60 . 70 .\n");
18 | if (e instanceof ParserException) {
19 | int col = Integer.parseInt(e.getMessage().split("\\[|\\]|,")[2]);
20 | s.append(indent);
21 | for (int i = 0; i < col-1; i++) {
22 | s.append(" ");
23 | }
24 | s.append("v\n");
25 | }
26 |
27 | String[] lines = input.split("\n");
28 | for (int i = 0; i < lines.length; i++) {
29 | s.append(indent.substring(3)).append(i+1).append(": ").append(lines[i]).append('\n');
30 | }
31 | return s.toString();
32 | }
33 | @Override public void apply(Switch sw) { throw new UnsupportedOperationException(); }
34 | @Override public Object clone() { throw new UnsupportedOperationException(); }
35 | }
--------------------------------------------------------------------------------
/src/test/java/appel/ch03/TestLexer.java:
--------------------------------------------------------------------------------
1 | package appel.ch03;
2 |
3 | import static org.testng.Assert.*;
4 |
5 | import java.io.PushbackReader;
6 | import java.io.StringReader;
7 |
8 | import org.testng.annotations.Test;
9 |
10 | import appel.ch03.lexer.Lexer;
11 | import appel.ch03.node.*;
12 |
13 | @Test
14 | public class TestLexer {
15 |
16 | public void symbols() {
17 | testTokens(",:;()[]{}", TComma.class, TColon.class, TSemi.class, TLparen.class, TRparen.class, TLbrack.class, TRbrack.class, TLbrace.class, TRbrace.class);
18 | testTokens("+-*/%", TPlus.class, TMinus.class, TTimes.class, TDivide.class, TMod.class);
19 | testTokens("= += -= *= /= %=", TEq.class, TPluseq.class, TMinuseq.class, TTimeseq.class, TDivideeq.class, TModeq.class);
20 | testTokens("== != > >= < <= && ||", TEqeq.class, TNeq.class, TGt.class, TGe.class, TLt.class, TLe.class, TAndand.class, TOror.class);
21 | testTokens("! ++ --", TBang.class, TPlusplus.class, TMinusminus.class);
22 | }
23 |
24 | public void reserved() {
25 | testTokens("if else", TIf.class, TElse.class);
26 | testTokens("for do while break", TFor.class, TDo.class, TWhile.class, TBreak.class);
27 | testTokens("try catch finally throw finally", TTry.class, TCatch.class, TFinally.class, TThrow.class, TFinally.class);
28 | testTokens("new interface class extends implements this instanceof", TNew.class, TInterface.class, TClasskeyword.class, TExtends.class, TImplements.class, TThis.class, TInstanceof.class);
29 | testTokens("return true false null", TReturn.class, TTrue.class, TFalse.class, TNull.class);
30 | testTokens("void int boolean String", TVoid.class, TTypeInt.class, TTypeBoolean.class, TTypeString.class);
31 | }
32 |
33 | public void numbers() {
34 | testTokens("0 1 10", TInteger.class, TInteger.class, TInteger.class);
35 | testTokens("01 07", TOctal.class, TOctal.class);
36 | testTokens("0x0 0x0111 0xAF1", THex.class, THex.class, THex.class);
37 | testTokens("1. 1.0 0.1 .1 .111 1.111", TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class);
38 | testTokens("1e1 1e+1 1e-1 1.e1 1.e+1 1.e-1 .1e10 0.1e10 1.5e10 1.0e+10", TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class, TFloat.class);
39 | }
40 |
41 | public void strings() {
42 | testTokens("' ' 'x' '\\n' '\\t' '\\''", TChar.class, TChar.class, TChar.class, TChar.class, TChar.class);
43 | testTokens("\"\" \"string\" \"\\\"\" \"\\\"\\\"\" \"\\\"abc\\\"\\\"\\\"\"", TString.class, TString.class, TString.class, TString.class, TString.class);
44 | }
45 |
46 | public void lineComments() {
47 | testTokens("//", TComment.class);
48 | testTokens("//\n", TComment.class);
49 | testTokens("// ", TComment.class);
50 | testTokens("// this is a comment", TComment.class);
51 | testTokens( "// mutli \n// lines", TComment.class, TComment.class);
52 | testTokens("x // inline", TId.class, TComment.class);
53 | }
54 |
55 | public void comments() {
56 | testTokens("/**/", TComment.class);
57 | testTokens("/* */", TComment.class);
58 | testTokens("/*a*/", TComment.class);
59 | testTokens("/***/", TComment.class);
60 | testTokens("/*\n*/", TComment.class);
61 | testTokens("/* one line */", TComment.class);
62 | testTokens("/* multi\nline */", TComment.class);
63 | testTokens("/* if while */", TComment.class);
64 | testTokens("/* contains * */", TComment.class);
65 | testTokens("/* contains * /*/", TComment.class);
66 | testTokens("/* contains ****/", TComment.class);
67 | }
68 |
69 | private Lexer initLexer(String input) {
70 | Lexer lex = new Lexer(new PushbackReader(new StringReader(input)));
71 | return lex;
72 | }
73 |
74 | private void testTokens(String input, Class>... expectedTokens) {
75 | Lexer lex = initLexer(input);
76 | Class> tokClass;
77 | int i = 0;
78 |
79 | try {
80 | for (i = 0; i < expectedTokens.length; i++) {
81 | // Get next token, skipping whitespace unless explicitly specified in expected token list
82 | do {
83 | tokClass = lex.next().getClass();
84 | } while (tokClass == TWhitespace.class && expectedTokens[i] != TWhitespace.class);
85 | assertEquals(tokClass, expectedTokens[i], "Token " + i + " of '" + input + "' ");
86 | }
87 | // skip over any trailing whitespace
88 | do {
89 | tokClass = lex.next().getClass();
90 | } while (tokClass == TWhitespace.class);
91 |
92 | // and assert that the whole string was consumed
93 | assertEquals(tokClass, EOF.class);
94 | } catch (Exception e) {
95 | fail("Unable to get token " + i + " from " + input, e);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/appel/ch03/TestParser.java:
--------------------------------------------------------------------------------
1 | package appel.ch03;
2 |
3 | import static org.testng.Assert.*;
4 |
5 | import java.io.IOException;
6 | import java.io.PushbackReader;
7 | import java.io.StringReader;
8 |
9 | import org.testng.annotations.Test;
10 | import org.testng.log4testng.Logger;
11 |
12 | import appel.ch03.analysis.DepthFirstAdapter;
13 | import appel.ch03.lexer.Lexer;
14 | import appel.ch03.lexer.LexerException;
15 | import appel.ch03.node.*;
16 | import appel.ch03.parser.Parser;
17 | import appel.ch03.parser.ParserException;
18 |
19 | @Test
20 | public class TestParser {
21 |
22 | private static Logger LOG = Logger.getLogger(TestParser.class);
23 |
24 | public void emptyInterfaceDecl() {
25 | Node s = parse("interface MainProgram { }");
26 | assertValid(s);
27 | assertContainsSingleClassOrInterface(s, "MainProgram");
28 | }
29 |
30 | public void simpleInterfaceDecl() {
31 | Node s = parse("interface MainProgram { void Main(); }");
32 | assertValid(s);
33 | assertMethodExists(s, "Main");
34 | }
35 | public void errorInInterfaceDecl() {
36 | // 1 10 20 30
37 | Node s = parse("interface MainProgram { M; }");
38 | assertInvalid(s, 1, 1, 20, 30);
39 | s = parse("interface MainProgram { M(int a); }");
40 | assertInvalid(s, 1, 1, 20, 30);
41 | }
42 | public void interfaceDeclWithFunParams() {
43 | Node result = parse("interface MainProgram { int M(X x, Y y(A, int)); }");
44 | assertValid(result);
45 | }
46 | public void interfaceWithManyMethods() {
47 | Node s = parse(
48 | "interface MainProgram {\n" +
49 | " int x();\n" +
50 | " String y(String[] params);\n" +
51 | " int z(boolean a, int b, AnotherClass c(int[]));\n" +
52 | "}");
53 | assertValid(s);
54 | assertContainsSingleClassOrInterface(s, "MainProgram");
55 | assertMethodExists(s, "x");
56 | assertMethodExists(s, "y");
57 | assertMethodExists(s, "z");
58 | }
59 |
60 | public void emptyClassDecl() {
61 | Node s = parse("class Main { }");
62 | assertValid(s);
63 | assertContainsSingleClassOrInterface(s, "Main");
64 | }
65 |
66 | public void classWithConstructor() {
67 | Node s = parse("class Main { Main(int x) {} }");
68 | assertValid(s);
69 | assertContainsSingleClassOrInterface(s, "Main");
70 | }
71 |
72 | public void classWithVar() {
73 | Node s = parse("class Main { int[] x = 10; }");
74 | assertValid(s);
75 | }
76 |
77 | public void classWithMethod() {
78 | Node s = parse("class Main { int[] x() {} }");
79 | assertValid(s);
80 | assertMethodExists(s, "x");
81 | }
82 |
83 | public void classWithManyMembers() {
84 | Node s = parse(
85 | "class Main {\n" +
86 | " boolean[] b = true;\n" +
87 | " \n" +
88 | " Main(int arg, T obj) {}\n" +
89 | " \n" +
90 | " int x() { int a = 0e-10; }\n" +
91 | " String y(String[] params) { String[] s = params; }\n" +
92 | " int z(boolean a, int b, AnotherClass c(int[])) {\n" +
93 | " }\n" +
94 | "}");
95 | assertValid(s);
96 | assertMethodExists(s, "x");
97 | assertMethodExists(s, "y");
98 | assertMethodExists(s, "z");
99 | }
100 |
101 | public void primitiveVars() {
102 | assertValid(parseStmt("int x"));
103 | assertValid(parseStmt("boolean x, y = 0"));
104 | assertValid(parseStmt("String x"));
105 | assertValid(parseStmt("MyClass x"));
106 | }
107 |
108 | public void funDecls() {
109 | assertValid(parseStmt("void x()"));
110 | assertValid(parseStmt("int x()"));
111 | assertValid(parseStmt("String x() { x(); }"));
112 | assertValid(parseStmt("int x(boolean y, void z(int)) {}"));
113 | assertValid(parseStmt("int x(boolean y, void z(int(int,void()))) {}"));
114 |
115 | assertValid(parseStmt("int[] x()"));
116 | assertInvalid(parseStmt("int x()[]"));
117 |
118 | assertValid(parseStmt("(A r(void()))[] x(boolean y, void z())"));
119 | }
120 |
121 | public void voidVarTypeIsInvalid() {
122 | assertInvalid(parseStmt("void x"));
123 | assertInvalid(parseStmt("int x(void y)"));
124 | }
125 |
126 | public void newExpr() {
127 | assertValid(parseStmt("Main m = new Main()"));
128 | assertInvalid(parseStmt("Main m = new Main"));
129 | assertValid(parseStmt("Main[] m = new Main[10]"));
130 | assertValid(parseStmt("int[] m = new int[2]"));
131 | assertValid(parseStmt("m = new boolean[2][3][]"));
132 | assertInvalid(parseStmt("m = new boolean[2][][3]"));
133 | assertValid(parseStmt("String[][] m[][] = new String[2][1][][]"));
134 | assertInvalid(parseStmt("m = new int[]"));
135 | }
136 |
137 | public void binopExpr() {
138 | assertValid(parseStmt("1 + 2"));
139 | assertValid(parseStmt("a + b / 2 * c / (d % e && true || false)"));
140 | }
141 |
142 | public void binopPrecedence() throws ParserException, LexerException, IOException {
143 | assertEquals(ExprEvaluator.evalInt("1+2"), 3);
144 | assertEquals(ExprEvaluator.evalInt("1+2*2"), 5);
145 | assertEquals(ExprEvaluator.evalInt("(1+2)*2"), 6);
146 | assertEquals(ExprEvaluator.evalInt("1+-2"), -1);
147 | assertEquals(ExprEvaluator.evalInt("-1+2"), 1);
148 |
149 | assertEquals(ExprEvaluator.evalInt("2+2--"), 4);
150 | assertEquals(ExprEvaluator.evalInt("2+--2"), 3);
151 |
152 | assertEquals(ExprEvaluator.evalBool("1+2==3"), true);
153 | assertEquals(ExprEvaluator.evalBool("1+2==2"), false);
154 | assertEquals(ExprEvaluator.evalBool("1+2<=2"), false);
155 |
156 |
157 | assertEquals(ExprEvaluator.evalBool("true == true"), true);
158 | assertEquals(ExprEvaluator.evalBool("true || false && false"), true);
159 | assertEquals(ExprEvaluator.evalBool("(false || true) && false"), false);
160 |
161 | assertEquals(ExprEvaluator.evalBool("!true"), false);
162 | assertEquals(ExprEvaluator.evalBool("!true || true"), true);
163 | assertEquals(ExprEvaluator.evalBool("!(true || true)"), false);
164 |
165 | assertEquals(ExprEvaluator.evalBool("1+2<=2 || true"), true);
166 | }
167 |
168 | public void binopAssociativity() throws ParserException, LexerException, IOException {
169 | assertEquals(ExprEvaluator.evalInt("1-2-3"), -4);
170 | assertEquals(ExprEvaluator.evalInt("1-(2-3)"), 2);
171 | }
172 |
173 | public void unopExpr() {
174 | assertValid(parseStmt("!x; -x; x++; x--"));
175 | assertValid(parseStmt("!(-x++--)"));
176 | }
177 |
178 | public void boolExpr() {
179 | assertValid(parseStmt("1 != 2"));
180 | assertValid(parseStmt("1 != 2 + 3 * 4 == 5"));
181 | assertValid(parseStmt("1 < 2 <= 3 > 4 >= 5"));
182 | }
183 |
184 | public void assignExpr() {
185 | assertValid(parseStmt("x = 2"));
186 | assertValid(parseStmt("x = y = z"));
187 | assertValid(parseStmt("x = (f || g)"));
188 | }
189 |
190 | public void binopAssignExpr() {
191 | assertValid(parseStmt("x += 2"));
192 | assertValid(parseStmt("a += b /= 2 *= c /= (d %= e && true)"));
193 | }
194 |
195 | public void projectionExpr() {
196 | assertValid(parseStmt("x.y"));
197 | assertValid(parseStmt("x.y()"));
198 | assertValid(parseStmt("x.y[2]"));
199 | assertValid(parseStmt("x.y.a.b = 12"));
200 | assertValid(parseStmt("x.y.a.b = c.d"));
201 | assertInvalid(parseStmt("x.1"));
202 | }
203 |
204 | public void subscriptExpr() {
205 | assertValid(parseStmt("x[1]"));
206 | assertValid(parseStmt("x[1][2][3].y.a.b = 12"));
207 | assertValid(parseStmt("x.y.a.b[3] = c[1][2].d[3]"));
208 | assertInvalid(parseStmt("x[]"));
209 | }
210 |
211 | public void applyExpr() {
212 | assertValid(parseStmt("x()"));
213 | assertValid(parseStmt("x(12, 'b', f(), !(-1 + 2))"));
214 | assertInvalid(parseStmt("x(int a)"));
215 | }
216 |
217 | public void forLoop() {
218 | assertValid(parseStmt("for (;;);"));
219 | assertValid(parseStmt("for (i = 0; i < 10; i++);"));
220 | assertValid(parseStmt("for (i = 0; i < 10; i++) {}"));
221 | assertValid(parseStmt("for (i = 0; i < 10; i++) { f(i); }"));
222 | }
223 |
224 | public void whileLoop() {
225 | assertValid(parseStmt("while (true);"));
226 | assertValid(parseStmt("while (true) {}"));
227 | assertValid(parseStmt("while ((i = s.length()) < 10) { f(i); }"));
228 | }
229 |
230 | public void returnStmt() {
231 | assertValid(parseStmt("return true;"));
232 | }
233 |
234 | public void breakStmt() {
235 | assertValid(parseStmt("for (;;) { break; }"));
236 | }
237 |
238 | public void tryCatchStmt() {
239 | assertValid(parseStmt("try {} catch (Exception e) {}"));
240 | assertInvalid(parseStmt("try {}"));
241 | assertInvalid(parseStmt("try {} catch ()"));
242 | assertInvalid(parseStmt("try {} catch () {}"));
243 | assertInvalid(parseStmt("try catch (Exception e) {}"));
244 | }
245 |
246 | public void ifStmt() {
247 | assertValid(parseStmt("if (true);"));
248 | assertValid(parseStmt("if (true) {} else {}"));
249 | assertValid(parseStmt("if (true) ; else ;"));
250 | }
251 |
252 | public void nestedIfStmt() {
253 | assertValid(parseStmt("if (true) if (true) {} else {}"));
254 | assertValid(parseStmt("if (true) if (true) {} else {} else {}"));
255 | }
256 |
257 | public void elseIfStmt() {
258 | assertValid(parseStmt("if (true) {} else if (true) {}"));
259 | assertValid(parseStmt("if (true) {} else if (true) {} else {}"));
260 | }
261 |
262 | private Node parse(String input) {
263 | try {
264 | Lexer l = new Lexer(new PushbackReader(new StringReader(input)));
265 | Parser p = new Parser(l);
266 | Start s = p.parse();
267 | s.apply(new ASTCleaner());
268 | return s;
269 | } catch (Exception e) {
270 | return new ParseFailed(input, e);
271 | }
272 | }
273 |
274 | private Node parseStmt(String stmt) {
275 | // 1 10 20
276 | Node s = parse("class _{void _(){ " + stmt + ";}}");
277 | if (!(s instanceof ParseFailed)) {
278 | LOG.debug(stmt + " ->\n" +
279 | nodeToString(((AFunDef) ((AClassDef) ((ADefExpr) ((ASeqExpr) ((Start) s).getPExpr()).getExpr().get(0)).getDef()).getDef().get(0)).getExpr().get(0)));
280 | }
281 | return s;
282 | }
283 |
284 | private void assertValid(Node parseResult) {
285 | if (parseResult instanceof ParseFailed) {
286 | ParseFailed r = (ParseFailed) parseResult;
287 | fail(r.getMessage(), r.e);
288 | }
289 | }
290 |
291 | private void assertInvalid(Node parseResult) {
292 | assertInvalid(parseResult, -1, -1, -1, -1);
293 | }
294 |
295 | private void assertInvalid(Node parseResult, int linestart, int lineend, int colstart, int colend) {
296 | if (!(parseResult instanceof ParseFailed)) {
297 | fail("Expected parse error but succeeded");
298 | }
299 | ParseFailed r = (ParseFailed) parseResult;
300 | if (!(r.e instanceof ParserException)) {
301 | fail("Expected a parse exception, but got " + r.e.getMessage(), r.e);
302 | }
303 | String[] pos = r.e.getMessage().split("\\[|\\]|,");
304 | int line = Integer.parseInt(pos[1]), col = Integer.parseInt(pos[2]);
305 | assertTrue(linestart < 0 || (linestart <= line && lineend >= line), "\n Error at unexpected position: " + r.getMessage());
306 | assertTrue(colstart < 0 || (colstart <= col && colend >= col), "\n Error at unexpected position: " + r.getMessage());
307 | }
308 |
309 | private void assertContainsSingleClassOrInterface(final Node s, final String name) {
310 | s.apply(new DepthFirstAdapter() {
311 | boolean found = false;
312 | @Override public void inAClassDef(AClassDef node) {
313 | assertFalse(found, "Should only find 1 class/interface");
314 | found = true;
315 | assertEquals(node.getId().getText(), name);
316 | }
317 | @Override public void inAInterfaceType(AInterfaceType node) {
318 | assertFalse(found, "Should only find 1 class/interface");
319 | found = true;
320 | assertEquals(node.getId().getText(), name);
321 | }
322 | @Override public void outStart(Start node) {
323 | assertTrue(found);
324 | }
325 | });
326 | }
327 | private void assertMethodExists(final Node s, final String name) {
328 | s.apply(new DepthFirstAdapter() {
329 | boolean found = false;
330 | @Override public void inAFunDef(AFunDef node) {
331 | if (name.equals(node.getId().getText())) { found = true; }
332 | }
333 | @Override public void outStart(Start node) {
334 | assertTrue(found, "Method " + name + " not found.");
335 | }
336 | });
337 | }
338 |
339 | private String nodeToString(Node s) {
340 | final StringBuilder ast = new StringBuilder();
341 | s.apply(new DepthFirstAdapter() {
342 | int indent = 0;
343 | void append(Node n) {
344 | StringBuilder s = new StringBuilder();
345 | for (int i = 0; i < indent; i++) {
346 | s.append("| ");
347 | }
348 | s.append(n.getClass().toString().replaceFirst("class appel.ch03.node.A?", "")).append(getText(n));
349 | ast.append(s).append("\n");
350 | }
351 | String getText(Node n) {
352 | String s = null;
353 | if (n instanceof ANumberExpr || n instanceof ABoolExpr || n instanceof ACharExpr || n instanceof AStringExpr || n instanceof AVarExpr) {
354 | s = n.toString();
355 | } else if (n instanceof AClassDef) {
356 | s = ((AClassDef) n).getId().toString();
357 | } else if (n instanceof AFunDef) {
358 | s = ((AFunDef) n).getId().toString().trim() + "(" + ((AFunDef) n).getFormal().toString() + ")";
359 | }
360 | if (s != null) {
361 | return "(" + s.trim() + ")";
362 | }
363 | return "";
364 | }
365 | @Override public void defaultIn(Node node) {
366 | append(node);
367 | indent++;
368 | }
369 | @Override public void defaultOut(Node node) {
370 | indent--;
371 | }
372 | });
373 | return ast.toString();
374 | }
375 | }
--------------------------------------------------------------------------------
/src/test/java/util/TestFieldMTable.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 | import static org.testng.Assert.*;
4 | import java.util.LinkedList;
5 | import java.util.List;
6 | import org.testng.annotations.Test;
7 | import appel.ch07.Symbol;
8 |
9 | public class TestFieldMTable {
10 | @Test public void testTable() {
11 | FieldMTable t = FieldMTable.create();
12 | java.util.Random gen = new java.util.Random();
13 |
14 | for (int i = 0; i < 1000; i++) {
15 | Symbol internal = new Symbol.Var("i");
16 | Symbol external = new Symbol.Var("e", gen.nextInt(100));
17 | int v = gen.nextInt(10000);
18 |
19 | t = t.add(external, internal, v);
20 |
21 | // Check that (internal,value) is returns for external symbol
22 | assertTrue(t.contains(external));
23 | boolean found = false;
24 | for (FieldMTable.Entry entry : t.get(external)) {
25 | if (internal.equals(entry.symbol) && v == entry.value) {
26 | found = true;
27 | }
28 | }
29 | assertTrue(found, external + ", " + internal + " -> " + v + ") not found in table");
30 |
31 | // Check that (external,value) is returns for internal symbol
32 | FieldMTable.Entry entry = t.getInternal(internal);
33 | assertEquals(entry.symbol, external);
34 | assertEquals(entry.value.intValue(), v);
35 | }
36 | }
37 |
38 | @Test public void internalSymbolsReturnedInInsertionOrder() {
39 | FieldMTable t = FieldMTable.create();
40 | List internals = new LinkedList();
41 | java.util.Random gen = new java.util.Random();
42 |
43 | for (int i = 0; i < 1000; i++) {
44 | Symbol internal = new Symbol.Var("i");
45 | Symbol external = new Symbol.Var("e", gen.nextInt(100));
46 | int v = gen.nextInt(10000);
47 |
48 | internals.add(internal);
49 | t = t.add(external, internal, v);
50 | }
51 |
52 | List symbols = t.internals();
53 | for (int i = 0; i < symbols.size(); i++) {
54 | assertEquals(symbols.get(0), internals.get(0));
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/java/util/TestTable.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 | import org.testng.annotations.Test;
4 | import static org.testng.Assert.*;
5 |
6 | public class TestTable {
7 | @Test public void testRBTree() {
8 | Table t = Table. create();
9 | java.util.Random gen = new java.util.Random();
10 |
11 | for (int i = 0; i < 1000; i++) {
12 | Integer k = gen.nextInt(10000);
13 | Integer v = gen.nextInt(10000);
14 | t = t.put(k, v);
15 | assertTrue(t.contains(k));
16 | assertEquals(t.get(k), v);
17 | assertCorrectRBTree(t);
18 | }
19 | }
20 |
21 | private static void assertCorrectRBTree(Table,?> t) {
22 | assertNodesColored(t);
23 | assertRootIsBlack(t);
24 | assertRedNodesHaveTwoBlackChildren(t);
25 | assertSameNumberOfBlackNodesInAllPaths(t, 0, -1);
26 | }
27 |
28 | private static void assertNodesColored(Table,?> n) {
29 | if (n == null)
30 | return;
31 | assertTrue(n.isRed() || n.isBlack());
32 | assertNodesColored(n.left());
33 | assertNodesColored(n.right());
34 | }
35 |
36 | private static void assertRootIsBlack(Table,?> root) {
37 | assertTrue(root.isBlack());
38 | }
39 |
40 | private static void assertRedNodesHaveTwoBlackChildren(Table,?> t) {
41 | if (t == null)
42 | return;
43 | if (t.isRed()) {
44 | assertNotNull(t.left());
45 | assertNotNull(t.right());
46 | assertTrue(t.left().isBlack());
47 | assertTrue(t.right().isBlack());
48 | }
49 | assertRedNodesHaveTwoBlackChildren(t.left());
50 | assertRedNodesHaveTwoBlackChildren(t.right());
51 | }
52 |
53 | private static int assertSameNumberOfBlackNodesInAllPaths(Table,?> t, int blackCount, int pathBlackCount) {
54 | if (t == null) {
55 | if (pathBlackCount == -1) {
56 | pathBlackCount = blackCount;
57 | } else {
58 | assertEquals(blackCount, pathBlackCount);
59 | }
60 | return pathBlackCount;
61 | }
62 | if (t.isBlack()) {
63 | blackCount++;
64 | }
65 | pathBlackCount = assertSameNumberOfBlackNodesInAllPaths(t.left(), blackCount, pathBlackCount);
66 | pathBlackCount = assertSameNumberOfBlackNodesInAllPaths(t.right(), blackCount, pathBlackCount);
67 | return pathBlackCount;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/test/resources/log4testng.properties:
--------------------------------------------------------------------------------
1 | log4testng.rootLogger=WARN
2 | log4testng.logger.appel.ch03.TestParser=DEBUG
--------------------------------------------------------------------------------