From 8fa96cb362ba6a604a409b939ee3e0a43b892e20 Mon Sep 17 00:00:00 2001 From: User <> Date: Fri, 20 Oct 2017 14:16:11 +0200 Subject: [PATCH] extracted strings, sorted methods, added constants, added procedures, added more tests, restructured parser rules --- src/pp/iloc/Simulator.java | 85 ++- src/pp/iloc/eval/Machine.java | 22 +- src/pp/iloc/eval/Memory.java | 77 ++- src/pp/s1184725/boppi/Annotations.java | 8 + src/pp/s1184725/boppi/BoppiChecker.java | 234 +++++++-- src/pp/s1184725/boppi/BoppiGenerator.java | 495 ++++++++++-------- src/pp/s1184725/boppi/CachingSymbolTable.java | 218 ++++---- .../boppi/DebugCachingSymbolTable.java | 26 +- src/pp/s1184725/boppi/FunctionScope.java | 18 +- src/pp/s1184725/boppi/Messages.java | 31 ++ src/pp/s1184725/boppi/ToolChain.java | 296 +++++------ src/pp/s1184725/boppi/Variable.java | 75 ++- src/pp/s1184725/boppi/antlr/Boppi.g4 | 53 +- src/pp/s1184725/boppi/messages.properties | 24 + src/pp/s1184725/boppi/test/BoppiTests.java | 27 +- src/pp/s1184725/boppi/test/ClosureTest.java | 27 +- .../boppi/test/SimpleFunctionTest.java | 23 +- .../boppi/test/SimpleVariableTest.java | 21 +- .../boppi/test/programs/closure1.boppi | 2 + .../boppi/test/programs/closure2.boppi | 3 + .../boppi/test/programs/closure3.boppi | 3 + .../test/programs/fibonacciIterative.boppi | 1 + .../test/programs/fibonacciRecursive.boppi | 44 ++ .../boppi/test/programs/simpleProcedure.boppi | 12 + .../boppi/util/InteractivePrompt.java | 6 +- src/pp/s1184725/boppi/util/RegisterPool.java | 152 +++--- 26 files changed, 1283 insertions(+), 700 deletions(-) create mode 100644 src/pp/s1184725/boppi/Messages.java create mode 100644 src/pp/s1184725/boppi/messages.properties create mode 100644 src/pp/s1184725/boppi/test/programs/fibonacciRecursive.boppi create mode 100644 src/pp/s1184725/boppi/test/programs/simpleProcedure.boppi diff --git a/src/pp/iloc/Simulator.java b/src/pp/iloc/Simulator.java index e1f2a58..3f15a21 100644 --- a/src/pp/iloc/Simulator.java +++ b/src/pp/iloc/Simulator.java @@ -13,6 +13,7 @@ import pp.iloc.model.Num; import pp.iloc.model.Op; import pp.iloc.model.OpClaz; import pp.iloc.model.OpCode; +import pp.iloc.model.Operand.Type; import pp.iloc.model.Program; import pp.iloc.parse.FormatException; @@ -101,10 +102,8 @@ public class Simulator { Op o = this.prg.getOpAt(this.vm.getPC()); OPContext c = new OPContext(o); Machine vm = this.vm; - if (DEBUG) { - System.out.printf("Op %d: %s%n", vm.getPC(), o); - System.out.println(vm); - } + String pre = c.toPreString(); + switch (o.getOpCode()) { case nop: // do nothing @@ -328,6 +327,13 @@ public class Simulator { this.vm.incPC(); return; } + + if (DEBUG) { + System.out.printf("Op %d: %s%n", vm.getPC(), o); + System.out.printf("Op %d: %s%n", vm.getPC(), pre+c.toPostString()); + System.out.println(vm); + } + if (o.getClaz() != OpClaz.CONTROL) { this.vm.incPC(); } @@ -474,5 +480,76 @@ public class Simulator { public int label(int ix) { return getProgram().getLine(this.op.label(ix)); } + + protected String toPreString() { + OpCode opcode = op.getOpCode(); + StringBuilder sb = new StringBuilder(); + sb.append(opcode.toString()); + sb.append(" "); + + for (int i = 0; i < opcode.getSigSize(); i++) { + if (i == opcode.getSourceCount()) + sb.append(" -> "); + else if (i > 0) + sb.append(", "); + + if (i >= opcode.getSourceCount()) + break; + + try { + switch (opcode.getSig().get(i)) { + case LABEL: + sb.append(String.format("%d", label(i))); + break; + case NUM: + sb.append(String.format("%8x", num(i)).replace(" ", ".")); + break; + case REG: + sb.append(String.format("%8x", reg(i)).replace(" ", ".")); + break; + case STR: + sb.append(""); + break; + } + } + catch (IllegalArgumentException e) { + sb.append("undefine"); + } + } + + return sb.toString(); + } + + protected String toPostString() { + OpCode opcode = op.getOpCode(); + StringBuilder sb = new StringBuilder(); + + for (int i = opcode.getSourceCount(); i < opcode.getSigSize(); i++) { + if (i > opcode.getSourceCount()) + sb.append(", "); + + try { + switch (opcode.getSig().get(i)) { + case LABEL: + sb.append(String.format("%d", label(i))); + break; + case NUM: + sb.append(String.format("%8x", num(i)).replace(" ", ".")); + break; + case REG: + sb.append(String.format("%8x", reg(i)).replace(" ", ".")); + break; + case STR: + sb.append(""); + break; + } + } + catch (IllegalArgumentException e) { + sb.append("undefine"); + } + } + + return sb.toString(); + } } } diff --git a/src/pp/iloc/eval/Machine.java b/src/pp/iloc/eval/Machine.java index f8177c8..cab8936 100644 --- a/src/pp/iloc/eval/Machine.java +++ b/src/pp/iloc/eval/Machine.java @@ -2,6 +2,7 @@ package pp.iloc.eval; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; import pp.iloc.model.Num; import pp.iloc.model.Reg; @@ -292,11 +293,10 @@ public class Machine { } /** - * Sets the interrupt value of the machine. - * @param interruptCode the new interrupt value + * Clears the interrupt value of the machine. */ - public void setInterrupt(int interruptCode) { - this.interrupt = interruptCode; + public void clearInterrupt() { + this.interrupt = 0; } /** @@ -307,6 +307,14 @@ public class Machine { return interrupt; } + /** + * Sets the interrupt value of the machine. + * @param interruptCode the new interrupt value + */ + public void setInterrupt(int interruptCode) { + this.interrupt = interruptCode; + } + /** Clears the registers, constants, memory, PC and interrupt. */ public void clear() { this.registers.clear(); @@ -321,7 +329,9 @@ public class Machine { @Override public String toString() { - return String.format("Registers: %s%nConstants: %s%nMemory: %s%n", - this.registers, this.symbMap, this.memory); + String regs = this.registers.entrySet().parallelStream().sorted((el1, el2) -> el1.getKey().compareTo(el2.getKey())).map((el) -> el.getKey()+": "+String.format("%8x", el.getValue()).replace(" ", ".")).collect(Collectors.joining(", ", "{", "}")); + + return String.format("Registers: %s%nConstants: %s%nMemory: %n%s%n", + regs, this.symbMap, this.memory); } } diff --git a/src/pp/iloc/eval/Memory.java b/src/pp/iloc/eval/Memory.java index b92f4dd..497f516 100644 --- a/src/pp/iloc/eval/Memory.java +++ b/src/pp/iloc/eval/Memory.java @@ -2,6 +2,8 @@ package pp.iloc.eval; import java.util.Arrays; +import pp.s1184725.boppi.ToolChain; + /** Simulated memory. */ public class Memory { /** The default size of the memory, in number of bytes. */ @@ -40,17 +42,76 @@ public class Memory { @Override public String toString() { StringBuilder result = new StringBuilder(); - for (int i = 0; i < size(); i++) { - if (get(i) == 0) { - continue; +// boolean wasEmpty = false; +// for (int i = 0; i < size(); i++) { +//// if (get(i) == 0) { +//// continue; +//// } +//// if (result.length() > 0) { +//// result.append(", "); +//// } +//// result.append(i); +//// result.append(":"); +//// result.append(String.format("%02X", get(i) & 0xFF)); +// if (i % 32 == 0) { +// boolean anyNonzero = false; +// +// for (int j = i; j < i+32 && j < size(); j++) +// if ((get(j) & 0xFF) != 0) { +// anyNonzero = true; +// break; +// } +// +// if (!anyNonzero) { +// if (!wasEmpty) +// result.append("\n. . ."); +// wasEmpty = true; +// +// i += 31; +// continue; +// } +// else +// wasEmpty = false; +// +// result.append("\n"); +// result.append(String.format("%08x ", i)); +// } +// if ((get(i) & 0xFF) != 0) +// result.append(String.format("%02X", get(i) & 0xFF)); +// else +// result.append(".."); +// if (i % 4 == 3) +// result.append(" "); +// } + + int loc = 4; + + System.err.flush(); + + while (loc != 0 && loc < ToolChain.machine.getReg("brk")) { + int size = ToolChain.machine.load(loc+4); + int nloc = ToolChain.machine.load(loc); + + if (!(size == 0 && nloc == 0)) { + result.append(String.format("0x%05x ", loc+8)); + + if ((nloc >= ToolChain.machine.load(0) && nloc > loc) || nloc == 0) + result.append(" free"); + else + result.append(String.format("% 2d ref ", ToolChain.machine.load(loc))); + + for(int i = 0; i < size && i < 40; i += 4) { + if (ToolChain.machine.load(loc+8+i) == 0) + result.append("........"); + else + result.append(String.format("%8x", ToolChain.machine.load(loc+8+i)).replace(" ", ".")); + result.append(" "); } - if (result.length() > 0) { - result.append(", "); + result.append(size > 40 ? "...\n" : "\n"); } - result.append(i); - result.append(":"); - result.append(String.format("%02X", get(i) & 0xFF)); + loc += size+8; } + return result.toString(); } } diff --git a/src/pp/s1184725/boppi/Annotations.java b/src/pp/s1184725/boppi/Annotations.java index 1be492b..1296e7a 100644 --- a/src/pp/s1184725/boppi/Annotations.java +++ b/src/pp/s1184725/boppi/Annotations.java @@ -1,5 +1,8 @@ package pp.s1184725.boppi; +import java.util.*; + +import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTreeProperty; import pp.iloc.model.Reg; @@ -31,6 +34,10 @@ public class Annotations { * A symbol table */ public CachingSymbolTable symbols; + /** + * Maps variable instances to the AST node they reside in. + */ + public Map,ParserRuleContext> variableRoot; /** * Creates a new annotations object with empty maps. @@ -41,5 +48,6 @@ public class Annotations { function = new ParseTreeProperty<>(); variables = new ParseTreeProperty<>(); symbols = new CachingSymbolTable<>(); + variableRoot = new HashMap<>(); } } diff --git a/src/pp/s1184725/boppi/BoppiChecker.java b/src/pp/s1184725/boppi/BoppiChecker.java index 0b4a2a4..7528963 100644 --- a/src/pp/s1184725/boppi/BoppiChecker.java +++ b/src/pp/s1184725/boppi/BoppiChecker.java @@ -20,6 +20,7 @@ import pp.s1184725.boppi.type.*; public class BoppiChecker extends BoppiBaseVisitor { private Annotations an; private Logger log; + private boolean inLhs, inType; /** * Checks and annotates a program. Problems are reported to the given @@ -40,6 +41,8 @@ public class BoppiChecker extends BoppiBaseVisitor { protected BoppiChecker(Logger logger, Annotations annotations) { an = annotations; log = logger; + inLhs = false; + inType = false; } /** @@ -54,7 +57,7 @@ public class BoppiChecker extends BoppiBaseVisitor { */ private void checkConstraint(Type type1, Type type2, ParserRuleContext node) { if (!type2.equals(type1)) - log.severe(getError(node, "Could not match type %s with type %s.", type1.toString(), type2.toString())); + log.severe(getError(node, Messages.getString("BoppiChecker.0"), type1.toString(), type2.toString())); //$NON-NLS-1$ } /** @@ -70,7 +73,33 @@ public class BoppiChecker extends BoppiBaseVisitor { private String getError(ParserRuleContext node, String message, Object... args) { int line = node.getStart().getLine(); int column = node.getStart().getCharPositionInLine(); - return String.format("Line %d:%d - %s", line, column, String.format(message, args)); + return String.format(Messages.getString("BoppiChecker.1"), line, column, String.format(message, args)); //$NON-NLS-1$ + } + + /** + * Returns whether the current context resides within a given rule context + * within a set root (may be {@code null}). Effectively traverses between + * {@code current} and {@code root} in the parent hierarchy looking for a + * {@code rule} class. + * + * @param + * the rule class + * @param ctx + * the node to start traversing + * @param root + * the node to stop traversing + * @param rule + * the ParserRuleContext class to look for + * @return whether a matching rule is found between the current and root + * node + */ + private boolean isWithinRule(ParserRuleContext ctx, ParserRuleContext root, + Class rule) { + for (ParserRuleContext node = ctx; node != root && node != null; node = node.getParent()) + if (node.getClass().isAssignableFrom(rule)) + return true; + + return false; } @Override @@ -82,15 +111,17 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitAssign(AssignContext ctx) { - Type expr = visit(ctx.singleExpr()); + Type expr = visit(ctx.assignStat() != null ? ctx.assignStat() : ctx.expr()); + inLhs = true; Type var = visit(ctx.variable()); + inLhs = false; checkConstraint(expr, var, ctx); return expr; } @Override public Type visitBlock(BlockContext ctx) { - return an.symbols.withScope(() -> visit(ctx.expr())); + return an.symbols.withScope(() -> visit(ctx.stats())); } @Override @@ -101,14 +132,14 @@ public class BoppiChecker extends BoppiBaseVisitor { FunctionType type = (FunctionType) t; TupleType parameters = (TupleType) type.getParameter(); if (parameters.size() != ctx.expr().size()) - log.severe(getError(ctx, "Expected %d arguments but got %d.", parameters.size(), ctx.expr().size())); + log.severe(getError(ctx, Messages.getString("BoppiChecker.2"), parameters.size(), ctx.expr().size())); //$NON-NLS-1$ for (int i = 0; i < ctx.expr().size() && i < parameters.size(); i++) checkConstraint(visit(ctx.expr(i)), parameters.get(i), ctx.expr(i)); return type.getReturn(); } else { - log.severe(getError(ctx, "'%s' is not a function.", ctx.variable().getText())); + log.severe(getError(ctx, Messages.getString("BoppiChecker.3"), ctx.variable().getText())); //$NON-NLS-1$ return SimpleType.VOID; } } @@ -117,16 +148,18 @@ public class BoppiChecker extends BoppiBaseVisitor { public Type visitDeclare(DeclareContext ctx) { try { Variable var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type())); + var.setConstant(ctx.CONSTANT() != null); an.variables.put(ctx, var); + an.variableRoot.put(var, ctx.getParent()); if (var.getType() instanceof TupleType) - log.severe(getError(ctx, "Variable must have a type %s, %s, %s or function.", + log.severe(getError(ctx, Messages.getString("BoppiChecker.4"), //$NON-NLS-1$ BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.BOOLTYPE), BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.CHARTYPE), BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.INTTYPE))); - if (!(var.getType() instanceof SimpleType)) - log.warning("Be careful only to pass pure functions outside their scope."); + if (var.getType() instanceof ReferenceType) + var.assign(); } catch (SymbolTableException e) { log.severe(getError(ctx, e.getMessage())); } @@ -136,21 +169,28 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitDeclareFunction(DeclareFunctionContext ctx) { try { - TupleType parameterTypes = new TupleType( - ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList())); - FunctionType type = new FunctionType(visit(ctx.result), parameterTypes); + TupleType parameterTypes; + if (ctx.parameters() != null) + parameterTypes = new TupleType( + ctx.parameters().type().stream().map(this::visit).collect(Collectors.toList())); + else + parameterTypes = TupleType.UNIT; + + Type returnType = ctx.result != null ? visit(ctx.result) : SimpleType.VOID; + FunctionType type = new FunctionType(returnType, parameterTypes); + Variable func = an.symbols.put(ctx.name.getText(), type); + func.assign(); an.variables.put(ctx, func); an.function.put(ctx, an.symbols.withFunctionScope(() -> { - for (int i = 1; i < ctx.type().size(); i++) - try { - an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i))); - } catch (SymbolTableException e) { - log.severe(getError(ctx, e.getMessage())); - } + if (ctx.parameters() != null) + visit(ctx.parameters()); - checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx); + Type resultType = an.symbols.withScope(() -> visit(ctx.body)); + + if (returnType != SimpleType.VOID) + checkConstraint(resultType, returnType, ctx); })); } catch (SymbolTableException e) { log.severe(getError(ctx, e.getMessage())); @@ -159,15 +199,6 @@ public class BoppiChecker extends BoppiBaseVisitor { return SimpleType.VOID; } - @Override - public Type visitExpr(ExprContext ctx) { - ParserRuleContext lastExpr = ctx.singleExpr(ctx.singleExpr().size() - 1); - if (lastExpr instanceof DeclareContext || lastExpr instanceof DeclareFunctionContext) - log.severe(getError(ctx, "Compound expression ends with declaration.")); - - return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get(); - } - @Override public Type visitGetVariable(GetVariableContext ctx) { return visit(ctx.variable()); @@ -238,6 +269,36 @@ public class BoppiChecker extends BoppiBaseVisitor { return SimpleType.BOOL; } + @Override + public Type visitLambda(LambdaContext ctx) { + // try { + // TupleType parameterTypes = new TupleType( + // ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList())); + // FunctionType type = new FunctionType(visit(ctx.result), + // parameterTypes); + // Variable func = an.symbols.put(ctx.name.getText(), type); + // an.variables.put(ctx, func); + // + // an.function.put(ctx, an.symbols.withFunctionScope(() -> { + // for (int i = 1; i < ctx.type().size(); i++) + // try { + // an.symbols.put(ctx.IDENTIFIER(i).getText(), + // an.types.get(ctx.type(i))); + // } catch (SymbolTableException e) { + // log.severe(getError(ctx, e.getMessage())); + // } + // + // checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), + // an.types.get(ctx.result), ctx); + // })); + // } catch (SymbolTableException e) { + // log.severe(getError(ctx, e.getMessage())); + // } + log.severe("Lambda not yet implemented"); //$NON-NLS-1$ + + return SimpleType.VOID; + } + @Override public Type visitLiteralBoolean(LiteralBooleanContext ctx) { return SimpleType.BOOL; @@ -255,27 +316,38 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitParens(ParensContext ctx) { - return visit(ctx.expr()); + return visit(ctx.stats()); } @Override public Type visitPrefix1(Prefix1Context ctx) { - Type type = visit(ctx.singleExpr()); + Type type = visit(ctx.expr()); switch (ctx.op.getType()) { case BoppiLexer.NOT: - checkConstraint(type, SimpleType.BOOL, ctx.singleExpr()); + checkConstraint(type, SimpleType.BOOL, ctx.expr()); break; case BoppiLexer.PLUS: case BoppiLexer.MINUS: - checkConstraint(type, SimpleType.INT, ctx.singleExpr()); + checkConstraint(type, SimpleType.INT, ctx.expr()); break; } return type; } + @Override + public Type visitParameters(ParametersContext ctx) { + for (int i = 0; i < ctx.type().size(); i++) + try { + an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i))).assign(); + } catch (SymbolTableException e) { + log.severe(getError(ctx, e.getMessage())); + } + return null; + } + @Override public Type visitProgram(ProgramContext ctx) { an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx))); @@ -284,24 +356,46 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitRead(ReadContext ctx) { + Type result; + + inLhs = true; if (ctx.variable().size() == 1) { - return visit(ctx.variable(0)); + result = visit(ctx.variable(0)); } else { ctx.variable().forEach(this::visit); - return SimpleType.VOID; + result = SimpleType.VOID; } + inLhs = false; + + return result; } - + + @Override + public Type visitStat(StatContext ctx) { + return visit(ctx.getChild(0)); + } + + @Override + public Type visitStats(StatsContext ctx) { + Type resultType = ctx.stat().stream().map(this::visit).reduce((__, snd) -> snd).get(); + + ParseTree lastExpr = ctx.stat(ctx.stat().size() - 1).getChild(0); + if (lastExpr instanceof DeclareContext || lastExpr instanceof DeclareFunctionContext) + log.severe(getError(ctx, Messages.getString("BoppiChecker.5"))); //$NON-NLS-1$ + + return resultType; + } + @Override public Type visitTypeArray(TypeArrayContext ctx) { int size = 0; - + try { size = Integer.parseInt(ctx.LITERAL10().getText()); - }catch (NumberFormatException e) { - log.severe(getError(ctx, "Error parsing number '%s'", ctx.LITERAL10().getText())); + } catch (NumberFormatException e) { + log.severe(getError(ctx, Messages.getString("BoppiChecker.6"), ctx.LITERAL10().getText())); //$NON-NLS-1$ } - + return new ArrayType(size, visit(ctx.type())); } @@ -322,40 +416,44 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitTypeVariable(TypeVariableContext ctx) { - return visit(ctx.variable()); + inType = true; + Type type = visit(ctx.variable()); + inType = false; + return type; } @Override public Type visitVariableArray(VariableArrayContext ctx) { - Type t = visit(ctx.variable()); - + boolean wasOnLhs = inLhs; + inLhs = false; checkConstraint(visit(ctx.expr()), SimpleType.INT, ctx); + inLhs = wasOnLhs; + + Type t = visit(ctx.variable()); if (t instanceof ArrayType) { ArrayType arrayType = (ArrayType) t; return arrayType.getType(); } else { - log.severe(getError(ctx, "Expected array type but got %s", t)); + log.severe(getError(ctx, Messages.getString("BoppiChecker.7"), t)); //$NON-NLS-1$ return SimpleType.VOID; } } - + @Override public Type visitVariableProperty(VariablePropertyContext ctx) { Type varType = visit(ctx.variable()); String prop = ctx.IDENTIFIER().getText(); - + if (varType instanceof ArrayType) { - if (prop.equals("length")) { + if (prop.equals("length")) { //$NON-NLS-1$ return SimpleType.INT; - } - else { - log.severe(getError(ctx, "Unknown array property '%s'.", prop)); + } else { + log.severe(getError(ctx, Messages.getString("BoppiChecker.9"), prop)); //$NON-NLS-1$ return SimpleType.VOID; } - } - else { - log.severe(getError(ctx, "Record types not implemented.")); + } else { + log.severe(getError(ctx, Messages.getString("BoppiChecker.10"))); //$NON-NLS-1$ return SimpleType.VOID; } } @@ -365,6 +463,34 @@ public class BoppiChecker extends BoppiBaseVisitor { try { Variable var = an.symbols.get(ctx.getText()); + if (!inType) { + if (inLhs) { + if (var.isConstant()) { + if (var.isAssigned()) + log.severe(getError(ctx, Messages.getString("BoppiChecker.8"), ctx.getText())); //$NON-NLS-1$ + else if (var.isPossiblyAssigned()) + log.warning(getError(ctx, Messages.getString("BoppiChecker.13"), ctx.getText())); //$NON-NLS-1$ + } + + if (inLhs) { + if (isWithinRule(ctx, an.variableRoot.get(var), IfContext.class) + || isWithinRule(ctx, an.variableRoot.get(var), WhileContext.class) + || isWithinRule(ctx, an.variableRoot.get(var), DeclareFunctionContext.class)) + var.possiblyAssign(); + else + var.assign(); + } + } else { + if (!var.isAssigned()) { + if (var.isPossiblyAssigned() + || isWithinRule(ctx, an.variableRoot.get(var), DeclareFunctionContext.class)) + log.warning(getError(ctx, Messages.getString("BoppiChecker.14"), ctx.getText())); //$NON-NLS-1$ + else + log.severe(getError(ctx, Messages.getString("BoppiChecker.15"), ctx.getText())); //$NON-NLS-1$ + } + } + } + try { an.function.put(ctx, an.symbols.getFunctionScope()); } catch (NoProgramException e) { @@ -393,13 +519,13 @@ public class BoppiChecker extends BoppiBaseVisitor { if (ctx.expr().size() == 1) { Type type = visit(ctx.expr(0)); if (SimpleType.VOID.equals(type)) - log.severe(getError(ctx, "Cannot print argument of type %s.", type)); + log.severe(getError(ctx, Messages.getString("BoppiChecker.11"), type)); //$NON-NLS-1$ return type; } else { ctx.expr().stream().map(this::visit).forEach((type) -> { if (SimpleType.VOID.equals(type)) - log.severe(getError(ctx, "Cannot print argument of type %s.", type)); + log.severe(getError(ctx, Messages.getString("BoppiChecker.12"), type)); //$NON-NLS-1$ }); return SimpleType.VOID; } diff --git a/src/pp/s1184725/boppi/BoppiGenerator.java b/src/pp/s1184725/boppi/BoppiGenerator.java index e24ff91..1ef800c 100644 --- a/src/pp/s1184725/boppi/BoppiGenerator.java +++ b/src/pp/s1184725/boppi/BoppiGenerator.java @@ -22,6 +22,13 @@ import pp.s1184725.boppi.util.RegisterPool; * @author Frank Wibbelink */ public class BoppiGenerator extends BoppiBaseVisitor { + /** + * Whether local variables are always deallocated at the end of a function + * call to provide good memory management or kept if there are multiple + * references to the function for full closure support. + */ + public static final boolean PROPER_CLEANUP_INSTEAD_OF_PROPER_CLOSURES = false; + private static final int ARBASESIZE = 16; private static final Num ZERO = new Num(0); private static final Num OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8), @@ -31,7 +38,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { private Program prog; private Annotations an; - private final Reg tempArp = new Reg("ART"), arp = Machine.ARP_REG; + private final Reg tempArp = new Reg("ART"), arp = Machine.ARP_REG; //$NON-NLS-1$ private Logger logger; private RegisterPool regPool; private int labels = 0; @@ -78,7 +85,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { protected BoppiGenerator(Annotations annotations, Logger logger) { try { - String memlib = new String(Files.readAllBytes(ToolChain.PATH.resolve("memlib.iloc"))); + String memlib = new String(Files.readAllBytes(ToolChain.PATH.resolve("memlib.iloc"))); //$NON-NLS-1$ prog = Assembler.instance().assemble(memlib); } catch (Exception e) { throw new RuntimeException(e); @@ -95,7 +102,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { */ private Op emit(String comment, Label label, OpCode opCode, Operand... args) { Op result = new Op(label, opCode, args); - result.setComment(String.format("%s", comment)); + result.setComment(String.format("%s", comment)); //$NON-NLS-1$ this.prog.addInstr(result); return result; } @@ -117,8 +124,8 @@ public class BoppiGenerator extends BoppiBaseVisitor { * register containing the top-level AR */ private void incARReferences(Reg temp, Reg ar) { - String comment = "AR incRef"; - Label loop = makeLabel("aril"), done = makeLabel("arid"); + String comment = "AR incRef"; //$NON-NLS-1$ + Label loop = makeLabel("aril"), done = makeLabel("arid"); //$NON-NLS-1$ //$NON-NLS-2$ emit(comment, OpCode.i2i, ar, tempArp); emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, loop, done); @@ -126,7 +133,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit(comment, OpCode.push, temp); emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp); emit(comment, OpCode.push, temp); - emit(comment, OpCode.jumpI, new Label("memaddref")); + emit(comment, OpCode.jumpI, new Label("memaddref")); //$NON-NLS-1$ emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); @@ -143,8 +150,8 @@ public class BoppiGenerator extends BoppiBaseVisitor { * register containing the top-level AR */ private void decARReferences(Reg temp, Reg ar) { - String comment = "AR decRef"; - Label loop = makeLabel("ardl"), done = makeLabel("ardd"); + String comment = "AR decRef"; //$NON-NLS-1$ + Label loop = makeLabel("ardl"), done = makeLabel("ardd"); //$NON-NLS-1$ //$NON-NLS-2$ emit(comment, OpCode.i2i, ar, tempArp); emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, loop, done); @@ -152,7 +159,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit(comment, OpCode.push, temp); emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp); emit(comment, OpCode.push, temp); - emit(comment, OpCode.jumpI, new Label("memfree")); + emit(comment, OpCode.jumpI, new Label("memfree")); //$NON-NLS-1$ emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); @@ -170,12 +177,12 @@ public class BoppiGenerator extends BoppiBaseVisitor { * the size of the memory chunk */ private void malloc(Reg temp, int size) { - emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp); - emit("malloc", OpCode.push, temp); - emit("malloc", OpCode.loadI, new Num(size), temp); - emit("malloc", OpCode.push, temp); - emit("malloc", OpCode.jumpI, new Label("memalloc")); - emit("malloc", OpCode.pop, temp); + emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp); //$NON-NLS-1$ + emit("malloc", OpCode.push, temp); //$NON-NLS-1$ + emit("malloc", OpCode.loadI, new Num(size), temp); //$NON-NLS-1$ + emit("malloc", OpCode.push, temp); //$NON-NLS-1$ + emit("malloc", OpCode.jumpI, new Label("memalloc")); //$NON-NLS-1$ //$NON-NLS-2$ + emit("malloc", OpCode.pop, temp); //$NON-NLS-1$ } /** @@ -188,11 +195,11 @@ public class BoppiGenerator extends BoppiBaseVisitor { * the register containing the size of the memory chunk */ private void malloc(Reg temp, Reg size) { - emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp); - emit("malloc", OpCode.push, temp); - emit("malloc", OpCode.push, size); - emit("malloc", OpCode.jumpI, new Label("memalloc")); - emit("malloc", OpCode.pop, temp); + emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$ + emit("malloc", OpCode.push, temp); //$NON-NLS-1$ + emit("malloc", OpCode.push, size); //$NON-NLS-1$ + emit("malloc", OpCode.jumpI, new Label("memalloc")); //$NON-NLS-1$ //$NON-NLS-2$ + emit("malloc", OpCode.pop, temp); //$NON-NLS-1$ } /** @@ -204,10 +211,10 @@ public class BoppiGenerator extends BoppiBaseVisitor { * the register containing the memory pointer */ private void free(Reg temp, Reg ptr) { - emit("free", OpCode.loadI, new Num(prog.size() + 4), temp); - emit("free", OpCode.push, temp); - emit("free", OpCode.push, ptr); - emit("free", OpCode.jumpI, new Label("memfree")); + emit("free", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$ + emit("free", OpCode.push, temp); //$NON-NLS-1$ + emit("free", OpCode.push, ptr); //$NON-NLS-1$ + emit("free", OpCode.jumpI, new Label("memfree")); //$NON-NLS-1$ //$NON-NLS-2$ } /** @@ -219,10 +226,10 @@ public class BoppiGenerator extends BoppiBaseVisitor { * the register containing the memory pointer */ private void incRef(Reg temp, Reg ptr) { - emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp); - emit("memaddref", OpCode.push, temp); - emit("memaddref", OpCode.push, ptr); - emit("memaddref", OpCode.jumpI, new Label("memaddref")); + emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$ + emit("memaddref", OpCode.push, temp); //$NON-NLS-1$ + emit("memaddref", OpCode.push, ptr); //$NON-NLS-1$ + emit("memaddref", OpCode.jumpI, new Label("memaddref")); //$NON-NLS-1$ //$NON-NLS-2$ } /** @@ -238,7 +245,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { private String getError(ParserRuleContext node, String message, Object... args) { int line = node.getStart().getLine(); int column = node.getStart().getCharPositionInLine(); - return String.format("Line %d:%d - %s", line, column, String.format(message, args)); + return String.format(Messages.getString("BoppiGenerator.1"), line, column, String.format(message, args)); //$NON-NLS-1$ } /** @@ -265,10 +272,10 @@ public class BoppiGenerator extends BoppiBaseVisitor { */ private void incrementReference(Type type, Reg addr) { if (!(type instanceof ReferenceType)) - logger.severe(String.format("INTERNAL: trying to increment reference for '%s'", type)); + logger.severe(String.format(Messages.getString("BoppiGenerator.0"), type)); //$NON-NLS-1$ regPool.withReg((temp, ar) -> { - String comment = "add new reference"; + String comment = "add new reference"; //$NON-NLS-1$ incRef(temp, addr); @@ -292,11 +299,11 @@ public class BoppiGenerator extends BoppiBaseVisitor { */ private void decrementReference(Type type, Reg addr) { if (!(type instanceof ReferenceType)) - logger.severe(String.format("INTERNAL: trying to increment reference for '%s'", type)); + logger.severe(String.format(Messages.getString("BoppiGenerator.2"), type)); //$NON-NLS-1$ regPool.withReg((temp) -> { - Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); - String comment = "remove old reference"; + Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); //$NON-NLS-1$ //$NON-NLS-2$ + String comment = "remove old reference"; //$NON-NLS-1$ emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, isNull, notNull); @@ -328,9 +335,9 @@ public class BoppiGenerator extends BoppiBaseVisitor { Type type = var.getType(); if (type instanceof ReferenceType) { - Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); - String comment = "remove reference"; - emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr); + Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); //$NON-NLS-1$ //$NON-NLS-2$ + String comment = "remove reference"; //$NON-NLS-1$ + emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr); //$NON-NLS-1$ emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, ft); emit(comment, OpCode.cbr, ft, isNull, notNull); @@ -388,16 +395,18 @@ public class BoppiGenerator extends BoppiBaseVisitor { } catch (Exception e) { if (tree instanceof ParserRuleContext) - logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage())); + logger.severe(getError((ParserRuleContext) tree, Messages.getString("BoppiGenerator.3"), //$NON-NLS-1$ + e.getClass().getName(), e.getMessage())); else - logger.severe(String.format("Error %s: %s", e.getClass().getName(), e.getMessage())); + logger.severe( + String.format(Messages.getString("BoppiGenerator.4"), e.getClass().getName(), e.getMessage())); //$NON-NLS-1$ return null; } } @Override public Reg visitAssign(AssignContext ctx) { - Reg result = visit(ctx.singleExpr()); + Reg result = visit(ctx.assignStat() != null ? ctx.assignStat() : ctx.expr()); regPool.blockReg(result, () -> { Type type = an.types.get(ctx); @@ -406,7 +415,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { if (type instanceof ReferenceType) { regPool.blockReg(addr, () -> { regPool.withReg((temp) -> { - emit("load reference", OpCode.load, addr, temp); + emit("load reference", OpCode.load, addr, temp); //$NON-NLS-1$ decrementReference(an.types.get(ctx), temp); }); }); @@ -414,12 +423,12 @@ public class BoppiGenerator extends BoppiBaseVisitor { OpCode op = (SimpleType.CHAR.equals(type)) ? OpCode.cstore : OpCode.store; - emit("to " + ctx.variable().getText(), op, result, addr); + emit("to " + ctx.variable().getText(), op, result, addr); //$NON-NLS-1$ if (type instanceof ReferenceType) { regPool.blockReg(addr, () -> { regPool.withReg((temp) -> { - emit("load reference", OpCode.load, addr, temp); + emit("load reference", OpCode.load, addr, temp); //$NON-NLS-1$ incrementReference(an.types.get(ctx), temp); }); }); @@ -429,29 +438,45 @@ public class BoppiGenerator extends BoppiBaseVisitor { return result; } + /** + * Call function: + *
    + *
  1. generate load function reference
  2. + *
  3. generate allocate AR
  4. + *
  5. visit parameters and generate store in AR
  6. + *
  7. generate register save
  8. + *
  9. generate store AL, AR, return address
  10. + *
  11. generate increment AR references
  12. + *
  13. generate set AR
  14. + *
  15. generate call function
  16. + *
  17. generate decrement AR references
  18. + *
  19. generate register restore
  20. + *
  21. generate restore AR
  22. + *
+ */ @Override public Reg visitCall(CallContext ctx) { - String base = "call " + ctx.variable().getText() + " - "; + String base = "call " + ctx.variable().getText() + " - "; //$NON-NLS-1$ //$NON-NLS-2$ Variable function = an.variables.get(ctx.variable()); TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter(); Reg ar = regPool.withReg((tempAR) -> { regPool.withReg((rt) -> { Reg addr = visit(ctx.variable()); - emit(base + "load function reference", OpCode.load, addr, rt); - emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, rt); + emit(base + "load function reference", OpCode.load, addr, rt); //$NON-NLS-1$ + emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, rt); //$NON-NLS-1$ malloc(tempAR, rt); }); - emit(base + "shift AR", OpCode.addI, tempAR, new Num(ARBASESIZE), tempAR); + emit(base + "shift AR", OpCode.addI, tempAR, new Num(ARBASESIZE), tempAR); //$NON-NLS-1$ for (int i = 0; i < ctx.expr().size(); i++) { Reg exprReg = visit(ctx.expr(i)); if (SimpleType.CHAR.equals(parameters.get(i))) - emit(base + "store param " + i, OpCode.cstoreAI, exprReg, tempAR, new Num(parameters.getOffset(i))); + emit(base + "store param " + i, OpCode.cstoreAI, exprReg, tempAR, new Num(parameters.getOffset(i))); //$NON-NLS-1$ else - emit(base + "store param " + i, OpCode.storeAI, exprReg, tempAR, new Num(parameters.getOffset(i))); + emit(base + "store param " + i, OpCode.storeAI, exprReg, tempAR, new Num(parameters.getOffset(i))); //$NON-NLS-1$ } ; }); @@ -459,26 +484,26 @@ public class BoppiGenerator extends BoppiBaseVisitor { Stack inUse = new Stack<>(); for (Reg reg : regPool.getInUse()) { inUse.push(reg); - emit(base + "register save " + reg.getName(), OpCode.push, reg); + emit(base + "register save " + reg.getName(), OpCode.push, reg); //$NON-NLS-1$ } regPool.blockReg(ar, () -> { regPool.withReg((r1, r2) -> { Reg addr = visit(ctx.variable()); - emit(base + "load function reference", OpCode.load, addr, r2); - emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP); - emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); - emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL); + emit(base + "load function reference", OpCode.load, addr, r2); //$NON-NLS-1$ + emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP); //$NON-NLS-1$ + emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); //$NON-NLS-1$ + emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL); //$NON-NLS-1$ - emit("add ref for callee's AL", OpCode.loadAI, ar, OFFSET_AL, tempArp); + emit("add ref for callee's AL", OpCode.loadAI, ar, OFFSET_AL, tempArp); //$NON-NLS-1$ incARReferences(r1, tempArp); - emit(base + "load return address", OpCode.loadI, new Num(prog.size() + 5), r1); - emit(base + "set return address", OpCode.storeAI, r1, ar, OFFSET_RETURN_ADDR); - emit(base + "move ARP", OpCode.i2i, ar, arp); + emit(base + "load return address", OpCode.loadI, new Num(prog.size() + 5), r1); //$NON-NLS-1$ + emit(base + "set return address", OpCode.storeAI, r1, ar, OFFSET_RETURN_ADDR); //$NON-NLS-1$ + emit(base + "move ARP", OpCode.i2i, ar, arp); //$NON-NLS-1$ - emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); - emit(base + "execute", OpCode.jump, r1); + emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); //$NON-NLS-1$ + emit(base + "execute", OpCode.jump, r1); //$NON-NLS-1$ }); }); @@ -488,12 +513,12 @@ public class BoppiGenerator extends BoppiBaseVisitor { while (!inUse.isEmpty()) { Reg reg = inUse.pop(); - emit(base + "register unsave " + reg.getName(), OpCode.pop, reg); + emit(base + "register unsave " + reg.getName(), OpCode.pop, reg); //$NON-NLS-1$ } return regPool.withReg((res) -> { - emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res); - emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp); + emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res); //$NON-NLS-1$ + emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp); //$NON-NLS-1$ }); } @@ -505,13 +530,13 @@ public class BoppiGenerator extends BoppiBaseVisitor { if (t instanceof ReferenceType) { regPool.withReg((addr) -> { - emit("load var address", OpCode.addI, arp, new Num(var.getOffset()), addr); + emit("load var address", OpCode.addI, arp, new Num(var.getOffset()), addr); //$NON-NLS-1$ if (t instanceof ArrayType) { ArrayType type = (ArrayType) t; regPool.withReg((temp) -> { malloc(temp, type.getReferenceSize()); - emit("link array", OpCode.store, temp, addr); + emit("link array", OpCode.store, temp, addr); //$NON-NLS-1$ }); } }); @@ -527,6 +552,8 @@ public class BoppiGenerator extends BoppiBaseVisitor { *
  • generate entry point
  • *
  • visit function body
  • *
  • generate return
  • + *
  • generate check whether no more AR references
  • + *
  • if so, generate dereference local variables
  • *
  • generate allocate function reference
  • *
  • generate store entry point and AR to function reference
  • *
  • generate store function reference in variable
  • @@ -535,67 +562,64 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitDeclareFunction(DeclareFunctionContext ctx) { ReferenceType type = (ReferenceType) an.variables.get(ctx).getType(); - String base = "define " + ctx.IDENTIFIER(0).getText() + " - "; + String base = "define " + ctx.IDENTIFIER().getText() + " - "; //$NON-NLS-1$ //$NON-NLS-2$ - Label skip = makeLabel("s"); - emit(base + "jump over body", OpCode.jumpI, skip); - int callAddress = emit(base + "entry point", OpCode.nop).getLine(); + Label skip = makeLabel("s"); //$NON-NLS-1$ + emit(base + "jump over body", OpCode.jumpI, skip); //$NON-NLS-1$ + int callAddress = emit(base + "entry point", OpCode.nop).getLine(); //$NON-NLS-1$ Reg result = visit(ctx.body); - emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL); - dereferenceLocalVariables(an.function.get(ctx)); + if (((FunctionType) an.variables.get(ctx).getType()).getReturn() != SimpleType.VOID) + emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL); //$NON-NLS-1$ + + if (PROPER_CLEANUP_INSTEAD_OF_PROPER_CLOSURES) + dereferenceLocalVariables(an.function.get(ctx)); + else { + Label cleanup = makeLabel("ycl"), noCleanup = makeLabel("ncl"); //$NON-NLS-1$ //$NON-NLS-2$ + regPool.withReg((ref, one) -> { + emit("load ref count", OpCode.loadAI, arp, OFFSET_REF_COUNT, ref); //$NON-NLS-1$ + emit("one", OpCode.loadI, new Num(1), one); //$NON-NLS-1$ + emit("check more than one ref", OpCode.cmp_LE, ref, one, ref); //$NON-NLS-1$ + emit("remove vars if last reference", OpCode.cbr, ref, cleanup, noCleanup); //$NON-NLS-1$ + }); + emit("cleanup target", cleanup, OpCode.nop); //$NON-NLS-1$ + + dereferenceLocalVariables(an.function.get(ctx)); + + emit("no cleanup target", noCleanup, OpCode.nop); //$NON-NLS-1$ + } regPool.withReg((r1) -> { - emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1); - emit(base + "go to return address", OpCode.jump, r1); + emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1); //$NON-NLS-1$ + emit(base + "go to return address", OpCode.jump, r1); //$NON-NLS-1$ }); - emit(base + "skip target", skip, OpCode.nop); + emit(base + "skip target", skip, OpCode.nop); //$NON-NLS-1$ regPool.withReg((r1, r2) -> { malloc(r2, type.getReferenceSize()); - emit(base + "load target address", OpCode.loadI, new Num(callAddress), r1); - emit(base + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR); - emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP); - emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()), + emit(base + "load target address", OpCode.loadI, new Num(callAddress), r1); //$NON-NLS-1$ + emit(base + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR); //$NON-NLS-1$ + emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP); //$NON-NLS-1$ + emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()), //$NON-NLS-1$ r1); - emit(base + "set AR size", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ARSIZE); + emit(base + "set AR size", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ARSIZE); //$NON-NLS-1$ - emit(base + "set function reference", OpCode.storeAI, r2, arp, new Num(an.variables.get(ctx).getOffset())); + emit(base + "set function reference", OpCode.storeAI, r2, arp, new Num(an.variables.get(ctx).getOffset())); //$NON-NLS-1$ incARReferences(r2, arp); }); return null; } - @Override - public Reg visitExpr(ExprContext ctx) { - - for (int i = 0; i < ctx.singleExpr().size() - 1; i++) { - ParserRuleContext expr = ctx.singleExpr(i); - Reg result = visit(expr); - - if (result != null) - regPool.blockReg(result, () -> { - Type type = an.types.get(expr); - - if (type instanceof ReferenceType) - decrementReference(type, result); - }); - } - ParserRuleContext last = ctx.singleExpr(ctx.singleExpr().size() - 1); - - return visit(last); - } - @Override public Reg visitGetVariable(GetVariableContext ctx) { Type type = an.types.get(ctx); Reg result = visit(ctx.variable()); OpCode op = SimpleType.CHAR.equals(an.types.get(ctx)) ? OpCode.cload : OpCode.load; - emit("load address", op, result, result); + emit("load address", op, result, result); //$NON-NLS-1$ if (type instanceof ReferenceType) regPool.blockReg(result, () -> { @@ -607,44 +631,48 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitIf(IfContext ctx) { - Label toTrue = makeLabel("if_t"); - Label toFalse = makeLabel("if_f"); - Label toEnd = makeLabel("if_e"); - Reg cond = visit(ctx.cond); + Label toTrue = makeLabel("if_t"); //$NON-NLS-1$ + Label toFalse = makeLabel("if_f"); //$NON-NLS-1$ + Label toEnd = makeLabel("if_e"); //$NON-NLS-1$ + try { + Reg cond = visit(ctx.cond); - if (ctx.onFalse == null) { - emit("", OpCode.cbr, cond, toTrue, toEnd); + if (ctx.onFalse == null) { + emit("", OpCode.cbr, cond, toTrue, toEnd); //$NON-NLS-1$ - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); + emit("", toTrue, OpCode.nop); //$NON-NLS-1$ + visit(ctx.onTrue); - emit("end target", toEnd, OpCode.nop); - return null; - } else if (an.types.get(ctx) == SimpleType.VOID) { - emit("", OpCode.cbr, cond, toTrue, toFalse); + emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$ + return null; + } else if (an.types.get(ctx) == SimpleType.VOID) { + emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$ - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - emit("", OpCode.jumpI, toEnd); + emit("", toTrue, OpCode.nop); //$NON-NLS-1$ + visit(ctx.onTrue); + emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$ - emit("", toFalse, OpCode.nop); - visit(ctx.onFalse); + emit("", toFalse, OpCode.nop); //$NON-NLS-1$ + visit(ctx.onFalse); - emit("end target", toEnd, OpCode.nop); - return null; - } else { - return regPool.withReg((target) -> { - emit("", OpCode.cbr, cond, toTrue, toFalse); + emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$ + return null; + } else { + return regPool.withReg((target) -> { + emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$ - emit("", toTrue, OpCode.nop); - emit("result", OpCode.i2i, visit(ctx.onTrue), target); - emit("", OpCode.jumpI, toEnd); + emit("", toTrue, OpCode.nop); //$NON-NLS-1$ + emit("result", OpCode.i2i, visit(ctx.onTrue), target); //$NON-NLS-1$ + emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$ - emit("", toFalse, OpCode.nop); - emit("result", OpCode.i2i, visit(ctx.onFalse), target); + emit("", toFalse, OpCode.nop); //$NON-NLS-1$ + emit("result", OpCode.i2i, visit(ctx.onFalse), target); //$NON-NLS-1$ - emit("end target", toEnd, OpCode.nop); - }); + emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$ + }); + } + } catch (Throwable e) { + throw e; } } @@ -680,23 +708,23 @@ public class BoppiGenerator extends BoppiBaseVisitor { return regPool.withReg((i, temp1) -> { regPool.withReg((temp2) -> { - Label cond = makeLabel("aeqc"), loop = makeLabel("aeql"), end = makeLabel("aeqe"); + Label cond = makeLabel("aeqc"), loop = makeLabel("aeql"), end = makeLabel("aeqe"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - emit("iterate from 0", OpCode.loadI, ZERO, i); - emit("equality true", OpCode.loadI, new Num(1), temp1); + emit("iterate from 0", OpCode.loadI, ZERO, i); //$NON-NLS-1$ + emit("equality true", OpCode.loadI, new Num(1), temp1); //$NON-NLS-1$ - emit("load arr size", cond, OpCode.loadAI, lhs, OFFSET_REF_SIZE, temp2); - emit("check i in bounds", OpCode.cmp_LT, i, temp2, temp2); - emit("loop", OpCode.cbr, temp2, loop, end); + emit("load arr size", cond, OpCode.loadAI, lhs, OFFSET_REF_SIZE, temp2); //$NON-NLS-1$ + emit("check i in bounds", OpCode.cmp_LT, i, temp2, temp2); //$NON-NLS-1$ + emit("loop", OpCode.cbr, temp2, loop, end); //$NON-NLS-1$ - emit("load left", loop, OpCode.loadAO, lhs, i, temp1); - emit("load right", OpCode.loadAO, rhs, i, temp2); - emit("increment i", OpCode.addI, i, elementSize, i); + emit("load left", loop, OpCode.loadAO, lhs, i, temp1); //$NON-NLS-1$ + emit("load right", OpCode.loadAO, rhs, i, temp2); //$NON-NLS-1$ + emit("increment i", OpCode.addI, i, elementSize, i); //$NON-NLS-1$ - emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1); - emit("loop or break", OpCode.cbr, temp1, cond, end); + emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1); //$NON-NLS-1$ + emit("loop or break", OpCode.cbr, temp1, cond, end); //$NON-NLS-1$ - emit("end equality check", end, OpCode.nop); + emit("end equality check", end, OpCode.nop); //$NON-NLS-1$ }); }); }); @@ -723,6 +751,13 @@ public class BoppiGenerator extends BoppiBaseVisitor { return lhs; } + @Override + public Reg visitLambda(LambdaContext ctx) { + emit(ctx.getText(), OpCode.haltI, new Num(768)); + + return visit(ctx.expr()); + } + @Override public Reg visitLiteralBoolean(LiteralBooleanContext ctx) { return regPool.withReg((reg) -> { @@ -745,7 +780,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { try { val = Integer.parseInt(ctx.LITERAL10().getText()); } catch (NumberFormatException e) { - logger.severe(getError(ctx, "Error parsing number '%s'", ctx.LITERAL10().getText())); + logger.severe(getError(ctx, Messages.getString("BoppiGenerator.5"), ctx.LITERAL10().getText())); //$NON-NLS-1$ } emit(ctx.getText(), OpCode.loadI, new Num(val), reg); @@ -754,15 +789,15 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitPrefix1(Prefix1Context ctx) { - Reg reg = visit(ctx.singleExpr()); + Reg reg = visit(ctx.expr()); switch (ctx.op.getType()) { case BoppiLexer.MINUS: - emit("unary -", OpCode.rsubI, reg, ZERO, reg); + emit("unary -", OpCode.rsubI, reg, ZERO, reg); //$NON-NLS-1$ break; case BoppiLexer.NOT: - emit("not", OpCode.xorI, reg, new Num(1), reg); + emit("not", OpCode.xorI, reg, new Num(1), reg); //$NON-NLS-1$ break; } return reg; @@ -770,15 +805,15 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitProgram(ProgramContext ctx) { - emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO); + emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO); //$NON-NLS-1$ malloc(arp, an.function.get(ctx).getLocalDataSize() + ARBASESIZE); - emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp); + emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp); //$NON-NLS-1$ visitChildren(ctx); dereferenceLocalVariables(an.function.get(ctx)); - emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp); + emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp); //$NON-NLS-1$ regPool.withReg((temp) -> { free(temp, arp); }); @@ -794,37 +829,37 @@ public class BoppiGenerator extends BoppiBaseVisitor { if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { regPool.withReg((temp) -> { - emit("", OpCode.in, new Str(""), temp); + emit("", OpCode.in, new Str(""), temp); //$NON-NLS-1$ //$NON-NLS-2$ Reg addr = visit(expr); - emit("save to var " + expr.getText(), OpCode.store, temp, addr); + emit("save to var " + expr.getText(), OpCode.store, temp, addr); //$NON-NLS-1$ }); } else if (SimpleType.CHAR.equals(an.types.get(expr))) { // Get input until at least 1 character - Label getTarget = makeLabel("lcin_l"); - Label continueTarget = makeLabel("lcin_e"); + Label getTarget = makeLabel("lcin_l"); //$NON-NLS-1$ + Label continueTarget = makeLabel("lcin_e"); //$NON-NLS-1$ regPool.withReg((temp, count) -> { - emit("", getTarget, OpCode.cin, new Str("")); - emit("str length", OpCode.pop, count); - emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); + emit("", getTarget, OpCode.cin, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$ + emit("str length", OpCode.pop, count); //$NON-NLS-1$ + emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); //$NON-NLS-1$ // Get character - emit("pop whole string", continueTarget, OpCode.cpop, temp); + emit("pop whole string", continueTarget, OpCode.cpop, temp); //$NON-NLS-1$ Reg addr = visit(expr); - emit("save to var " + expr.getText(), OpCode.cstore, temp, addr); + emit("save to var " + expr.getText(), OpCode.cstore, temp, addr); //$NON-NLS-1$ // Pop all remaining characters - Label loopTarget = makeLabel("lcpop_l"); - Label iterTarget = makeLabel("lcpop_c"); - Label stopTarget = makeLabel("lcpop_e"); - emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp); - emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); - emit("pop remaining", iterTarget, OpCode.cpop, temp); - emit("pop remaining", OpCode.jumpI, loopTarget); - emit("pop remaining", stopTarget, OpCode.nop); + Label loopTarget = makeLabel("lcpop_l"); //$NON-NLS-1$ + Label iterTarget = makeLabel("lcpop_c"); //$NON-NLS-1$ + Label stopTarget = makeLabel("lcpop_e"); //$NON-NLS-1$ + emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp); //$NON-NLS-1$ + emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); //$NON-NLS-1$ + emit("pop remaining", iterTarget, OpCode.cpop, temp); //$NON-NLS-1$ + emit("pop remaining", OpCode.jumpI, loopTarget); //$NON-NLS-1$ + emit("pop remaining", stopTarget, OpCode.nop); //$NON-NLS-1$ }); } else { - emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); + emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); //$NON-NLS-1$ } } @@ -834,68 +869,88 @@ public class BoppiGenerator extends BoppiBaseVisitor { if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { return regPool.withReg((result) -> { - emit("", OpCode.in, new Str(""), result); + emit("", OpCode.in, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$ Reg addr = visit(expr); - emit("save to var " + expr.getText(), OpCode.store, result, addr); + emit("save to var " + expr.getText(), OpCode.store, result, addr); //$NON-NLS-1$ }); } else if (SimpleType.CHAR.equals(an.types.get(expr))) { return regPool.withReg((count, result) -> { // Get input until at least 1 character - Label getTarget = makeLabel("lcin_l"); - Label continueTarget = makeLabel("lcin_e"); + Label getTarget = makeLabel("lcin_l"); //$NON-NLS-1$ + Label continueTarget = makeLabel("lcin_e"); //$NON-NLS-1$ - emit("", getTarget, OpCode.cin, new Str("")); - emit("str length", OpCode.pop, count); - emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); + emit("", getTarget, OpCode.cin, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$ + emit("str length", OpCode.pop, count); //$NON-NLS-1$ + emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); //$NON-NLS-1$ // Get character - emit("pop whole string", continueTarget, OpCode.cpop, result); + emit("pop whole string", continueTarget, OpCode.cpop, result); //$NON-NLS-1$ Reg addr = visit(expr); - emit("save to var " + expr.getText(), OpCode.cstore, result, addr); + emit("save to var " + expr.getText(), OpCode.cstore, result, addr); //$NON-NLS-1$ // Pop all remaining characters - Label loopTarget = makeLabel("lcpop_l"); - Label iterTarget = makeLabel("lcpop_c"); - Label stopTarget = makeLabel("lcpop_e"); + Label loopTarget = makeLabel("lcpop_l"); //$NON-NLS-1$ + Label iterTarget = makeLabel("lcpop_c"); //$NON-NLS-1$ + Label stopTarget = makeLabel("lcpop_e"); //$NON-NLS-1$ regPool.withReg((temp) -> { - emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count); - emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); - emit("pop remaining", iterTarget, OpCode.cpop, temp); - emit("pop remaining", OpCode.jumpI, loopTarget); - emit("pop remaining", stopTarget, OpCode.nop); + emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count); //$NON-NLS-1$ + emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); //$NON-NLS-1$ + emit("pop remaining", iterTarget, OpCode.cpop, temp); //$NON-NLS-1$ + emit("pop remaining", OpCode.jumpI, loopTarget); //$NON-NLS-1$ + emit("pop remaining", stopTarget, OpCode.nop); //$NON-NLS-1$ }); }); } else { - emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); + emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); //$NON-NLS-1$ return null; } } } + @Override + public Reg visitStats(StatsContext ctx) { + + for (int i = 0; i < ctx.stat().size() - 1; i++) { + ParserRuleContext expr = ctx.stat(i); + Reg result = visit(expr); + + if (result != null) + regPool.blockReg(result, () -> { + Type type = an.types.get(expr); + + if (type instanceof ReferenceType) + decrementReference(type, result); + }); + } + ParserRuleContext last = ctx.stat(ctx.stat().size() - 1); + + return visit(last); + } + @Override public Reg visitVariableArray(VariableArrayContext ctx) { ArrayType type = (ArrayType) an.types.get(ctx.variable()); Reg addr = visit(ctx.variable()); - emit("get array object", OpCode.load, addr, addr); + emit("get array object", OpCode.load, addr, addr); //$NON-NLS-1$ regPool.blockReg(addr, () -> { Reg offset = visit(ctx.expr()); regPool.blockReg(offset, () -> { regPool.withReg((r1, r2) -> { - Label outOfBounds = makeLabel("oob"), inBounds = makeLabel("nob"); + Label outOfBounds = makeLabel("oob"), inBounds = makeLabel("nob"); //$NON-NLS-1$ //$NON-NLS-2$ - emit("check array index", OpCode.loadAI, addr, OFFSET_REF_SIZE, r1); - emit("check array index", OpCode.divI, r1, new Num(type.getType().getSize()), r1); - emit("check array index", OpCode.cmp_LT, offset, r1, r1); - emit("check array index", OpCode.cmp_GE, offset, RegisterPool.ZERO, r2); - emit("check array index", OpCode.and, r1, r2, r2); - emit("check array index", OpCode.cbr, r2, inBounds, outOfBounds); - emit("array index out of bounds", outOfBounds, OpCode.haltI, new Num(0x616f6f62)); + emit("check array index", OpCode.loadAI, addr, OFFSET_REF_SIZE, r1); //$NON-NLS-1$ + emit("check array index", OpCode.divI, r1, new Num(type.getType().getSize()), r1); //$NON-NLS-1$ + emit("check array index", OpCode.cmp_LT, offset, r1, r1); //$NON-NLS-1$ + emit("check array index", OpCode.cmp_GE, offset, RegisterPool.ZERO, r2); //$NON-NLS-1$ + emit("check array index", OpCode.and, r1, r2, r2); //$NON-NLS-1$ + emit("check array index", OpCode.cbr, r2, inBounds, outOfBounds); //$NON-NLS-1$ + emit("array index out of bounds", outOfBounds, OpCode.haltI, new Num(0x616f6f62)); //$NON-NLS-1$ - emit("multiply index by size", inBounds, OpCode.multI, offset, new Num(type.getType().getSize()), + emit("multiply index by size", inBounds, OpCode.multI, offset, new Num(type.getType().getSize()), //$NON-NLS-1$ offset); - emit("get array index address", OpCode.add, addr, offset, addr); + emit("get array index address", OpCode.add, addr, offset, addr); //$NON-NLS-1$ }); }); }); @@ -909,7 +964,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { Reg addr = visit(ctx.variable()); if (innerType instanceof ArrayType) { - emit("get size", OpCode.loadAI, addr, OFFSET_REF_SIZE, addr); + emit("get size", OpCode.loadAI, addr, OFFSET_REF_SIZE, addr); //$NON-NLS-1$ return addr; } else { return addr; @@ -925,30 +980,30 @@ public class BoppiGenerator extends BoppiBaseVisitor { int functionDepth = an.function.get(ctx).getFunctionDepth(); if (var.getDepth() < functionDepth) { - emit("travelling ALs", OpCode.i2i, ar, tempArp); + emit("travelling ALs", OpCode.i2i, ar, tempArp); //$NON-NLS-1$ for (int i = var.getDepth(); i < functionDepth; i++) - emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp); + emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp); //$NON-NLS-1$ ar = tempArp; } - emit("add offset", OpCode.addI, ar, new Num(var.getOffset()), result); + emit("add offset", OpCode.addI, ar, new Num(var.getOffset()), result); //$NON-NLS-1$ }); } @Override public Reg visitWhile(WhileContext ctx) { - Label toLoop = makeLabel("while_t"); - Label toCond = makeLabel("while_f"); - Label toEnd = makeLabel("while_e"); + Label toLoop = makeLabel("while_t"); //$NON-NLS-1$ + Label toCond = makeLabel("while_f"); //$NON-NLS-1$ + Label toEnd = makeLabel("while_e"); //$NON-NLS-1$ - emit("to condition", OpCode.jumpI, toCond); - emit("loop target", toLoop, OpCode.nop); + emit("to condition", OpCode.jumpI, toCond); //$NON-NLS-1$ + emit("loop target", toLoop, OpCode.nop); //$NON-NLS-1$ visit(ctx.onTrue); - emit("condition target", toCond, OpCode.nop); - emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd); - emit("end target", toEnd, OpCode.nop); + emit("condition target", toCond, OpCode.nop); //$NON-NLS-1$ + emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd); //$NON-NLS-1$ + emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$ return null; } @@ -963,16 +1018,16 @@ public class BoppiGenerator extends BoppiBaseVisitor { Type type = an.types.get(expr); if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) { - emit("", OpCode.out, new Str(""), result); + emit("", OpCode.out, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$ } else if (SimpleType.CHAR.equals(type)) { regPool.withReg((temp) -> { - emit("push character", OpCode.cpush, result); - emit("load 1", OpCode.loadI, new Num(1), temp); - emit("push 1", OpCode.push, temp); - emit("print character", OpCode.cout, new Str("")); + emit("push character", OpCode.cpush, result); //$NON-NLS-1$ + emit("load 1", OpCode.loadI, new Num(1), temp); //$NON-NLS-1$ + emit("push 1", OpCode.push, temp); //$NON-NLS-1$ + emit("print character", OpCode.cout, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$ }); } else { - emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); + emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); //$NON-NLS-1$ } if (!expr.equals(ctx.expr(ctx.expr().size() - 1))) @@ -989,18 +1044,18 @@ public class BoppiGenerator extends BoppiBaseVisitor { Type type = an.types.get(expr); if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) { - emit("", OpCode.out, new Str(""), result); + emit("", OpCode.out, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$ return result; } else if (SimpleType.CHAR.equals(type)) { regPool.withReg((temp) -> { - emit("push character", OpCode.cpush, result); - emit("load 1", OpCode.loadI, new Num(1), temp); - emit("push 1", OpCode.push, temp); - emit("print character", OpCode.cout, new Str("")); + emit("push character", OpCode.cpush, result); //$NON-NLS-1$ + emit("load 1", OpCode.loadI, new Num(1), temp); //$NON-NLS-1$ + emit("push 1", OpCode.push, temp); //$NON-NLS-1$ + emit("print character", OpCode.cout, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$ }); return result; } else { - emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); + emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); //$NON-NLS-1$ return null; } }); diff --git a/src/pp/s1184725/boppi/CachingSymbolTable.java b/src/pp/s1184725/boppi/CachingSymbolTable.java index 40f1eec..15e830f 100644 --- a/src/pp/s1184725/boppi/CachingSymbolTable.java +++ b/src/pp/s1184725/boppi/CachingSymbolTable.java @@ -33,19 +33,18 @@ public class CachingSymbolTable { } /** - * Opens a lexical scope. + * Closes a lexical scope for a function declaration, removing all + * declarations within this scope, restoring the local offset and decreasing + * the lookup depth. + * + * @return function scope details + * + * @throws NoProgramException + * if no scope is open */ - public void openScope() { - symbolMapStack.push(new HashMap<>()); - } - - /** - * Opens a lexical scope for a function declaration, resetting the local - * offset and increasing the lookup depth. - */ - public void openFunctionScope() { - openScope(); - functionScope.push(new FunctionScope(functionScope.size())); + public FunctionScope closeFunctionScope() throws NoProgramException { + closeScope(); + return functionScope.pop(); } /** @@ -69,40 +68,97 @@ public class CachingSymbolTable { } /** - * Closes a lexical scope for a function declaration, removing all - * declarations within this scope, restoring the local offset and decreasing - * the lookup depth. - * - * @return function scope details + * Returns the type of an identifier, if any. * + * @param id + * the name of the identifier + * @return the type of the identifier + * @throws UndeclaredException + * if the identifier is not declared * @throws NoProgramException * if no scope is open */ - public FunctionScope closeFunctionScope() throws NoProgramException { - closeScope(); - return functionScope.pop(); + public Variable get(String id) throws UndeclaredException, NoProgramException { + if (!this.has(id)) + throw new UndeclaredException(String.format(Messages.getString("SymbolTable.1"), id)); //$NON-NLS-1$ + + return symbolCache.get(id); } /** - * Opens a scope, executes the given code and closes the scope. This is a - * helper method to make sure calls to {@link #openScope()} and - * {@link #closeScope()} are balanced. {@code code} must take no arguments - * and must return a value. + * Returns the current function scope, if any. * - * @param - * the type of the code's return value - * @param code - * the code to execute within the scope - * @return the return value of the code + * @return the current function scope + * @throws NoProgramException + * if no scope is open */ - public U withScope(Supplier code) { + public FunctionScope getFunctionScope() throws NoProgramException { + if (symbolMapStack.isEmpty()) + throw new NoProgramException(); + + return functionScope.peek(); + } + + /** + * Returns whether the given identifier is declared. + * + * @param id + * the name of the identifier + * @return true if the identifier has a declared type + * @throws NoProgramException + * if no scope is open + */ + public boolean has(String id) throws NoProgramException { + if (symbolMapStack.isEmpty()) + throw new NoProgramException(); + + return symbolCache.containsKey(id); + } + + /** + * Opens a lexical scope for a function declaration, resetting the local + * offset and increasing the lookup depth. + */ + public void openFunctionScope() { openScope(); - U result = code.get(); - try { - closeScope(); - } catch (NoProgramException e) { - } - return result; + functionScope.push(new FunctionScope(functionScope.size())); + } + + /** + * Opens a lexical scope. + */ + public void openScope() { + symbolMapStack.push(new HashMap<>()); + } + + /** + * Associates an identifier with a certain type in the current lexical scope + * and returns the newly made variable instance that belongs to this + * identifier. Throws an exception if the identifier is declared in this + * scope already. + * + * @param id + * the name of the identifier + * @param type + * the type of the identifier + * @return the type of the identifier + * @throws DeclaredException + * if the identifier is declared in the current scope already + * @throws NoProgramException + * if no scope is open + */ + public Variable put(String id, T type) throws DeclaredException, NoProgramException { + if (symbolMapStack.isEmpty()) + throw new NoProgramException(); + + if (symbolMapStack.peek().containsKey(id)) + throw new DeclaredException(String.format(Messages.getString("SymbolTable.0"), id)); //$NON-NLS-1$ + + Variable var = functionScope.peek().addVariable(type); + + symbolMapStack.peek().put(id, var); + symbolCache.put(id, var); + return var; } /** @@ -127,81 +183,25 @@ public class CachingSymbolTable { } /** - * Associates an identifier with a certain type in the current lexical scope - * and returns the newly made variable instance that belongs to this - * identifier. Throws an exception if the identifier is declared in this - * scope already. + * Opens a scope, executes the given code and closes the scope. This is a + * helper method to make sure calls to {@link #openScope()} and + * {@link #closeScope()} are balanced. {@code code} must take no arguments + * and must return a value. * - * @param id - * the name of the identifier - * @param type - * the type of the identifier - * @return the type of the identifier - * @throws DeclaredException - * if the identifier is declared in the current scope already - * @throws NoProgramException - * if no scope is open + * @param + * the type of the code's return value + * @param code + * the code to execute within the scope + * @return the return value of the code */ - public Variable put(String id, T type) throws DeclaredException, NoProgramException { - if (symbolMapStack.isEmpty()) - throw new NoProgramException(); - - if (symbolMapStack.peek().containsKey(id)) - throw new DeclaredException(String.format("Identifier '%s' already declared in this scope.", id)); - - Variable var = functionScope.peek().addVariable(type); - - symbolMapStack.peek().put(id, var); - symbolCache.put(id, var); - return var; - } - - /** - * Returns whether the given identifier is declared. - * - * @param id - * the name of the identifier - * @return true if the identifier has a declared type - * @throws NoProgramException - * if no scope is open - */ - public boolean has(String id) throws NoProgramException { - if (symbolMapStack.isEmpty()) - throw new NoProgramException(); - - return symbolCache.containsKey(id); - } - - /** - * Returns the type of an identifier, if any. - * - * @param id - * the name of the identifier - * @return the type of the identifier - * @throws UndeclaredException - * if the identifier is not declared - * @throws NoProgramException - * if no scope is open - */ - public Variable get(String id) throws UndeclaredException, NoProgramException { - if (!this.has(id)) - throw new UndeclaredException(String.format("Identifier '%s' is undeclared.", id)); - - return symbolCache.get(id); - } - - /** - * Returns the current function scope, if any. - * - * @return the current function scope - * @throws NoProgramException - * if no scope is open - */ - public FunctionScope getFunctionScope() throws NoProgramException { - if (symbolMapStack.isEmpty()) - throw new NoProgramException(); - - return functionScope.peek(); + public U withScope(Supplier code) { + openScope(); + U result = code.get(); + try { + closeScope(); + } catch (NoProgramException e) { + } + return result; } } diff --git a/src/pp/s1184725/boppi/DebugCachingSymbolTable.java b/src/pp/s1184725/boppi/DebugCachingSymbolTable.java index 54dc21c..fc5cbad 100644 --- a/src/pp/s1184725/boppi/DebugCachingSymbolTable.java +++ b/src/pp/s1184725/boppi/DebugCachingSymbolTable.java @@ -14,25 +14,12 @@ import pp.s1184725.boppi.type.*; */ public class DebugCachingSymbolTable extends CachingSymbolTable { - @Override - public void openFunctionScope() { - super.openFunctionScope(); - System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size()); - } - @Override public FunctionScope closeFunctionScope() throws NoProgramException { System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size()); return super.closeFunctionScope(); } - @Override - public Variable put(String id, T type) throws DeclaredException, NoProgramException { - System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope " - + functionScope.size()); - return super.put(id, type); - } - @Override public Variable get(String id) throws UndeclaredException, NoProgramException { System.out.println(this.getClass().getName() + ": retrieving '" + id + "' (depth " @@ -40,4 +27,17 @@ public class DebugCachingSymbolTable extends CachingSymbolTable< return super.get(id); } + @Override + public void openFunctionScope() { + super.openFunctionScope(); + System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size()); + } + + @Override + public Variable put(String id, T type) throws DeclaredException, NoProgramException { + System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope " + + functionScope.size()); + return super.put(id, type); + } + } diff --git a/src/pp/s1184725/boppi/FunctionScope.java b/src/pp/s1184725/boppi/FunctionScope.java index 6633b6e..cdaa90e 100644 --- a/src/pp/s1184725/boppi/FunctionScope.java +++ b/src/pp/s1184725/boppi/FunctionScope.java @@ -42,6 +42,15 @@ public class FunctionScope { return var; } + /** + * Returns the current lexical scope/function depth. + * + * @return the function depth + */ + public int getFunctionDepth() { + return scopeDepth; + } + /** * Returns the size, in bytes, required to store all the variables declared * in the current function scope. Note that this is the size calculated up @@ -55,15 +64,6 @@ public class FunctionScope { return localVars.stream().map(Variable::getType).collect(Collectors.summingInt(Type::getSize)); } - /** - * Returns the current lexical scope/function depth. - * - * @return the function depth - */ - public int getFunctionDepth() { - return scopeDepth; - } - /** * Returns the variables local to this function scope. * diff --git a/src/pp/s1184725/boppi/Messages.java b/src/pp/s1184725/boppi/Messages.java new file mode 100644 index 0000000..1936fa5 --- /dev/null +++ b/src/pp/s1184725/boppi/Messages.java @@ -0,0 +1,31 @@ +package pp.s1184725.boppi; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Class for externalized messages. Generated by Eclipse. + */ +public class Messages { + private static final String BUNDLE_NAME = "pp.s1184725.boppi.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() { + } + + /** + * Returns a messages for the given key. + * + * @param key + * the key to lookup + * @return the appropriate message or notification of a missing message + */ + public static String getString(String key) { + try { + return RESOURCE_BUNDLE.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; + } + } +} diff --git a/src/pp/s1184725/boppi/ToolChain.java b/src/pp/s1184725/boppi/ToolChain.java index 2d287c1..8c73aeb 100644 --- a/src/pp/s1184725/boppi/ToolChain.java +++ b/src/pp/s1184725/boppi/ToolChain.java @@ -30,142 +30,6 @@ public class ToolChain { */ public static Machine machine; - /** - * Opens a file for reading and returns its charstream. Throws an unhandled - * exception if the file could not be read. - * - * @param file - * the file to read - * @return a {@link CharStream} to be used in the lexer phase - */ - public static CharStream getCharStream(Path file) { - try { - return CharStreams.fromFileName(file.toString()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Reads a string as a charstream. - * - * @param code - * the string to read - * @return a {@link CharStream} to be used in the lexer phase - */ - public static CharStream getCharStream(String code) { - return CharStreams.fromString(code); - } - - /** - * Creates and initialises a lexer for a given character {@code stream} with - * the given {@code logger} attached to it. - * - * @param stream - * the character stream to read from - * @param logger - * the logger to attach - * @return the initialised lexer - */ - public static BoppiLexer getLexer(CharStream stream, Logger logger) { - BoppiLexer lexer = new BoppiLexer(stream); - lexer.removeErrorListeners(); - lexer.addErrorListener(new BaseErrorListener() { - @Override - public void syntaxError(Recognizer r, Object oSym, int l, int c, String msg, RecognitionException e) { - logger.severe("" + l + ":" + c + " " + msg); - } - }); - - return lexer; - } - - /** - * Creates and initialises a parser for a given {@code lexer} with the given - * {@code logger} attached to it. - * - * @param lexer - * the lexer to read from - * @param logger - * the logger to attach - * @return the initialised parser - */ - public static BoppiParser getParser(BoppiLexer lexer, Logger logger) { - BoppiParser parser = new BoppiParser(new CommonTokenStream(lexer)); - parser.removeErrorListeners(); - parser.addErrorListener(new BaseErrorListener() { - @Override - public void syntaxError(Recognizer r, Object oSym, int l, int c, String msg, RecognitionException e) { - logger.severe("" + l + ":" + c + " " + msg); - } - }); - - return parser; - } - - /** - * Checks a (sub)program with the {@link BoppiChecker} and returns the - * annotations. - * - * @param program - * the parse tree to check - * @param logger - * the logger to write checker messages to - * @return the annotations made by the checker - */ - public static Annotations getAnnotations(ParseTree program, Logger logger) { - return BoppiChecker.checkProgram(program, logger); - } - - /** - * Generates ILOC code for an annotated program. - * - * @param program - * the parse tree to convert to ILOC - * @param annotations - * the annotations object provided by the checking phase - * @param logger - * the logger to write checker messages to - * @return an ILOC program - */ - public static Program getILOC(ParseTree program, Logger logger, Annotations annotations) { - return BoppiGenerator.generateProgram(program, annotations, logger); - } - - /** - * Replaces all handlers for the given {@code logger} with a handler that - * appends to the list that is returned. - * - * @param logger - * the logger to change - * @return the list to which messages are logged - */ - public static List makeListLog(Logger logger) { - List log = new ArrayList(); - Handler listLogger = new Handler() { - @Override - public void close() throws SecurityException { - } - - @Override - public void flush() { - } - - @Override - public void publish(LogRecord record) { - log.add(record); - } - }; - - for (Handler handler : logger.getHandlers()) - logger.removeHandler(handler); - - logger.setUseParentHandlers(false); - logger.addHandler(listLogger); - - return log; - } - /** * Shorthand for the compiler chain from {@link CharStream} to * {@link Program}. @@ -275,21 +139,14 @@ public class ToolChain { sb.append("digraph {\n"); new ParseTreeWalker().walk(new ParseTreeListener() { + @Override + public void enterEveryRule(ParserRuleContext ctx) { + } + private String escape(String str) { return str.replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """); } - @Override - public void visitTerminal(TerminalNode node) { - sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText()) + ">;shape=rect]\n"); - } - - @Override - public void visitErrorNode(ErrorNode node) { - sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText()) - + ">;style=filled;fillcolor=red]\n"); - } - @Override public void exitEveryRule(ParserRuleContext ctx) { float hue = (ctx.getClass().hashCode() % 65521) / 65521.0f; @@ -310,11 +167,154 @@ public class ToolChain { } @Override - public void enterEveryRule(ParserRuleContext ctx) { + public void visitErrorNode(ErrorNode node) { + sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText()) + + ">;style=filled;fillcolor=red]\n"); + } + + @Override + public void visitTerminal(TerminalNode node) { + sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText()) + ">;shape=rect]\n"); } }, tree); sb.append("}\n"); return sb.toString(); } + + /** + * Checks a (sub)program with the {@link BoppiChecker} and returns the + * annotations. + * + * @param program + * the parse tree to check + * @param logger + * the logger to write checker messages to + * @return the annotations made by the checker + */ + public static Annotations getAnnotations(ParseTree program, Logger logger) { + return BoppiChecker.checkProgram(program, logger); + } + + /** + * Opens a file for reading and returns its charstream. Throws an unhandled + * exception if the file could not be read. + * + * @param file + * the file to read + * @return a {@link CharStream} to be used in the lexer phase + */ + public static CharStream getCharStream(Path file) { + try { + return CharStreams.fromFileName(file.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Reads a string as a charstream. + * + * @param code + * the string to read + * @return a {@link CharStream} to be used in the lexer phase + */ + public static CharStream getCharStream(String code) { + return CharStreams.fromString(code); + } + + /** + * Generates ILOC code for an annotated program. + * + * @param program + * the parse tree to convert to ILOC + * @param annotations + * the annotations object provided by the checking phase + * @param logger + * the logger to write checker messages to + * @return an ILOC program + */ + public static Program getILOC(ParseTree program, Logger logger, Annotations annotations) { + return BoppiGenerator.generateProgram(program, annotations, logger); + } + + /** + * Creates and initialises a lexer for a given character {@code stream} with + * the given {@code logger} attached to it. + * + * @param stream + * the character stream to read from + * @param logger + * the logger to attach + * @return the initialised lexer + */ + public static BoppiLexer getLexer(CharStream stream, Logger logger) { + BoppiLexer lexer = new BoppiLexer(stream); + lexer.removeErrorListeners(); + lexer.addErrorListener(new BaseErrorListener() { + @Override + public void syntaxError(Recognizer r, Object oSym, int l, int c, String msg, RecognitionException e) { + logger.severe("" + l + ":" + c + " " + msg); + } + }); + + return lexer; + } + + /** + * Creates and initialises a parser for a given {@code lexer} with the given + * {@code logger} attached to it. + * + * @param lexer + * the lexer to read from + * @param logger + * the logger to attach + * @return the initialised parser + */ + public static BoppiParser getParser(BoppiLexer lexer, Logger logger) { + BoppiParser parser = new BoppiParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.addErrorListener(new BaseErrorListener() { + @Override + public void syntaxError(Recognizer r, Object oSym, int l, int c, String msg, RecognitionException e) { + logger.severe("" + l + ":" + c + " " + msg); + } + }); + + return parser; + } + + /** + * Replaces all handlers for the given {@code logger} with a handler that + * appends to the list that is returned. + * + * @param logger + * the logger to change + * @return the list to which messages are logged + */ + public static List makeListLog(Logger logger) { + List log = new ArrayList(); + Handler listLogger = new Handler() { + @Override + public void close() throws SecurityException { + } + + @Override + public void flush() { + } + + @Override + public void publish(LogRecord record) { + log.add(record); + } + }; + + for (Handler handler : logger.getHandlers()) + logger.removeHandler(handler); + + logger.setUseParentHandlers(false); + logger.addHandler(listLogger); + + return log; + } } diff --git a/src/pp/s1184725/boppi/Variable.java b/src/pp/s1184725/boppi/Variable.java index ebe13d2..783e2f8 100644 --- a/src/pp/s1184725/boppi/Variable.java +++ b/src/pp/s1184725/boppi/Variable.java @@ -13,6 +13,8 @@ public class Variable { private final T type; private final int depth; private final int offset; + private boolean constant; + private boolean assigned, possiblyAssigned; /** * Creates a variable with the given type instance and memory offset. @@ -28,15 +30,24 @@ public class Variable { this.type = type; this.depth = depth; this.offset = offset; + this.assigned = false; + this.possiblyAssigned = false; } /** - * Gets the type of this variable instance. - * - * @return the type + * Marks this variable assigned. */ - public T getType() { - return type; + public void assign() { + assigned = true; + } + + /** + * Gets the lexical depth of this variable instance. + * + * @return the depth + */ + public int getDepth() { + return depth; } /** @@ -49,16 +60,60 @@ public class Variable { } /** - * Gets the lexical depth of this variable instance. + * Gets the type of this variable instance. * - * @return the depth + * @return the type */ - public int getDepth() { - return depth; + public T getType() { + return type; + } + + /** + * Returns whether this variable has been assigned. + * + * @return whether this variable has been assigned + */ + public boolean isAssigned() { + return assigned; + } + + /** + * Returns whether this variable is a constant. + * + * @return whether this variable is a constant + */ + public boolean isConstant() { + return constant; + } + + /** + * Returns whether this variable has been assigned conditionally. + * + * @return whether this variable has been assigned conditionally + */ + public boolean isPossiblyAssigned() { + return possiblyAssigned; + } + + /** + * Marks this variable as possibly assigned. + */ + public void possiblyAssign() { + possiblyAssigned = true; + } + + /** + * Sets the const-ness of this variable. + * + * @param constant + * whether this variable is a constant + */ + public void setConstant(boolean constant) { + this.constant = constant; } @Override public String toString() { - return String.format("%s:%X@%d", type.toString(), hashCode(), offset); + return String.format("%s:%X@%d", type.toString(), hashCode(), offset); //$NON-NLS-1$ } } diff --git a/src/pp/s1184725/boppi/antlr/Boppi.g4 b/src/pp/s1184725/boppi/antlr/Boppi.g4 index b8c9894..cffa21f 100644 --- a/src/pp/s1184725/boppi/antlr/Boppi.g4 +++ b/src/pp/s1184725/boppi/antlr/Boppi.g4 @@ -1,33 +1,46 @@ grammar Boppi; import BoppiTokens; -program: expr EOF; +program: stats EOF; -expr - : singleExpr (COMPOUND singleExpr?)* +stats + : stat (COMPOUND stat?)* ; -singleExpr - : PAROPEN expr PARCLOSE #parens - | BRAOPEN expr BRACLOSE #block +stat + : declareStat + | assignStat + | expr + ; + +declareStat + : DECLARE CONSTANT? type IDENTIFIER #declare + | FUNCTION (result=type)? name=IDENTIFIER PAROPEN parameters? PARCLOSE body=expr #declareFunction + | LAMBDA IDENTIFIER+ ARROW expr #lambda + ; + +assignStat + : variable ASSIGN (assignStat | expr) #assign + ; + +expr + : PAROPEN stats PARCLOSE #parens + | BRAOPEN stats BRACLOSE #block + | op=(PLUS|MINUS|NOT) expr #prefix1 + | lhs=expr op=(MULTIPLY|DIVIDE) rhs=expr #infix1 + | lhs=expr op=(PLUS|MINUS) rhs=expr #infix2 + | lhs=expr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=expr #infix3 + | lhs=expr AND rhs=expr #infix4 + | lhs=expr OR rhs=expr #infix5 | IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read | OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write - | IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if - | WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while - | op=(PLUS|MINUS|NOT) singleExpr #prefix1 - | lhs=singleExpr op=(MULTIPLY|DIVIDE) rhs=singleExpr #infix1 - | lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2 - | lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3 - | lhs=singleExpr AND rhs=singleExpr #infix4 - | lhs=singleExpr OR rhs=singleExpr #infix5 - | DECLARE type IDENTIFIER #declare - | variable ASSIGN singleExpr #assign + | IFOPEN cond=stats IFTRUE onTrue=stats (IFFALSE onFalse=stats)? IFCLOSE #if + | WHILEOPEN cond=stats WHILETRUE onTrue=stats WHILECLOSE #while + | variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call | variable #getVariable | LITERAL10 #literalInteger | CHAR #literalCharacter | (TRUE|FALSE) #literalBoolean - | FUNCTION result=type name=IDENTIFIER PAROPEN (type IDENTIFIER (LISTDELIM type IDENTIFIER)*)? PARCLOSE body=singleExpr #declareFunction - | variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call ; type @@ -43,3 +56,7 @@ variable | variable PROP IDENTIFIER #variableProperty | IDENTIFIER #variableSimple ; + +parameters + : type IDENTIFIER (LISTDELIM type IDENTIFIER)* + ; diff --git a/src/pp/s1184725/boppi/messages.properties b/src/pp/s1184725/boppi/messages.properties new file mode 100644 index 0000000..8dd2538 --- /dev/null +++ b/src/pp/s1184725/boppi/messages.properties @@ -0,0 +1,24 @@ +BoppiChecker.0=Could not match type %s with type %s. +BoppiChecker.1=Line %d:%d - %s +BoppiChecker.10=Record types not implemented. +BoppiChecker.11=Cannot print argument of type '%s'. +BoppiChecker.12=Cannot print argument of type '%s'. +BoppiChecker.13=Constant '%s' may already be assigned. +BoppiChecker.14=Variable '%s' may not be assigned. +BoppiChecker.15=Variable '%s' is not assigned. +BoppiChecker.2=Expected %d arguments but got %d. +BoppiChecker.3='%s' is not a function. +BoppiChecker.4=Variable must have a type %s, %s, %s or function. +BoppiChecker.5=Compound expression ends with declaration. +BoppiChecker.6=Error parsing number '%s'. +BoppiChecker.7=Expected array type but got '%s'. +BoppiChecker.8=Cannot reassign constant '%s'. +BoppiChecker.9=Unknown array property '%s'. +BoppiGenerator.0=INTERNAL: trying to increment reference for '%s' +BoppiGenerator.1=Line %d:%d - %s +BoppiGenerator.2=INTERNAL: trying to increment reference for '%s' +BoppiGenerator.3=%s: %s +BoppiGenerator.4=Error %s: %s +BoppiGenerator.5=Error parsing number '%s' +SymbolTable.0=Identifier '%s' already declared in this scope. +SymbolTable.1=Identifier '%s' is undeclared. diff --git a/src/pp/s1184725/boppi/test/BoppiTests.java b/src/pp/s1184725/boppi/test/BoppiTests.java index 3fb0e94..73649eb 100644 --- a/src/pp/s1184725/boppi/test/BoppiTests.java +++ b/src/pp/s1184725/boppi/test/BoppiTests.java @@ -70,6 +70,19 @@ public class BoppiTests { logLevel = DEFAULT_LOG_LEVEL; } + /** + * Writes all program output and log messages to the standard output. + */ + public static void printOutput() { + if (out != null) + for (String line : out) + System.out.println(">>> " + line); + + System.out.println(); + log.forEach((message) -> System.out.println(message.getMessage())); + System.out.println(); + } + /** * Parses a string of code, logging to {@link #log}. * @@ -213,16 +226,22 @@ public class BoppiTests { logger.setLevel(Level.FINEST); ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + logger.info("done parsing"); Annotations annotations = ToolChain.getAnnotations(ast, logger); + logger.info("done checking"); Program program = ToolChain.getILOC(ast, logger, annotations); - - System.out.println(program.prettyPrint()); + logger.info("done generating"); out = ToolChain.execute(program, logger, in).split("\n"); vm = ToolChain.machine; - + + System.out.println(program.prettyPrint()); + System.out.println(); + for (String line : out) - System.out.println(">>> "+line); + System.out.println(">>> " + line); + + System.out.println(); log.forEach((entry) -> System.out.println(entry.getMessage())); Simulator.DEBUG = false; diff --git a/src/pp/s1184725/boppi/test/ClosureTest.java b/src/pp/s1184725/boppi/test/ClosureTest.java index ced835e..854aac3 100644 --- a/src/pp/s1184725/boppi/test/ClosureTest.java +++ b/src/pp/s1184725/boppi/test/ClosureTest.java @@ -2,14 +2,18 @@ package pp.s1184725.boppi.test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import org.junit.Test; +import org.junit.*; /** - * Tests for function closures, mostly testing runtime correctness and garbage collection. + * Tests for function closures, mostly testing runtime correctness and garbage + * collection. * * @author Frank Wibbelink */ public class ClosureTest { + private int fib(int n) { + return (n < 2) ? 1 : fib(n - 1) + fib(n - 2); + } /** * Correct closure parsing @@ -18,7 +22,7 @@ public class ClosureTest { public void correctClosureParsing() { BoppiTests.parseFile("closure1.boppi"); assertThat(BoppiTests.log, is(empty())); - + BoppiTests.parseFile("closure2.boppi"); assertThat(BoppiTests.log, is(empty())); } @@ -30,7 +34,7 @@ public class ClosureTest { public void correctClosureChecking() { BoppiTests.checkFile("closure1.boppi"); assertThat(BoppiTests.log, is(empty())); - + BoppiTests.checkFile("closure2.boppi"); assertThat(BoppiTests.log, is(empty())); } @@ -44,21 +48,29 @@ public class ClosureTest { assertThat(BoppiTests.vm.getInterrupt(), is(0)); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("8", "9"))); - + BoppiTests.compileAndRunFile("closure2.boppi"); assertThat(BoppiTests.vm.getInterrupt(), is(0)); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2"))); - + BoppiTests.compileAndRunFile("closure3.boppi"); assertThat(BoppiTests.vm.getInterrupt(), is(0)); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2"))); + + BoppiTests.compileAndRunFile("fibonacciRecursive.boppi", "1", "20", "30", "31", "0"); + assertThat(BoppiTests.vm.getInterrupt(), is(0)); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining(Integer.toString(fib(1)), Integer.toString(fib(20)), + Integer.toString(fib(30)), Integer.toString(fib(31))))); } /** - * Test garbago collection. Nothing must be allocated at the end of the program. + * Test garbage collection. Nothing must be allocated at the end of the + * program. */ + @Ignore("Disabled as long as closures do not nicely clean memory.") @Test public void correctClosureCleanup() { BoppiTests.compileAndRunFile("closure1.boppi"); @@ -71,5 +83,4 @@ public class ClosureTest { assertThat(BoppiTests.vm.load(0), is(4)); } - } diff --git a/src/pp/s1184725/boppi/test/SimpleFunctionTest.java b/src/pp/s1184725/boppi/test/SimpleFunctionTest.java index 548bbe6..488bce8 100644 --- a/src/pp/s1184725/boppi/test/SimpleFunctionTest.java +++ b/src/pp/s1184725/boppi/test/SimpleFunctionTest.java @@ -22,13 +22,13 @@ public class SimpleFunctionTest { BoppiTests.parseString("function int id(int a) a; id(1)"); assertThat(BoppiTests.log, is(empty())); - BoppiTests.parseString("var int a; a := 1; function int const() a; const()"); + BoppiTests.parseString("var int a; a := 1; function int constant() a; constant()"); assertThat(BoppiTests.log, is(empty())); BoppiTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); assertThat(BoppiTests.log, is(empty())); - BoppiTests.parseString("function char const(char a, char b) a; print(const('A', 'T'))"); + BoppiTests.parseString("function char constant(char a, char b) a; print(constant('A', 'T'))"); assertThat(BoppiTests.log, is(empty())); BoppiTests.parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); @@ -40,9 +40,6 @@ public class SimpleFunctionTest { */ @Test public void wrongFunctionParsing() { - BoppiTests.parseString("function doNothing(int a, char b) a+b; print(add(4, 'T')+1)"); - assertThat(BoppiTests.log, hasSize(1)); - BoppiTests.parseString("function char add(int a, int b);"); assertThat(BoppiTests.log, hasSize(1)); @@ -61,13 +58,13 @@ public class SimpleFunctionTest { BoppiTests.checkString("function int id(int a) a; id(1); 1"); assertThat(BoppiTests.log, is(empty())); - BoppiTests.checkString("var int a; function int const() a; 1"); + BoppiTests.checkString("var int a; function int constant() a; 1"); assertThat(BoppiTests.log, is(empty())); BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); assertThat(BoppiTests.log, is(empty())); - BoppiTests.checkString("function char const(char a, char b) a; print(const('A', 'T'))"); + BoppiTests.checkString("function char constant(char a, char b) a; print(constant('A', 'T'))"); assertThat(BoppiTests.log, is(empty())); BoppiTests.checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0"); @@ -90,7 +87,7 @@ public class SimpleFunctionTest { } /** - * Correct evaluation of basic functions + * Correct evaluation of basic functions and procedures */ @Test public void correctSimpleFunctionGeneration() { @@ -100,10 +97,10 @@ public class SimpleFunctionTest { BoppiTests.compileAndRunString("function int id(int a) a; id(1); 1"); assertThat(BoppiTests.log, is(empty())); - BoppiTests.compileAndRunString("var int a; function int const(int b) a; 1"); + BoppiTests.compileAndRunString("var int a; function int constant(int b) a; 1"); assertThat(BoppiTests.log, is(empty())); - BoppiTests.compileAndRunString("var int a; a := 1; function int const(int b) a; print(const(4))"); + BoppiTests.compileAndRunString("var int a; a := 1; function int constant(int b) a; print(constant(4))"); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("1"))); @@ -111,9 +108,13 @@ public class SimpleFunctionTest { assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("10"))); - BoppiTests.compileAndRunString("function char const(char a, char b) a; print(const('A', 'T'))"); + BoppiTests.compileAndRunString("function char constant(char a, char b) a; print(constant('A', 'T'))"); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("A"))); + + BoppiTests.compileAndRunFile("simpleProcedure.boppi", "8"); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining("5", "16"))); } /** diff --git a/src/pp/s1184725/boppi/test/SimpleVariableTest.java b/src/pp/s1184725/boppi/test/SimpleVariableTest.java index f1364f7..a3180be 100644 --- a/src/pp/s1184725/boppi/test/SimpleVariableTest.java +++ b/src/pp/s1184725/boppi/test/SimpleVariableTest.java @@ -34,8 +34,14 @@ public class SimpleVariableTest { BoppiTests.parseString("var 'c' varname"); assertThat(BoppiTests.log, not(empty())); + BoppiTests.parseString("var bool int; int := true"); + assertThat(BoppiTests.log, not(empty())); + BoppiTests.parseString("var bool; true true;"); assertThat(BoppiTests.log, hasSize(2)); + + BoppiTests.parseString("var int a; var int b; a := 4 + b := 3;"); + assertThat(BoppiTests.log, hasSize(1)); } /** @@ -54,10 +60,14 @@ public class SimpleVariableTest { public void correctVariableChecking() { BoppiTests.checkFile("simpleVariable.boppi"); assertThat(BoppiTests.log, is(empty())); + + BoppiTests.checkString("var const int x; x := 3; var x y; y := x;"); + assertThat(BoppiTests.log, is(empty())); + } /** - * Variable use with type errors + * Variable use with type errors, undefined variables and reassigning constants */ @Test public void wrongVariableChecking() { @@ -73,6 +83,15 @@ public class SimpleVariableTest { BoppiTests.checkString("var bool endsWithDeclaration;"); assertThat(BoppiTests.log, hasSize(1)); + BoppiTests.checkString("var int x; 2+x;"); + assertThat(BoppiTests.log, hasSize(1)); + + BoppiTests.checkString("var const int x; 2+x;"); + assertThat(BoppiTests.log, hasSize(1)); + + BoppiTests.checkString("var const int x; x := 3; var int y; y := x; x := 2;"); + assertThat(BoppiTests.log, hasSize(1)); + BoppiTests.checkString("var bool var1; var var1 var2; var2 := 'c' "); assertThat(BoppiTests.log, hasSize(1)); } diff --git a/src/pp/s1184725/boppi/test/programs/closure1.boppi b/src/pp/s1184725/boppi/test/programs/closure1.boppi index 0c1af52..1d325e5 100644 --- a/src/pp/s1184725/boppi/test/programs/closure1.boppi +++ b/src/pp/s1184725/boppi/test/programs/closure1.boppi @@ -1,3 +1,5 @@ +//Currying addition + function (int)->int bindAdd(int a) { var int left; left := a; diff --git a/src/pp/s1184725/boppi/test/programs/closure2.boppi b/src/pp/s1184725/boppi/test/programs/closure2.boppi index 0dffedd..481d79e 100644 --- a/src/pp/s1184725/boppi/test/programs/closure2.boppi +++ b/src/pp/s1184725/boppi/test/programs/closure2.boppi @@ -1,3 +1,6 @@ +//"Currying" an arbitrary Int->Int->Int function +//Logging the number of calls to an Int->Int->Int function by embedding it in a closure + function (int)->int bindInt((int,int)->int f, int first) { function int return(int second) f(first,second); return diff --git a/src/pp/s1184725/boppi/test/programs/closure3.boppi b/src/pp/s1184725/boppi/test/programs/closure3.boppi index 2d099e0..d14e1d2 100644 --- a/src/pp/s1184725/boppi/test/programs/closure3.boppi +++ b/src/pp/s1184725/boppi/test/programs/closure3.boppi @@ -1,3 +1,6 @@ +//Logging the number of calls to an Int->Int->Int function by embedding it in a closure +//Wrapping the whole inside a main function to test closure cleanup + function int main1() { function int add(int a, int b) a+b; diff --git a/src/pp/s1184725/boppi/test/programs/fibonacciIterative.boppi b/src/pp/s1184725/boppi/test/programs/fibonacciIterative.boppi index 40d7212..a1e52f4 100644 --- a/src/pp/s1184725/boppi/test/programs/fibonacciIterative.boppi +++ b/src/pp/s1184725/boppi/test/programs/fibonacciIterative.boppi @@ -2,6 +2,7 @@ var int n; var int i; var int current; var int previous; +previous := 0; current := 1; read(n); diff --git a/src/pp/s1184725/boppi/test/programs/fibonacciRecursive.boppi b/src/pp/s1184725/boppi/test/programs/fibonacciRecursive.boppi new file mode 100644 index 0000000..20a2868 --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/fibonacciRecursive.boppi @@ -0,0 +1,44 @@ +//Recursive fibonacci with memoization closure + +var int n; + +function int fib(int n) { + if n < 2 then + 1 + else + fib(n-1)+fib(n-2) + fi +}; + +function fib memoize(fib f) { + var [50]int memo; + + var int i; i := 0; while i < 50 do + memo[i] := 0; + i := i+1; + od; + + function int memoFib(int n) { + if n >= 0 && n < 50 then + if memo[n] > 0 then + memo[n] + else + memo[n] := f(n) + fi + else + f(n) + fi + }; + + memoFib +}; + +fib := memoize(fib); + + +read(n); + +while n > 0 do + print(fib(n)); + read(n) +od; diff --git a/src/pp/s1184725/boppi/test/programs/simpleProcedure.boppi b/src/pp/s1184725/boppi/test/programs/simpleProcedure.boppi new file mode 100644 index 0000000..7f9c118 --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/simpleProcedure.boppi @@ -0,0 +1,12 @@ +var int x; + +function setX(int n) { + x := n; +}; + +setX(5); +print(x); + +var const int y; +setX(read(y)*2); +print(x); diff --git a/src/pp/s1184725/boppi/util/InteractivePrompt.java b/src/pp/s1184725/boppi/util/InteractivePrompt.java index 42f7150..946573a 100644 --- a/src/pp/s1184725/boppi/util/InteractivePrompt.java +++ b/src/pp/s1184725/boppi/util/InteractivePrompt.java @@ -1,5 +1,6 @@ package pp.s1184725.boppi.util; +import java.io.ByteArrayOutputStream; import java.util.Scanner; import java.util.logging.Logger; @@ -85,7 +86,10 @@ public class InteractivePrompt { System.out.println("--- "+line); } - for (String str : ToolChain.execute(program, logger, "").split("\n")) + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ToolChain.execute(program, logger, System.in, out); + + for (String str : out.toString().split("\r\n|\r")) System.out.println("<<< "+str); } } diff --git a/src/pp/s1184725/boppi/util/RegisterPool.java b/src/pp/s1184725/boppi/util/RegisterPool.java index 58ebf24..a8d681a 100644 --- a/src/pp/s1184725/boppi/util/RegisterPool.java +++ b/src/pp/s1184725/boppi/util/RegisterPool.java @@ -23,6 +23,18 @@ public class RegisterPool { private Logger logger; private List regFree, regInUse; + /** + * Creates a new register pool + * + * @param logger + * the logger to use + */ + public RegisterPool(Logger logger) { + regFree = new ArrayList<>(); + regInUse = new ArrayList<>(); + this.logger = logger; + } + private void blockReg(Reg reg) { if (reg == null) logger.severe("INTERNAL: cannot reserve null register."); @@ -34,6 +46,55 @@ public class RegisterPool { } } + /** + * Blocks two registers while running some code. + * + * @param + * the return type of the code + * @param r1 + * the first register to reserve + * @param r2 + * the second register to reserve + * @param code + * the code to run + * @return the return value of the code + */ + public T blockReg(Reg r1, Reg r2, Supplier code) { + return blockReg(r1, () -> blockReg(r2, code)); + } + + /** + * Blocks a register while running some code. + * + * @param r1 + * the register to reserve + * @param code + * the code to run + */ + public void blockReg(Reg r1, Runnable code) { + blockReg(r1); + code.run(); + freeReg(r1); + } + + /** + * Blocks a register while running some code. + * + * @param + * the return type of the code + * @param r1 + * the register to reserve + * @param code + * the code to run + * @return the return value of the code + */ + public T blockReg(Reg r1, Supplier code) { + blockReg(r1); + T result = code.get(); + freeReg(r1); + return result; + } + private void freeReg(Reg reg) { if (reg == null) { logger.severe("INTERNAL: cannot free null register."); @@ -45,6 +106,15 @@ public class RegisterPool { } } + /** + * Returns the list of registers currently in use. + * + * @return the list of registers in use + */ + public List getInUse() { + return regInUse; + } + private Reg makeReg() { return makeRegThatIsNot(null); } @@ -65,24 +135,14 @@ public class RegisterPool { } /** - * Creates a new register pool + * Runs some code with two registers allocated. * - * @param logger - * the logger to use + * @param code + * the code to run + * @return the second register used in the code */ - public RegisterPool(Logger logger) { - regFree = new ArrayList<>(); - regInUse = new ArrayList<>(); - this.logger = logger; - } - - /** - * Returns the list of registers currently in use. - * - * @return the list of registers in use - */ - public List getInUse() { - return regInUse; + public Reg withReg(BiConsumer code) { + return withReg((r2) -> withReg((r1) -> code.accept(r1, r2))); } /** @@ -99,64 +159,4 @@ public class RegisterPool { return r; } - /** - * Runs some code with two registers allocated. - * - * @param code - * the code to run - * @return the second register used in the code - */ - public Reg withReg(BiConsumer code) { - return withReg((r2) -> withReg((r1) -> code.accept(r1, r2))); - } - - /** - * Blocks a register while running some code. - * - * @param - * the return type of the code - * @param r1 - * the register to reserve - * @param code - * the code to run - * @return the return value of the code - */ - public T blockReg(Reg r1, Supplier code) { - blockReg(r1); - T result = code.get(); - freeReg(r1); - return result; - } - - /** - * Blocks a register while running some code. - * - * @param r1 - * the register to reserve - * @param code - * the code to run - */ - public void blockReg(Reg r1, Runnable code) { - blockReg(r1); - code.run(); - freeReg(r1); - } - - /** - * Blocks two registers while running some code. - * - * @param - * the return type of the code - * @param r1 - * the first register to reserve - * @param r2 - * the second register to reserve - * @param code - * the code to run - * @return the return value of the code - */ - public T blockReg(Reg r1, Reg r2, Supplier code) { - return blockReg(r1, () -> blockReg(r2, code)); - } - }