├── .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 --------------------------------------------------------------------------------