extracted strings, sorted methods, added constants, added procedures, added more tests, restructured parser rules
This commit is contained in:
parent
f6cabe0ccb
commit
8fa96cb362
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Type> symbols;
|
||||
/**
|
||||
* Maps variable instances to the AST node they reside in.
|
||||
*/
|
||||
public Map<Variable<Type>,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<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import pp.s1184725.boppi.type.*;
|
|||
public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||
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<Type> {
|
|||
protected BoppiChecker(Logger logger, Annotations annotations) {
|
||||
an = annotations;
|
||||
log = logger;
|
||||
inLhs = false;
|
||||
inType = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +57,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
*/
|
||||
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<Type> {
|
|||
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 <T>
|
||||
* 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 <T extends ParserRuleContext> boolean isWithinRule(ParserRuleContext ctx, ParserRuleContext root,
|
||||
Class<T> 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<Type> {
|
|||
|
||||
@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<Type> {
|
|||
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<Type> {
|
|||
public Type visitDeclare(DeclareContext ctx) {
|
||||
try {
|
||||
Variable<Type> 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<Type> {
|
|||
@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<Type> 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<Type> {
|
|||
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<Type> {
|
|||
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<Type> 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<Type> {
|
|||
|
||||
@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,12 +356,34 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
|
||||
@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
|
||||
|
@ -298,8 +392,8 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
|
||||
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,20 +416,26 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
@ -346,16 +446,14 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
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<Type> {
|
|||
try {
|
||||
Variable<Type> 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<Type> {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,13 @@ import pp.s1184725.boppi.util.RegisterPool;
|
|||
* @author Frank Wibbelink
|
||||
*/
|
||||
public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
||||
/**
|
||||
* 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<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
*/
|
||||
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<Reg> {
|
|||
* 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<Reg> {
|
|||
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<Reg> {
|
|||
* 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<Reg> {
|
|||
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<Reg> {
|
|||
* 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<Reg> {
|
|||
* 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<Reg> {
|
|||
* 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<Reg> {
|
|||
* 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<Reg> {
|
|||
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<Reg> {
|
|||
*/
|
||||
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<Reg> {
|
|||
*/
|
||||
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<Reg> {
|
|||
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<Reg> {
|
|||
} 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<Reg> {
|
|||
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<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call function:
|
||||
* <ol>
|
||||
* <li>generate load function reference</li>
|
||||
* <li>generate allocate AR</li>
|
||||
* <li>visit parameters and generate store in AR</li>
|
||||
* <li>generate register save</li>
|
||||
* <li>generate store AL, AR, return address</li>
|
||||
* <li>generate increment AR references</li>
|
||||
* <li>generate set AR</li>
|
||||
* <li>generate call function</li>
|
||||
* <li>generate decrement AR references</li>
|
||||
* <li>generate register restore</li>
|
||||
* <li>generate restore AR</li>
|
||||
* </ol>
|
||||
*/
|
||||
@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<Type> 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<Reg> {
|
|||
Stack<Reg> 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<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
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<Reg> {
|
|||
* <li>generate entry point</li>
|
||||
* <li>visit function body</li>
|
||||
* <li>generate return</li>
|
||||
* <li>generate check whether no more AR references</li>
|
||||
* <li>if so, generate dereference local variables</li>
|
||||
* <li>generate allocate function reference</li>
|
||||
* <li>generate store entry point and AR to function reference</li>
|
||||
* <li>generate store function reference in variable</li>
|
||||
|
@ -535,67 +562,64 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
@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);
|
||||
|
||||
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,45 +631,49 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
|
||||
@Override
|
||||
public Reg visitIf(IfContext ctx) {
|
||||
Label toTrue = makeLabel("if_t");
|
||||
Label toFalse = makeLabel("if_f");
|
||||
Label toEnd = makeLabel("if_e");
|
||||
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);
|
||||
emit("", OpCode.cbr, cond, toTrue, toEnd); //$NON-NLS-1$
|
||||
|
||||
emit("", toTrue, OpCode.nop);
|
||||
emit("", toTrue, OpCode.nop); //$NON-NLS-1$
|
||||
visit(ctx.onTrue);
|
||||
|
||||
emit("end target", toEnd, OpCode.nop);
|
||||
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);
|
||||
emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$
|
||||
|
||||
emit("", toTrue, OpCode.nop);
|
||||
emit("", toTrue, OpCode.nop); //$NON-NLS-1$
|
||||
visit(ctx.onTrue);
|
||||
emit("", OpCode.jumpI, toEnd);
|
||||
emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$
|
||||
|
||||
emit("", toFalse, OpCode.nop);
|
||||
emit("", toFalse, OpCode.nop); //$NON-NLS-1$
|
||||
visit(ctx.onFalse);
|
||||
|
||||
emit("end target", toEnd, OpCode.nop);
|
||||
emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
|
||||
return null;
|
||||
} else {
|
||||
return regPool.withReg((target) -> {
|
||||
emit("", OpCode.cbr, cond, toTrue, toFalse);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -680,23 +708,23 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
|
||||
@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<Reg> {
|
|||
|
||||
@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<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
|
||||
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> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
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;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -33,19 +33,18 @@ public class CachingSymbolTable<T extends Type> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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<T>(functionScope.size()));
|
||||
public FunctionScope<T> closeFunctionScope() throws NoProgramException {
|
||||
closeScope();
|
||||
return functionScope.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,40 +68,97 @@ public class CachingSymbolTable<T extends Type> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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<T> closeFunctionScope() throws NoProgramException {
|
||||
closeScope();
|
||||
return functionScope.pop();
|
||||
public Variable<T> 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 <U>
|
||||
* 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> U withScope(Supplier<U> code) {
|
||||
openScope();
|
||||
U result = code.get();
|
||||
try {
|
||||
closeScope();
|
||||
} catch (NoProgramException e) {
|
||||
public FunctionScope<T> getFunctionScope() throws NoProgramException {
|
||||
if (symbolMapStack.isEmpty())
|
||||
throw new NoProgramException();
|
||||
|
||||
return functionScope.peek();
|
||||
}
|
||||
return result;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
functionScope.push(new FunctionScope<T>(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<T> 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<T> var = functionScope.peek().addVariable(type);
|
||||
|
||||
symbolMapStack.peek().put(id, var);
|
||||
symbolCache.put(id, var);
|
||||
return var;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,81 +183,25 @@ public class CachingSymbolTable<T extends Type> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 <U>
|
||||
* 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<T> 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<T> var = functionScope.peek().addVariable(type);
|
||||
|
||||
symbolMapStack.peek().put(id, var);
|
||||
symbolCache.put(id, var);
|
||||
return var;
|
||||
public <U> U withScope(Supplier<U> code) {
|
||||
openScope();
|
||||
U result = code.get();
|
||||
try {
|
||||
closeScope();
|
||||
} catch (NoProgramException e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T> 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<T> getFunctionScope() throws NoProgramException {
|
||||
if (symbolMapStack.isEmpty())
|
||||
throw new NoProgramException();
|
||||
|
||||
return functionScope.peek();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,25 +14,12 @@ import pp.s1184725.boppi.type.*;
|
|||
*/
|
||||
public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<T> {
|
||||
|
||||
@Override
|
||||
public void openFunctionScope() {
|
||||
super.openFunctionScope();
|
||||
System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionScope<T> closeFunctionScope() throws NoProgramException {
|
||||
System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size());
|
||||
return super.closeFunctionScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Variable<T> 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<T> get(String id) throws UndeclaredException, NoProgramException {
|
||||
System.out.println(this.getClass().getName() + ": retrieving '" + id + "' (depth "
|
||||
|
@ -40,4 +27,17 @@ public class DebugCachingSymbolTable<T extends Type> 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<T> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,15 @@ public class FunctionScope<T extends Type> {
|
|||
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<T extends Type> {
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -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 + '!';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<LogRecord> makeListLog(Logger logger) {
|
||||
List<LogRecord> log = new ArrayList<LogRecord>();
|
||||
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<LogRecord> makeListLog(Logger logger) {
|
||||
List<LogRecord> log = new ArrayList<LogRecord>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ public class Variable<T> {
|
|||
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<T> {
|
|||
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<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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$
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
| <assoc=right> 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)*
|
||||
;
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -54,11 +58,19 @@ public class ClosureTest {
|
|||
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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//Currying addition
|
||||
|
||||
function (int)->int bindAdd(int a) {
|
||||
var int left;
|
||||
left := a;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ var int n;
|
|||
var int i;
|
||||
var int current;
|
||||
var int previous;
|
||||
previous := 0;
|
||||
current := 1;
|
||||
|
||||
read(n);
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,18 @@ public class RegisterPool {
|
|||
private Logger logger;
|
||||
private List<Reg> 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 <T>
|
||||
* 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> T blockReg(Reg r1, Reg r2, Supplier<T> 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 <T>
|
||||
* 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> T blockReg(Reg r1, Supplier<T> 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<Reg> 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<Reg> getInUse() {
|
||||
return regInUse;
|
||||
public Reg withReg(BiConsumer<Reg, Reg> 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<Reg, Reg> code) {
|
||||
return withReg((r2) -> withReg((r1) -> code.accept(r1, r2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks a register while running some code.
|
||||
*
|
||||
* @param <T>
|
||||
* 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> T blockReg(Reg r1, Supplier<T> 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 <T>
|
||||
* 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> T blockReg(Reg r1, Reg r2, Supplier<T> code) {
|
||||
return blockReg(r1, () -> blockReg(r2, code));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue