diff --git a/src/pp/s1184725/boppi/Annotations.java b/src/pp/s1184725/boppi/Annotations.java index c705e92..86a35fc 100644 --- a/src/pp/s1184725/boppi/Annotations.java +++ b/src/pp/s1184725/boppi/Annotations.java @@ -4,15 +4,29 @@ import org.antlr.v4.runtime.tree.ParseTreeProperty; import pp.iloc.model.Reg; +/** + * This class holds properties of all AST nodes during compilation. + * + */ public class Annotations { + /** + * Maps nodes to registers. + */ public ParseTreeProperty registers; - public CachingSymbolTable symbols; + /** + * Maps nodes to {@link SimpleType}s. + */ public ParseTreeProperty types; + /** + * Maps nodes to {@link Variable}s. + */ public ParseTreeProperty> variables; + /** + * Creates a new annotations object with empty maps. + */ public Annotations() { registers = new ParseTreeProperty<>(); - symbols = new CachingSymbolTable<>(); types = new ParseTreeProperty<>(); variables = new ParseTreeProperty<>(); } diff --git a/src/pp/s1184725/boppi/BasicParserHelper.java b/src/pp/s1184725/boppi/BasicParserHelper.java deleted file mode 100644 index 18e39f7..0000000 --- a/src/pp/s1184725/boppi/BasicParserHelper.java +++ /dev/null @@ -1,126 +0,0 @@ -package pp.s1184725.boppi; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Handler; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -import org.antlr.v4.runtime.BaseErrorListener; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.Recognizer; -import org.antlr.v4.runtime.tree.ErrorNode; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.ParseTreeListener; -import org.antlr.v4.runtime.tree.ParseTreeWalker; -import org.antlr.v4.runtime.tree.TerminalNode; -import org.apache.commons.lang3.tuple.Pair; - -public class BasicParserHelper { - public static Pair> getParserWithLog(Path file, Logger logger) throws IOException { - return Pair.of(getParser(CharStreams.fromFileName(file.toString()), logger), makeListLog(logger)); - } - - public static Pair> getParserWithLog(String code, Logger logger) { - return Pair.of(getParser(CharStreams.fromString(code), logger), makeListLog(logger)); - } - - public static BasicParser getParser(CharStream stream, Logger logger) { - BasicLexer lexer = new BasicLexer(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); - } - }); - - BasicParser parser = new BasicParser(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; - } - - public static List makeListLog(Logger logger) { - List log = new ArrayList(); - Handler listLogger = new Handler() { - @Override - public void close() throws SecurityException {} - - @Override - public void flush() {} - - @Override - public void publish(LogRecord record) { - log.add(record); - } - }; - - for (Handler handler : logger.getHandlers()) - logger.removeHandler(handler); - - logger.setUseParentHandlers(false); - logger.addHandler(listLogger); - - return log; - } - - public static String getAnnotatedDOT(ParseTree tree, Annotations annotater) { - StringBuilder sb = new StringBuilder(); - sb.append("digraph {\n"); - - new ParseTreeWalker().walk(new ParseTreeListener() { - 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; - - sb.append("\tn"+ctx.hashCode()+"[label=<"); - sb.append("rule: "+escape(ctx.getClass().getSimpleName())+"
"); - if (annotater.registers.get(ctx) != null) - sb.append("reg: "+escape(annotater.registers.get(ctx).getName())+"
"); - if (annotater.variables.get(ctx) != null) - sb.append("var: "+escape(annotater.variables.get(ctx).toString())+"
"); - if (annotater.types.get(ctx) != null) - sb.append("type: "+escape(annotater.types.get(ctx).name())+"
"); - sb.append(">;style=filled;fillcolor=\""+hue+"+0.1+1\"]\n"); - - if (ctx.children != null) - for (ParseTree child : ctx.children) - sb.append("\tn"+ctx.hashCode()+" -> n"+child.hashCode()+"\n"); - } - - @Override - public void enterEveryRule(ParserRuleContext ctx) {} - }, tree); - sb.append("}\n"); - - return sb.toString(); - } - -} diff --git a/src/pp/s1184725/boppi/BoppiBasicChecker.java b/src/pp/s1184725/boppi/BoppiBasicChecker.java index c49200e..fcbde00 100644 --- a/src/pp/s1184725/boppi/BoppiBasicChecker.java +++ b/src/pp/s1184725/boppi/BoppiBasicChecker.java @@ -1,7 +1,6 @@ package pp.s1184725.boppi; import java.util.EmptyStackException; -import java.util.function.Supplier; import java.util.logging.Logger; import org.antlr.v4.runtime.ParserRuleContext; @@ -9,15 +8,31 @@ import org.antlr.v4.runtime.tree.ParseTree; import pp.s1184725.boppi.BasicParser.*; +/** + * This class performs type checking and variable assignment on a bare parse + * tree. + * + */ public class BoppiBasicChecker extends BasicBaseVisitor { + private CachingSymbolTable symbols; private Annotations an; private Logger log; + /** + * Creates a new checker with a logger and an object to link types and + * variables to parse tree nodes. + * + * @param logger + * the logger object to write warnings and errors to + * @param annotations + * the annotations object + */ public BoppiBasicChecker(Logger logger, Annotations annotations) { + symbols = new CachingSymbolTable<>(); an = annotations; log = logger; } - + @Override public SimpleType visit(ParseTree tree) { SimpleType type = super.visit(tree); @@ -40,7 +55,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor { @Override public SimpleType visitBlock(BlockContext ctx) { - return inScope(() -> visit(ctx.expr())); + return symbols.withScope(() -> visit(ctx.expr())); } @Override @@ -59,7 +74,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor { @Override public SimpleType visitDeclare(DeclareContext ctx) { try { - Variable var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type())); + Variable var = symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type())); an.variables.put(ctx, var); } catch (EmptyStackException e) { log.severe(getError(ctx, "Declaring variable outside a program.")); @@ -71,15 +86,15 @@ public class BoppiBasicChecker extends BasicBaseVisitor { @Override public SimpleType visitIf(IfContext ctx) { - return inScope(() -> { + return symbols.withScope(() -> { visit(ctx.cond); if (ctx.onFalse != null) { - SimpleType trueType = inScope(() -> visit(ctx.onTrue)); - SimpleType falseType = inScope(() -> visit(ctx.onFalse)); + SimpleType trueType = symbols.withScope(() -> visit(ctx.onTrue)); + SimpleType falseType = symbols.withScope(() -> visit(ctx.onFalse)); return (trueType.equals(falseType)) ? trueType : SimpleType.VOID; } else { - inScope(() -> visit(ctx.onTrue)); + symbols.withScope(() -> visit(ctx.onTrue)); return SimpleType.VOID; } }); @@ -164,7 +179,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor { @Override public SimpleType visitProgram(ProgramContext ctx) { - inScope(() -> super.visitProgram(ctx)); + symbols.withScope(() -> super.visitProgram(ctx)); return null; } @@ -194,7 +209,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor { @Override public SimpleType visitVariable(VariableContext ctx) { try { - Variable var = an.symbols.get(ctx.getText()); + Variable var = symbols.get(ctx.getText()); an.variables.put(ctx, var); return var.getType(); } catch (EmptyStackException e) { @@ -208,9 +223,9 @@ public class BoppiBasicChecker extends BasicBaseVisitor { @Override public SimpleType visitWhile(WhileContext ctx) { - return inScope(() -> { + return symbols.withScope(() -> { checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond); - inScope(() -> visit(ctx.onTrue)); + symbols.withScope(() -> visit(ctx.onTrue)); return SimpleType.VOID; }); } @@ -221,7 +236,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor { SimpleType type = visit(ctx.expr(0)); if (SimpleType.VOID.equals(type)) log.severe(getError(ctx, "Cannot print argument of type %s.", type)); - + return type; } else { ctx.expr().stream().map(this::visit).forEach((type) -> { @@ -232,26 +247,21 @@ public class BoppiBasicChecker extends BasicBaseVisitor { } } + /** + * Checks whether two types are compatible and log an error if they do not. + * + * @param type1 + * the left hand or actual type + * @param type2 + * the right hand or desired type + * @param node + * the context used for logging + */ private void checkConstraint(SimpleType type1, SimpleType type2, ParserRuleContext node) { if (!type2.equals(type1)) log.severe(getError(node, "Could not match type %s with type %s.", type1.toString(), type2.toString())); } - /** - * Helper method to correctly open a scope, run some code and close the - * scope. {@code} must take no arguments and must return a value. - * - * @param code - * the code to execute within the scope - * @return the result of {@code} - */ - protected T inScope(Supplier code) { - an.symbols.openScope(); - T result = code.get(); - an.symbols.closeScope(); - return result; - } - /** * Returns an error message for a given parse tree node. * @@ -262,7 +272,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor { * @param args * arguments for the message, see {@link String#format} */ - protected String getError(ParserRuleContext node, String message, Object... args) { + 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)); diff --git a/src/pp/s1184725/boppi/BoppiBasicGenerator.java b/src/pp/s1184725/boppi/BoppiBasicGenerator.java index 8f356b3..31c4896 100644 --- a/src/pp/s1184725/boppi/BoppiBasicGenerator.java +++ b/src/pp/s1184725/boppi/BoppiBasicGenerator.java @@ -1,11 +1,9 @@ package pp.s1184725.boppi; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import org.antlr.v4.runtime.RuleContext; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.RuleNode; +import org.antlr.v4.runtime.tree.*; import pp.iloc.model.*; import pp.s1184725.boppi.BasicParser.*; @@ -16,8 +14,12 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { private int regNum; private final Reg zero = new Reg("zero"); - static Map ops = new HashMap() { + /** + * Maps operator tokens to ILOC opcodes. + */ + private static Map ops = new HashMap() { private static final long serialVersionUID = 8979722313842633807L; + { put(BasicLexer.AND, OpCode.and); put(BasicLexer.DIVIDE, OpCode.div); @@ -34,6 +36,12 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { } }; + /** + * Creates a new generator with the given variable annotations. + * + * @param annotations + * the annotation object to use during generation + */ public BoppiBasicGenerator(Annotations annotations) { an = annotations; prog = new Program(); @@ -42,6 +50,13 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { emit(OpCode.loadI, new Num(0), zero); } + /** + * Retrieves a register for a given tree node. + * + * @param parseTree + * the tree node + * @return a register for the tree node + */ private Reg getReg(ParseTree parseTree) { Reg reg = an.registers.get(parseTree); if (reg == null) { @@ -51,6 +66,16 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { return reg; } + /** + * Attaches the register used in the {@code source} tree node to the + * {@link destination} node. + * + * @param source + * the node to retrieve the register from + * @param destination + * the node to copy the register to + * @return the register that was copied + */ private Reg copyReg(ParseTree source, ParseTree destination) { Reg reg = an.registers.get(source); an.registers.put(destination, reg); @@ -92,7 +117,7 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { public Void visitAssign(AssignContext ctx) { visit(ctx.singleExpr()); copyReg(ctx.singleExpr(), ctx); - + switch (an.types.get(ctx)) { case CHAR: emit(OpCode.cstoreAI, getReg(ctx), zero, new Num(an.variables.get(ctx.variable()).getOffset())); @@ -179,7 +204,7 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { emit(OpCode.i2c, getReg(ctx), getReg(ctx)); return null; } - + @Override public Void visitRead(ReadContext ctx) { for (VariableContext expr : ctx.variable()) { @@ -191,22 +216,22 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { emit(OpCode.storeAI, getReg(ctx), zero, new Num(an.variables.get(expr).getOffset())); break; case CHAR: - //Get input until at least 1 character - Label getTarget = new Label("lcin_l"+ctx.hashCode()); - Label continueTarget = new Label("lcin_e"+ctx.hashCode()); + // Get input until at least 1 character + Label getTarget = new Label("lcin_l" + ctx.hashCode()); + Label continueTarget = new Label("lcin_e" + ctx.hashCode()); Reg countReg = new Reg("tempReg"); emit(getTarget, OpCode.cin, new Str("")); emit(OpCode.pop, countReg); emit(OpCode.cbr, countReg, continueTarget, getTarget); - - //Get character + + // Get character emit(continueTarget, OpCode.cpop, getReg(ctx)); emit(OpCode.cstoreAI, getReg(ctx), zero, new Num(an.variables.get(expr).getOffset())); - - //Pop all remaining characters - Label loopTarget = new Label("lcpop_l"+ctx.hashCode()); - Label iterTarget = new Label("lcpop_c"+ctx.hashCode()); - Label stopTarget = new Label("lcpop_e"+ctx.hashCode()); + + // Pop all remaining characters + Label loopTarget = new Label("lcpop_l" + ctx.hashCode()); + Label iterTarget = new Label("lcpop_c" + ctx.hashCode()); + Label stopTarget = new Label("lcpop_e" + ctx.hashCode()); Reg tempReg2 = new Reg("tempReg2"); emit(loopTarget, OpCode.subI, countReg, new Num(1), countReg); emit(OpCode.cbr, countReg, iterTarget, stopTarget); @@ -220,46 +245,45 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { } return null; } - + @Override public Void visitIf(IfContext ctx) { - Label toTrue = new Label("if_t"+ctx.hashCode()); - Label toFalse = new Label("if_f"+ctx.hashCode()); - Label toEnd = new Label("if_e"+ctx.hashCode()); - + Label toTrue = new Label("if_t" + ctx.hashCode()); + Label toFalse = new Label("if_f" + ctx.hashCode()); + Label toEnd = new Label("if_e" + ctx.hashCode()); + visit(ctx.cond); if (ctx.onFalse == null) { emit(OpCode.cbr, getReg(ctx.cond), toTrue, toEnd); - + emit(toTrue, OpCode.nop); visit(ctx.onTrue); - } - else { + } else { emit(OpCode.cbr, getReg(ctx.cond), toTrue, toFalse); - + emit(toTrue, OpCode.nop); visit(ctx.onTrue); if (an.types.get(ctx) != SimpleType.VOID) emit(OpCode.i2i, getReg(ctx.onTrue), getReg(ctx)); emit(OpCode.jumpI, toEnd); - + emit(toFalse, OpCode.nop); visit(ctx.onFalse); if (an.types.get(ctx) != SimpleType.VOID) emit(OpCode.i2i, getReg(ctx.onFalse), getReg(ctx)); - + } emit(toEnd, OpCode.nop); return null; } - + @Override public Void visitWhile(WhileContext ctx) { - Label toLoop = new Label("while_t"+ctx.hashCode()); - Label toCond = new Label("while_f"+ctx.hashCode()); - Label toEnd = new Label("while_e"+ctx.hashCode()); - + Label toLoop = new Label("while_t" + ctx.hashCode()); + Label toCond = new Label("while_f" + ctx.hashCode()); + Label toEnd = new Label("while_e" + ctx.hashCode()); + emit(OpCode.jumpI, toCond); emit(toLoop, OpCode.nop); visit(ctx.onTrue); @@ -267,7 +291,7 @@ public class BoppiBasicGenerator extends BasicBaseVisitor { visit(ctx.cond); emit(OpCode.cbr, getReg(ctx.cond), toLoop, toEnd); emit(toEnd, OpCode.nop); - + return null; } diff --git a/src/pp/s1184725/boppi/CachingSymbolTable.java b/src/pp/s1184725/boppi/CachingSymbolTable.java index e375b5a..efdba75 100644 --- a/src/pp/s1184725/boppi/CachingSymbolTable.java +++ b/src/pp/s1184725/boppi/CachingSymbolTable.java @@ -1,11 +1,22 @@ package pp.s1184725.boppi; import java.util.*; +import java.util.function.Supplier; +/** + * This class maps string identifiers to {@link Variable} instances. It takes + * care of lexical scope, memory offsets and variable lifetime. This + * implementation has a theoretical lookup time of O(1), scope opening time of + * O(1) and scope closing time of O(k log k log n) with k the number of + * identifiers going out of scope. It is agnostic to the type system used. + * + * @param + * the typing class + */ public class CachingSymbolTable { - protected Stack>> symbolMapStack; + protected Stack>> symbolMapStack; protected Stack offsets; - protected Map> symbolCache; + protected Map> symbolCache; protected int offset; /** @@ -17,7 +28,7 @@ public class CachingSymbolTable { offsets = new Stack<>(); offset = 0; } - + /** * Opens a lexical scope. */ @@ -25,47 +36,79 @@ public class CachingSymbolTable { symbolMapStack.push(new HashMap<>()); offsets.push(offset); } - + /** * Closes a lexical scope, removing all definitions within this scope. - * @throws EmptyStackException if no scope is open + * + * @throws EmptyStackException + * if no scope is open */ public void closeScope() throws EmptyStackException { - Map> deletions = symbolMapStack.pop(); + Map> deletions = symbolMapStack.pop(); for (String identifier : deletions.keySet()) { - Optional> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier)).map((map) -> map.get(identifier)).reduce((__, snd) -> snd); + Optional> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier)) + .map((map) -> map.get(identifier)).reduce((__, snd) -> snd); maybeType.ifPresent((var) -> symbolCache.replace(identifier, var)); if (!maybeType.isPresent()) symbolCache.remove(identifier); } offset = offsets.pop(); } - + /** - * Associates an identifier with a certain type in the current lexical scope. - * @param id the name of the identifier - * @param type the type of the identifier + * Opens a scope, executes the given code and closes the scope. This is a + * helper method to make sure {@link #openScope()} and {@link #closeScope()} + * calls are balanced. {@code code} must take no arguments and must return a + * value. + * + * @param + * the type of the code's return value + * @param code + * the code to execute within the scope + * @return the return value of the code + */ + public U withScope(Supplier code) { + openScope(); + U result = code.get(); + closeScope(); + return result; + } + + /** + * 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 + * + * @param id + * the name of the identifier + * @param type + * the type of the identifier * @return the type of the identifier - * @throws Exception if the identifier is declared in the current scope already - * @throws EmptyStackException if no scope is open + * @throws Exception + * if the identifier is declared in the current scope already + * @throws EmptyStackException + * if no scope is open */ public Variable put(String id, T type) throws Exception, EmptyStackException { if (symbolMapStack.peek().containsKey(id)) throw new Exception(String.format("Identifier '%s' already declared in this scope.", id)); - + Variable var = new Variable<>(type, offset); // TODO refactor to get type size - offset += ((SimpleType)type).getSize(); + offset += ((SimpleType) type).getSize(); symbolMapStack.peek().put(id, var); symbolCache.put(id, var); return var; } - + /** * Returns whether the given identifier is defined. - * @param id the name of the identifier + * + * @param id + * the name of the identifier * @return true if the identifier has a defined type - * @throws EmptyStackException if no scope is open + * @throws EmptyStackException + * if no scope is open */ public boolean has(String id) throws EmptyStackException { if (symbolMapStack.isEmpty()) @@ -73,13 +116,17 @@ public class CachingSymbolTable { return symbolCache.containsKey(id); } - + /** * Retrieves the type of an identifier, if any. - * @param id the name of the identifier + * + * @param id + * the name of the identifier * @return the type of the identifier - * @throws Exception if the identifier is not defined - * @throws EmptyStackException if no scope is open + * @throws Exception + * if the identifier is not defined + * @throws EmptyStackException + * if no scope is open */ public Variable get(String id) throws Exception, EmptyStackException { if (!this.has(id)) diff --git a/src/pp/s1184725/boppi/SimpleType.java b/src/pp/s1184725/boppi/SimpleType.java index fadd815..0ccc39a 100644 --- a/src/pp/s1184725/boppi/SimpleType.java +++ b/src/pp/s1184725/boppi/SimpleType.java @@ -1,21 +1,44 @@ package pp.s1184725.boppi; + import static pp.s1184725.boppi.BasicLexer.*; import pp.iloc.eval.Machine; +/** + * This class provides 4 basic data types: + *
    + *
  • character (sized {@link Machine#DEFAULT_CHAR_SIZE})
  • + *
  • integer (sized {@link Machine#INT_SIZE})
  • + *
  • boolean (sized {@link Machine#INT_SIZE})
  • + *
  • void (empty/unit type)
  • + *
+ */ public enum SimpleType { INT(Machine.INT_SIZE), CHAR(Machine.DEFAULT_CHAR_SIZE), BOOL(Machine.INT_SIZE), VOID(0); - + private final int size; - + + /** + * Gets the size of this data type. + * + * @return the size (in bytes) of this type + */ public int getSize() { return size; } - + private SimpleType(int typeSize) { size = typeSize; } - + + /** + * Maps a token lexed by {@link BasicLexer} to a data type. + * + * @param token + * the token to parse + * @return the data type of the token, {@link SimpleType#VOID} if + * unrecognized + */ public static SimpleType fromToken(int token) { switch (token) { case INTTYPE: diff --git a/src/pp/s1184725/boppi/ToolChain.java b/src/pp/s1184725/boppi/ToolChain.java new file mode 100644 index 0000000..2e4deaa --- /dev/null +++ b/src/pp/s1184725/boppi/ToolChain.java @@ -0,0 +1,290 @@ +package pp.s1184725.boppi; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.*; +import java.util.logging.*; + +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.tree.*; +import org.apache.commons.lang3.tuple.Pair; + +import pp.iloc.Simulator; +import pp.iloc.model.Program; + +/** + * This class provides methods for all steps in the Boppi tool chain. + * + */ +public class ToolChain { + + /** + * 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 BasicLexer getLexer(CharStream stream, Logger logger) { + BasicLexer lexer = new BasicLexer(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 BasicParser getParser(BasicLexer lexer, Logger logger) { + BasicParser parser = new BasicParser(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 BoppiBasicChecker} 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) { + Annotations annotations = new Annotations(); + new BoppiBasicChecker(logger, annotations).visit(program); + return annotations; + } + + /** + * 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 + * @return an ILOC program + */ + public static Program getILOC(ParseTree program, Annotations annotations) { + BoppiBasicGenerator generator = new BoppiBasicGenerator(annotations); + generator.visit(program); + return generator.prog; + } + + /** + * Replaces all handlers for the given {@code logger} with a handler that + * appends to the list that is returned. + * + * @param logger + * the logger to change + * @return the list to which messages are logged + */ + public static List makeListLog(Logger logger) { + List log = new ArrayList(); + Handler listLogger = new Handler() { + @Override + public void close() throws SecurityException { + } + + @Override + public void flush() { + } + + @Override + public void publish(LogRecord record) { + log.add(record); + } + }; + + for (Handler handler : logger.getHandlers()) + logger.removeHandler(handler); + + logger.setUseParentHandlers(false); + logger.addHandler(listLogger); + + return log; + } + + /** + * Shorthand for the compiler chain from {@link CharStream} to + * {@link Program}. + * + * @param stream + * the charstream to compile + * @return a pair of the ILOC program and the logger + */ + public static Pair> compile(CharStream stream) { + Logger logger = Logger.getAnonymousLogger(); + List logs = makeListLog(logger); + + ParseTree ast = getParser(getLexer(stream, logger), logger).program(); + return Pair.of(getILOC(ast, getAnnotations(ast, logger)), logs); + } + + /** + * Shorthand for the compiler chain from {@link CharStream} to + * {@link Program}. + * + * @param stream + * the charstream to compile + * @param logger + * the logger to report compiler messages to + * @return a pair of the ILOC program and the logger + */ + public static Program compile(CharStream stream, Logger logger) { + ParseTree ast = getParser(getLexer(stream, logger), logger).program(); + return getILOC(ast, getAnnotations(ast, logger)); + } + + /** + * Runs a program using the given input and output streams. Run errors will + * be logged as {@link Level#SEVERE}. + * + * @param program + * the program to run + * @param logger + * the logger to write runtime errors to + * @param in + * the input stream the program can read from + * @param out + * the output stream the program can write to + */ + public static void execute(Program program, Logger logger, InputStream in, OutputStream out) { + Simulator s = new Simulator(program); + s.setIn(in); + s.setOut(out); + try { + s.run(); + } catch (Exception e) { + logger.severe(e.getMessage()); + } + } + + /** + * Runs a program with a given input string and returns all output as a + * string. Run errors will be logged as {@link Level#SEVERE} in the logger. + * + * @param program + * the program to run + * @param logger + * the logger to write runtime messages to + * @param input + * the string to use as input for the program + * @return the output of the program + */ + public static String execute(Program program, Logger logger, String input) { + InputStream in = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + execute(program, logger, in, out); + return out.toString().replaceAll("\r\n|\r", "\n"); + } + + /** + * Converts an annotated {@link ParseTree} to a colourful DOT graph. + * + * @param tree + * the tree to build + * @param annotater + * the objct containing annotations, if any + * @return a string containing the DOT graph + */ + public static String getAnnotatedDOT(ParseTree tree, Annotations annotater) { + StringBuilder sb = new StringBuilder(); + sb.append("digraph {\n"); + + new ParseTreeWalker().walk(new ParseTreeListener() { + 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; + + sb.append("\tn" + ctx.hashCode() + "[label=<"); + sb.append("rule: " + escape(ctx.getClass().getSimpleName()) + "
"); + if (annotater.registers.get(ctx) != null) + sb.append("reg: " + escape(annotater.registers.get(ctx).getName()) + "
"); + if (annotater.variables.get(ctx) != null) + sb.append("var: " + escape(annotater.variables.get(ctx).toString()) + "
"); + if (annotater.types.get(ctx) != null) + sb.append("type: " + escape(annotater.types.get(ctx).name()) + "
"); + sb.append(">;style=filled;fillcolor=\"" + hue + "+0.1+1\"]\n"); + + if (ctx.children != null) + for (ParseTree child : ctx.children) + sb.append("\tn" + ctx.hashCode() + " -> n" + child.hashCode() + "\n"); + } + + @Override + public void enterEveryRule(ParserRuleContext ctx) { + } + }, tree); + sb.append("}\n"); + + return sb.toString(); + } +} diff --git a/src/pp/s1184725/boppi/Variable.java b/src/pp/s1184725/boppi/Variable.java index e401b26..d876a68 100644 --- a/src/pp/s1184725/boppi/Variable.java +++ b/src/pp/s1184725/boppi/Variable.java @@ -1,24 +1,49 @@ package pp.s1184725.boppi; +/** + * This class tracks a variable's properties. It is agnostic of the type system + * used. The type reference and offset are immutable. + * + * @param + * the typing class + */ public class Variable { private final T type; private final int offset; - + + /** + * Creates a variable with the given type instance and memory offset. + * + * @param type + * the type of the variable + * @param offset + * the memory offset for this variable + */ public Variable(T type, int offset) { this.type = type; this.offset = offset; } - + + /** + * Gets the type of this variable instance. + * + * @return the type + */ public T getType() { return type; } - + + /** + * Gets the memory offset of this variable instance. + * + * @return the offset + */ public int getOffset() { return offset; } - + @Override public String toString() { - return type.toString()+" @ "+offset; + return String.format("%s:%X@%d", type.toString(), hashCode(), offset); } } diff --git a/src/pp/s1184725/boppi/test/AllTests.java b/src/pp/s1184725/boppi/test/AllTests.java new file mode 100644 index 0000000..7ddcce4 --- /dev/null +++ b/src/pp/s1184725/boppi/test/AllTests.java @@ -0,0 +1,14 @@ +package pp.s1184725.boppi.test; + +import java.nio.file.*; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ CheckerTest.class, GeneratorTest.class, ParserTest.class }) +public class AllTests { + static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/"); + +} diff --git a/src/pp/s1184725/boppi/test/CheckerTest.java b/src/pp/s1184725/boppi/test/CheckerTest.java index 0bb0f2b..3cf13a4 100644 --- a/src/pp/s1184725/boppi/test/CheckerTest.java +++ b/src/pp/s1184725/boppi/test/CheckerTest.java @@ -3,123 +3,116 @@ package pp.s1184725.boppi.test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.logging.*; -import org.apache.commons.lang3.tuple.Pair; +import org.antlr.v4.runtime.CharStream; import org.junit.Test; -import pp.s1184725.boppi.*; +import pp.s1184725.boppi.ToolChain; public class CheckerTest { - static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/"); private List log; - static private List checkAndGetLog(String code) { - Logger logger = Logger.getAnonymousLogger(); - Pair> pair = BasicParserHelper.getParserWithLog(code, logger); - new BoppiBasicChecker(logger, new Annotations()).visit(pair.getLeft().program()); - return pair.getRight(); + private void checkString(String code) { + check(ToolChain.getCharStream(code)); } - static private List checkAndGetLog(Path code) throws IOException { - Logger logger = Logger.getAnonymousLogger(); - Pair> pair = BasicParserHelper.getParserWithLog(code, logger); - new BoppiBasicChecker(logger, new Annotations()).visit(pair.getLeft().program()); - return pair.getRight(); + private void checkFile(String file) { + check(ToolChain.getCharStream(AllTests.directory.resolve(file))); } - - + private void check(CharStream stream) { + Logger logger = Logger.getAnonymousLogger(); + log = ToolChain.makeListLog(logger); + ToolChain.getAnnotations(ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(), logger); + } @Test - public void correctExpressionTest() throws Exception { - log = checkAndGetLog(directory.resolve("simpleExpression.boppi")); - assertThat(log, empty()); + public void correctExpressionTest() { + checkFile("simpleExpression.boppi"); + assertThat(log, is(empty())); } @Test public void wrongExpressionTest() { - log = checkAndGetLog("+true"); + checkString("+true"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("5 || true"); + checkString("5 || true"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("6 + 'c'"); + checkString("6 + 'c'"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("4 + print(5, 6)"); + checkString("4 + print(5, 6)"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("print(print(3, 5))"); + checkString("print(print(3, 5))"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); } @Test - public void correctVariableTest() throws Exception { - log = checkAndGetLog(directory.resolve("simpleVariable.boppi")); - assertThat(log, empty()); + public void correctVariableTest() { + checkFile("simpleVariable.boppi"); + assertThat(log, is(empty())); } @Test public void wrongVariableTest() { - log = checkAndGetLog("var bool name; name := 5"); + checkString("var bool name; name := 5"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("undefinedName"); + checkString("undefinedName"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("var undefinedType name; 1"); + checkString("var undefinedType name; 1"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("var bool endsWithDeclaration;"); + checkString("var bool endsWithDeclaration;"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("var bool var1; var var1 var2; var2 := 'c' "); + checkString("var bool var1; var var1 var2; var2 := 'c' "); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); } @Test - public void correctScopeTest() throws Exception { - log = checkAndGetLog(directory.resolve("simpleScope.boppi")); - assertThat(log, empty()); + public void correctScopeTest() { + checkFile("simpleScope.boppi"); + assertThat(log, is(empty())); } @Test public void wrongScopeTest() { - log = checkAndGetLog("var bool var1; var bool var1; 1"); + checkString("var bool var1; var bool var1; 1"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("var bool var1; var char var1; 1"); + checkString("var bool var1; var char var1; 1"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = checkAndGetLog("{ var int var1; var1 := 4}; var int var2; var1"); + checkString("{ var int var1; var1 := 4}; var int var2; var1"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); } @Test - public void correctConditionalTest() throws Exception { - log = checkAndGetLog(directory.resolve("if.boppi")); - assertThat(log, empty()); + public void correctConditionalTest() { + checkFile("if.boppi"); + assertThat(log, is(empty())); - log = checkAndGetLog(directory.resolve("while.boppi")); - assertThat(log, empty()); + checkFile("while.boppi"); + assertThat(log, is(empty())); } } diff --git a/src/pp/s1184725/boppi/test/GeneratorTest.java b/src/pp/s1184725/boppi/test/GeneratorTest.java index ed8b1cc..1a2f220 100644 --- a/src/pp/s1184725/boppi/test/GeneratorTest.java +++ b/src/pp/s1184725/boppi/test/GeneratorTest.java @@ -1,111 +1,83 @@ package pp.s1184725.boppi.test; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.*; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; -import java.util.logging.LogRecord; -import java.util.logging.Logger; +import java.util.logging.*; -import org.antlr.v4.runtime.ParserRuleContext; -import org.apache.commons.lang3.tuple.Pair; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.tree.ParseTree; import org.junit.Test; -import pp.iloc.Simulator; -import pp.s1184725.boppi.Annotations; -import pp.s1184725.boppi.BasicParser; -import pp.s1184725.boppi.BasicParserHelper; -import pp.s1184725.boppi.BoppiBasicChecker; -import pp.s1184725.boppi.BoppiBasicGenerator; +import pp.iloc.model.Program; +import pp.s1184725.boppi.*; public class GeneratorTest { - static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/"); - private Pair> result; + private List log; + private String[] out; - static private Pair> generateAndGetLog(String code, String input) { + private void compileAndRunString(String code, String... input) { + compileAndRun(ToolChain.getCharStream(code), String.join("\n", input) + "\n"); + } + + private void compileAndRunFile(String file, String... input) { + compileAndRun(ToolChain.getCharStream(AllTests.directory.resolve(file)), String.join("\n", input) + "\n"); + } + + private void compileAndRun(CharStream stream, String input) { Logger logger = Logger.getAnonymousLogger(); - Pair> pair = BasicParserHelper.getParserWithLog(code, logger); - return derp(logger, pair, input); + log = ToolChain.makeListLog(logger); + + ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + Annotations annotations = ToolChain.getAnnotations(ast, logger); + Program program = ToolChain.getILOC(ast, annotations); + out = ToolChain.execute(program, logger, input).split("\n"); } - - static private Pair> generateAndGetLog(Path code, String input) throws IOException { - Logger logger = Logger.getAnonymousLogger(); - Pair> pair = BasicParserHelper.getParserWithLog(code, logger); - return derp(logger, pair, input); - } - - static private Pair> derp(Logger logger, Pair> pair, String input) { - Annotations annotater = new Annotations(); - ParserRuleContext tree = pair.getLeft().program(); - new BoppiBasicChecker(logger, annotater).visit(tree); - BoppiBasicGenerator generator = new BoppiBasicGenerator(annotater); - generator.visit(tree); - Simulator s = new Simulator(generator.prog); - InputStream in = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - s.setIn(in); - s.setOut(out); - try { - s.run(); - } - catch (Exception e) { - logger.severe(e.getMessage()); - } - return Pair.of(out.toString().replaceAll("\r\n", "\n"), pair.getRight()); - } - - - - + @Test - public void correctExpressionTest() throws IOException { - result = generateAndGetLog(directory.resolve("simpleExpression.boppi"), ""); - assertThat(result.getRight(), empty()); + public void correctExpressionTest() { + compileAndRunFile("simpleExpression.boppi"); + assertThat(log, is(empty())); } - + @Test public void wrongExpressionTest() { - result = generateAndGetLog("1/0", ""); - assertThat(result.getRight(), hasSize(1)); - assertThat(result.getRight().get(0).getMessage(), containsString("zero")); + compileAndRunString("1/0"); + assertThat(log, hasSize(1)); + assertThat(log.get(0).getMessage(), containsString("zero")); } @Test - public void correctVariableTest() throws IOException { - result = generateAndGetLog(directory.resolve("simpleVariable.boppi"), ""); - assertThat(result.getRight(), empty()); + public void correctVariableTest() { + compileAndRunFile("simpleVariable.boppi"); + assertThat(log, is(empty())); } @Test - public void basicPrograms() throws IOException { - result = generateAndGetLog("print(5*3)", ""); - assertThat(result.getRight(), empty()); - assertThat(result.getLeft(), is("15\n")); - - result = generateAndGetLog("print('T', 'e', 's', 't', '!')", ""); - assertThat(result.getRight(), empty()); - assertThat(result.getLeft(), is("T\ne\ns\nt\n!\n")); - - result = generateAndGetLog("var int x; var int y; x := 3*(y := 4); print(x,y)", ""); - assertThat(result.getRight(), empty()); - assertThat(result.getLeft(), is("12\n4\n")); - - result = generateAndGetLog(directory.resolve("basicProgram1.boppi"), "1\nT\n"); - assertThat(result.getRight(), empty()); - assertThat(result.getLeft(), is("T\nT\n")); - - result = generateAndGetLog(directory.resolve("fibonacciIterative.boppi"), "6\n"); - assertThat(result.getRight(), empty()); - assertThat(result.getLeft(), is("13\n")); + public void basicPrograms() { + compileAndRunString("print(5*3)"); + assertThat(log, is(empty())); + assertThat(out, is(arrayContaining("15"))); + + compileAndRunString("print({var int x; x := 8; print(x)})"); + assertThat(log, is(empty())); + assertThat(out, is(arrayContaining("8", "8"))); + + compileAndRunString("print('T', 'e', 's', 't', '!')"); + assertThat(log, is(empty())); + assertThat(out, is(arrayContaining("T", "e", "s", "t", "!"))); + + compileAndRunString("var int x; var int y; x := 3*(y := 4); print(x,y)"); + assertThat(log, is(empty())); + assertThat(out, is(arrayContaining("12", "4"))); + + compileAndRunFile("basicProgram1.boppi", "1", "T"); + assertThat(log, is(empty())); + assertThat(out, is(arrayContaining("T", "T"))); + + compileAndRunFile("fibonacciIterative.boppi", "6"); + assertThat(log, is(empty())); + assertThat(out, is(arrayContaining("13"))); } } diff --git a/src/pp/s1184725/boppi/test/ParserTest.java b/src/pp/s1184725/boppi/test/ParserTest.java index af63cbd..ff02dd4 100644 --- a/src/pp/s1184725/boppi/test/ParserTest.java +++ b/src/pp/s1184725/boppi/test/ParserTest.java @@ -1,100 +1,98 @@ package pp.s1184725.boppi.test; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.logging.*; -import org.apache.commons.lang3.tuple.Pair; +import org.antlr.v4.runtime.CharStream; import org.junit.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import pp.s1184725.boppi.*; public class ParserTest { - static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/"); private List log; - - static private List parseAndGetLog(String code) { - Pair> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger()); - pair.getLeft().program(); - return pair.getRight(); + + private void parseString(String code) { + parse(ToolChain.getCharStream(code)); } - - static private List parseAndGetLog(Path code) throws IOException { - Pair> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger()); - pair.getLeft().program(); - return pair.getRight(); + + private void parseFile(String file) { + parse(ToolChain.getCharStream(AllTests.directory.resolve(file))); } - + + private void parse(CharStream stream) { + Logger logger = Logger.getAnonymousLogger(); + log = ToolChain.makeListLog(logger); + ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + } + @Test - public void correctExpressionTest() throws Exception { - log = parseAndGetLog(directory.resolve("simpleExpression.boppi")); + public void correctExpressionTest() { + parseFile("simpleExpression.boppi"); assertThat(log, empty()); } - + @Test public void wrongExpressionTest() { - log = parseAndGetLog(""); + parseString(""); assertThat(log, not(empty())); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("~"); + parseString("~"); assertThat(log, not(empty())); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("0A"); + parseString("0A"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("do"); + parseString("do"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("true true"); + parseString("true true"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); } @Test - public void correctVariableTest() throws Exception { - log = parseAndGetLog(directory.resolve("simpleVariable.boppi")); + public void correctVariableTest() { + parseFile("simpleVariable.boppi"); assertThat(log, empty()); } - + @Test public void wrongVariableTest() { - log = parseAndGetLog("var"); + parseString("var"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("var bool 5"); + parseString("var bool 5"); assertThat(log, hasSize(1)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("var 'c' varname"); + parseString("var 'c' varname"); assertThat(log, not(empty())); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); - log = parseAndGetLog("var bool; true true;"); + parseString("var bool; true true;"); assertThat(log, hasSize(2)); assertThat(log.get(0).getLevel(), is(Level.SEVERE)); } @Test - public void correctScopeTest() throws Exception { - log = parseAndGetLog(directory.resolve("simpleScope.boppi")); + public void correctScopeTest() { + parseFile("simpleScope.boppi"); assertThat(log, empty()); } @Test - public void correctConditionalTest() throws Exception { - log = parseAndGetLog(directory.resolve("if.boppi")); + public void correctConditionalTest() { + parseFile("if.boppi"); assertThat(log, empty()); - log = parseAndGetLog(directory.resolve("while.boppi")); + parseFile("while.boppi"); assertThat(log, empty()); } - + }