improved toolchain and test structure, improved documentation
This commit is contained in:
		
							parent
							
								
									8a05789dde
								
							
						
					
					
						commit
						a42c621515
					
				| 
						 | 
					@ -4,15 +4,29 @@ import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.model.Reg;
 | 
					import pp.iloc.model.Reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This class holds properties of all AST nodes during compilation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class Annotations {
 | 
					public class Annotations {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Maps nodes to registers.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public ParseTreeProperty<Reg> registers;
 | 
						public ParseTreeProperty<Reg> registers;
 | 
				
			||||||
	public CachingSymbolTable<SimpleType> symbols;
 | 
						/**
 | 
				
			||||||
 | 
						 * Maps nodes to {@link SimpleType}s.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public ParseTreeProperty<SimpleType> types;
 | 
						public ParseTreeProperty<SimpleType> types;
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Maps nodes to {@link Variable}s.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public ParseTreeProperty<Variable<SimpleType>> variables;
 | 
						public ParseTreeProperty<Variable<SimpleType>> variables;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Creates a new annotations object with empty maps.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public Annotations() {
 | 
						public Annotations() {
 | 
				
			||||||
		registers = new ParseTreeProperty<>();
 | 
							registers = new ParseTreeProperty<>();
 | 
				
			||||||
		symbols = new CachingSymbolTable<>();
 | 
					 | 
				
			||||||
		types = new ParseTreeProperty<>();
 | 
							types = new ParseTreeProperty<>();
 | 
				
			||||||
		variables = new ParseTreeProperty<>();
 | 
							variables = new ParseTreeProperty<>();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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<BasicParser,List<LogRecord>> getParserWithLog(Path file, Logger logger) throws IOException {
 | 
					 | 
				
			||||||
		return Pair.of(getParser(CharStreams.fromFileName(file.toString()), logger), makeListLog(logger));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public static Pair<BasicParser,List<LogRecord>> 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<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;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	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())+"<br/>");
 | 
					 | 
				
			||||||
				if (annotater.registers.get(ctx) != null)
 | 
					 | 
				
			||||||
					sb.append("reg: <b>"+escape(annotater.registers.get(ctx).getName())+"</b><br/>");
 | 
					 | 
				
			||||||
				if (annotater.variables.get(ctx) != null)
 | 
					 | 
				
			||||||
					sb.append("var: "+escape(annotater.variables.get(ctx).toString())+"<br/>");
 | 
					 | 
				
			||||||
				if (annotater.types.get(ctx) != null)
 | 
					 | 
				
			||||||
					sb.append("type: "+escape(annotater.types.get(ctx).name())+"<br/>");
 | 
					 | 
				
			||||||
				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();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.EmptyStackException;
 | 
					import java.util.EmptyStackException;
 | 
				
			||||||
import java.util.function.Supplier;
 | 
					 | 
				
			||||||
import java.util.logging.Logger;
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.antlr.v4.runtime.ParserRuleContext;
 | 
					import org.antlr.v4.runtime.ParserRuleContext;
 | 
				
			||||||
| 
						 | 
					@ -9,11 +8,27 @@ import org.antlr.v4.runtime.tree.ParseTree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.s1184725.boppi.BasicParser.*;
 | 
					import pp.s1184725.boppi.BasicParser.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This class performs type checking and variable assignment on a bare parse
 | 
				
			||||||
 | 
					 * tree.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
					public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
 | 
						private CachingSymbolTable<SimpleType> symbols;
 | 
				
			||||||
	private Annotations an;
 | 
						private Annotations an;
 | 
				
			||||||
	private Logger log;
 | 
						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) {
 | 
						public BoppiBasicChecker(Logger logger, Annotations annotations) {
 | 
				
			||||||
 | 
							symbols = new CachingSymbolTable<>();
 | 
				
			||||||
		an = annotations;
 | 
							an = annotations;
 | 
				
			||||||
		log = logger;
 | 
							log = logger;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -40,7 +55,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public SimpleType visitBlock(BlockContext ctx) {
 | 
						public SimpleType visitBlock(BlockContext ctx) {
 | 
				
			||||||
		return inScope(() -> visit(ctx.expr()));
 | 
							return symbols.withScope(() -> visit(ctx.expr()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
| 
						 | 
					@ -59,7 +74,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public SimpleType visitDeclare(DeclareContext ctx) {
 | 
						public SimpleType visitDeclare(DeclareContext ctx) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Variable<SimpleType> var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
 | 
								Variable<SimpleType> var = symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
 | 
				
			||||||
			an.variables.put(ctx, var);
 | 
								an.variables.put(ctx, var);
 | 
				
			||||||
		} catch (EmptyStackException e) {
 | 
							} catch (EmptyStackException e) {
 | 
				
			||||||
			log.severe(getError(ctx, "Declaring variable outside a program."));
 | 
								log.severe(getError(ctx, "Declaring variable outside a program."));
 | 
				
			||||||
| 
						 | 
					@ -71,15 +86,15 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public SimpleType visitIf(IfContext ctx) {
 | 
						public SimpleType visitIf(IfContext ctx) {
 | 
				
			||||||
		return inScope(() -> {
 | 
							return symbols.withScope(() -> {
 | 
				
			||||||
			visit(ctx.cond);
 | 
								visit(ctx.cond);
 | 
				
			||||||
			if (ctx.onFalse != null) {
 | 
								if (ctx.onFalse != null) {
 | 
				
			||||||
				SimpleType trueType = inScope(() -> visit(ctx.onTrue));
 | 
									SimpleType trueType = symbols.withScope(() -> visit(ctx.onTrue));
 | 
				
			||||||
				SimpleType falseType = inScope(() -> visit(ctx.onFalse));
 | 
									SimpleType falseType = symbols.withScope(() -> visit(ctx.onFalse));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				return (trueType.equals(falseType)) ? trueType : SimpleType.VOID;
 | 
									return (trueType.equals(falseType)) ? trueType : SimpleType.VOID;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				inScope(() -> visit(ctx.onTrue));
 | 
									symbols.withScope(() -> visit(ctx.onTrue));
 | 
				
			||||||
				return SimpleType.VOID;
 | 
									return SimpleType.VOID;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -164,7 +179,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public SimpleType visitProgram(ProgramContext ctx) {
 | 
						public SimpleType visitProgram(ProgramContext ctx) {
 | 
				
			||||||
		inScope(() -> super.visitProgram(ctx));
 | 
							symbols.withScope(() -> super.visitProgram(ctx));
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,7 +209,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public SimpleType visitVariable(VariableContext ctx) {
 | 
						public SimpleType visitVariable(VariableContext ctx) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Variable<SimpleType> var = an.symbols.get(ctx.getText());
 | 
								Variable<SimpleType> var = symbols.get(ctx.getText());
 | 
				
			||||||
			an.variables.put(ctx, var);
 | 
								an.variables.put(ctx, var);
 | 
				
			||||||
			return var.getType();
 | 
								return var.getType();
 | 
				
			||||||
		} catch (EmptyStackException e) {
 | 
							} catch (EmptyStackException e) {
 | 
				
			||||||
| 
						 | 
					@ -208,9 +223,9 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public SimpleType visitWhile(WhileContext ctx) {
 | 
						public SimpleType visitWhile(WhileContext ctx) {
 | 
				
			||||||
		return inScope(() -> {
 | 
							return symbols.withScope(() -> {
 | 
				
			||||||
			checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond);
 | 
								checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond);
 | 
				
			||||||
			inScope(() -> visit(ctx.onTrue));
 | 
								symbols.withScope(() -> visit(ctx.onTrue));
 | 
				
			||||||
			return SimpleType.VOID;
 | 
								return SimpleType.VOID;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -232,26 +247,21 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 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) {
 | 
						private void checkConstraint(SimpleType type1, SimpleType type2, ParserRuleContext node) {
 | 
				
			||||||
		if (!type2.equals(type1))
 | 
							if (!type2.equals(type1))
 | 
				
			||||||
			log.severe(getError(node, "Could not match type %s with type %s.", type1.toString(), type2.toString()));
 | 
								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> T inScope(Supplier<T> code) {
 | 
					 | 
				
			||||||
		an.symbols.openScope();
 | 
					 | 
				
			||||||
		T result = code.get();
 | 
					 | 
				
			||||||
		an.symbols.closeScope();
 | 
					 | 
				
			||||||
		return result;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns an error message for a given parse tree node.
 | 
						 * Returns an error message for a given parse tree node.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
| 
						 | 
					@ -262,7 +272,7 @@ public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
				
			||||||
	 * @param args
 | 
						 * @param args
 | 
				
			||||||
	 *            arguments for the message, see {@link String#format}
 | 
						 *            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 line = node.getStart().getLine();
 | 
				
			||||||
		int column = node.getStart().getCharPositionInLine();
 | 
							int column = node.getStart().getCharPositionInLine();
 | 
				
			||||||
		return String.format("Line %d:%d - %s", line, column, String.format(message, args));
 | 
							return String.format("Line %d:%d - %s", line, column, String.format(message, args));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,9 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.antlr.v4.runtime.RuleContext;
 | 
					import org.antlr.v4.runtime.RuleContext;
 | 
				
			||||||
import org.antlr.v4.runtime.tree.ParseTree;
 | 
					import org.antlr.v4.runtime.tree.*;
 | 
				
			||||||
import org.antlr.v4.runtime.tree.RuleNode;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.model.*;
 | 
					import pp.iloc.model.*;
 | 
				
			||||||
import pp.s1184725.boppi.BasicParser.*;
 | 
					import pp.s1184725.boppi.BasicParser.*;
 | 
				
			||||||
| 
						 | 
					@ -16,8 +14,12 @@ public class BoppiBasicGenerator extends BasicBaseVisitor<Void> {
 | 
				
			||||||
	private int regNum;
 | 
						private int regNum;
 | 
				
			||||||
	private final Reg zero = new Reg("zero");
 | 
						private final Reg zero = new Reg("zero");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static Map<Integer, OpCode> ops = new HashMap<Integer, OpCode>() {
 | 
						/**
 | 
				
			||||||
 | 
						 * Maps operator tokens to ILOC opcodes.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private static Map<Integer, OpCode> ops = new HashMap<Integer, OpCode>() {
 | 
				
			||||||
		private static final long serialVersionUID = 8979722313842633807L;
 | 
							private static final long serialVersionUID = 8979722313842633807L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			put(BasicLexer.AND, OpCode.and);
 | 
								put(BasicLexer.AND, OpCode.and);
 | 
				
			||||||
			put(BasicLexer.DIVIDE, OpCode.div);
 | 
								put(BasicLexer.DIVIDE, OpCode.div);
 | 
				
			||||||
| 
						 | 
					@ -34,6 +36,12 @@ public class BoppiBasicGenerator extends BasicBaseVisitor<Void> {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Creates a new generator with the given variable annotations.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param annotations
 | 
				
			||||||
 | 
						 *            the annotation object to use during generation
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public BoppiBasicGenerator(Annotations annotations) {
 | 
						public BoppiBasicGenerator(Annotations annotations) {
 | 
				
			||||||
		an = annotations;
 | 
							an = annotations;
 | 
				
			||||||
		prog = new Program();
 | 
							prog = new Program();
 | 
				
			||||||
| 
						 | 
					@ -42,6 +50,13 @@ public class BoppiBasicGenerator extends BasicBaseVisitor<Void> {
 | 
				
			||||||
		emit(OpCode.loadI, new Num(0), zero);
 | 
							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) {
 | 
						private Reg getReg(ParseTree parseTree) {
 | 
				
			||||||
		Reg reg = an.registers.get(parseTree);
 | 
							Reg reg = an.registers.get(parseTree);
 | 
				
			||||||
		if (reg == null) {
 | 
							if (reg == null) {
 | 
				
			||||||
| 
						 | 
					@ -51,6 +66,16 @@ public class BoppiBasicGenerator extends BasicBaseVisitor<Void> {
 | 
				
			||||||
		return reg;
 | 
							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) {
 | 
						private Reg copyReg(ParseTree source, ParseTree destination) {
 | 
				
			||||||
		Reg reg = an.registers.get(source);
 | 
							Reg reg = an.registers.get(source);
 | 
				
			||||||
		an.registers.put(destination, reg);
 | 
							an.registers.put(destination, reg);
 | 
				
			||||||
| 
						 | 
					@ -233,8 +258,7 @@ public class BoppiBasicGenerator extends BasicBaseVisitor<Void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit(toTrue, OpCode.nop);
 | 
								emit(toTrue, OpCode.nop);
 | 
				
			||||||
			visit(ctx.onTrue);
 | 
								visit(ctx.onTrue);
 | 
				
			||||||
		}
 | 
							} else {
 | 
				
			||||||
		else {
 | 
					 | 
				
			||||||
			emit(OpCode.cbr, getReg(ctx.cond), toTrue, toFalse);
 | 
								emit(OpCode.cbr, getReg(ctx.cond), toTrue, toFalse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit(toTrue, OpCode.nop);
 | 
								emit(toTrue, OpCode.nop);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,18 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.*;
 | 
					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 <T>
 | 
				
			||||||
 | 
					 *            the typing class
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class CachingSymbolTable<T> {
 | 
					public class CachingSymbolTable<T> {
 | 
				
			||||||
	protected Stack<Map<String, Variable<T>>> symbolMapStack;
 | 
						protected Stack<Map<String, Variable<T>>> symbolMapStack;
 | 
				
			||||||
	protected Stack<Integer> offsets;
 | 
						protected Stack<Integer> offsets;
 | 
				
			||||||
| 
						 | 
					@ -28,12 +39,15 @@ public class CachingSymbolTable<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Closes a lexical scope, removing all definitions within this scope.
 | 
						 * 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 {
 | 
						public void closeScope() throws EmptyStackException {
 | 
				
			||||||
		Map<String, Variable<T>> deletions = symbolMapStack.pop();
 | 
							Map<String, Variable<T>> deletions = symbolMapStack.pop();
 | 
				
			||||||
		for (String identifier : deletions.keySet()) {
 | 
							for (String identifier : deletions.keySet()) {
 | 
				
			||||||
			Optional<Variable<T>> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier)).map((map) -> map.get(identifier)).reduce((__, snd) -> snd);
 | 
								Optional<Variable<T>> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier))
 | 
				
			||||||
 | 
										.map((map) -> map.get(identifier)).reduce((__, snd) -> snd);
 | 
				
			||||||
			maybeType.ifPresent((var) -> symbolCache.replace(identifier, var));
 | 
								maybeType.ifPresent((var) -> symbolCache.replace(identifier, var));
 | 
				
			||||||
			if (!maybeType.isPresent())
 | 
								if (!maybeType.isPresent())
 | 
				
			||||||
				symbolCache.remove(identifier);
 | 
									symbolCache.remove(identifier);
 | 
				
			||||||
| 
						 | 
					@ -42,12 +56,38 @@ public class CachingSymbolTable<T> {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Associates an identifier with a certain type in the current lexical scope.
 | 
						 * Opens a scope, executes the given code and closes the scope. This is a
 | 
				
			||||||
	 * @param id the name of the identifier
 | 
						 * helper method to make sure {@link #openScope()} and {@link #closeScope()}
 | 
				
			||||||
	 * @param type the type of the identifier
 | 
						 * calls are balanced. {@code code} must take no arguments and must return a
 | 
				
			||||||
 | 
						 * value.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @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 <U> U withScope(Supplier<U> 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
 | 
						 * @return the type of the identifier
 | 
				
			||||||
	 * @throws Exception if the identifier is declared in the current scope already
 | 
						 * @throws Exception
 | 
				
			||||||
	 * @throws EmptyStackException if no scope is open
 | 
						 *             if the identifier is declared in the current scope already
 | 
				
			||||||
 | 
						 * @throws EmptyStackException
 | 
				
			||||||
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public Variable<T> put(String id, T type) throws Exception, EmptyStackException {
 | 
						public Variable<T> put(String id, T type) throws Exception, EmptyStackException {
 | 
				
			||||||
		if (symbolMapStack.peek().containsKey(id))
 | 
							if (symbolMapStack.peek().containsKey(id))
 | 
				
			||||||
| 
						 | 
					@ -63,9 +103,12 @@ public class CachingSymbolTable<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns whether the given identifier is defined.
 | 
						 * 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
 | 
						 * @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 {
 | 
						public boolean has(String id) throws EmptyStackException {
 | 
				
			||||||
		if (symbolMapStack.isEmpty())
 | 
							if (symbolMapStack.isEmpty())
 | 
				
			||||||
| 
						 | 
					@ -76,10 +119,14 @@ public class CachingSymbolTable<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Retrieves the type of an identifier, if any.
 | 
						 * 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
 | 
						 * @return the type of the identifier
 | 
				
			||||||
	 * @throws Exception if the identifier is not defined
 | 
						 * @throws Exception
 | 
				
			||||||
	 * @throws EmptyStackException if no scope is open
 | 
						 *             if the identifier is not defined
 | 
				
			||||||
 | 
						 * @throws EmptyStackException
 | 
				
			||||||
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public Variable<T> get(String id) throws Exception, EmptyStackException {
 | 
						public Variable<T> get(String id) throws Exception, EmptyStackException {
 | 
				
			||||||
		if (!this.has(id))
 | 
							if (!this.has(id))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,28 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static pp.s1184725.boppi.BasicLexer.*;
 | 
					import static pp.s1184725.boppi.BasicLexer.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.eval.Machine;
 | 
					import pp.iloc.eval.Machine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This class provides 4 basic data types:
 | 
				
			||||||
 | 
					 * <ul>
 | 
				
			||||||
 | 
					 * <li>character (sized {@link Machine#DEFAULT_CHAR_SIZE})</li>
 | 
				
			||||||
 | 
					 * <li>integer (sized {@link Machine#INT_SIZE})</li>
 | 
				
			||||||
 | 
					 * <li>boolean (sized {@link Machine#INT_SIZE})</li>
 | 
				
			||||||
 | 
					 * <li>void (empty/unit type)</li>
 | 
				
			||||||
 | 
					 * </ul>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public enum SimpleType {
 | 
					public enum SimpleType {
 | 
				
			||||||
	INT(Machine.INT_SIZE), CHAR(Machine.DEFAULT_CHAR_SIZE), BOOL(Machine.INT_SIZE), VOID(0);
 | 
						INT(Machine.INT_SIZE), CHAR(Machine.DEFAULT_CHAR_SIZE), BOOL(Machine.INT_SIZE), VOID(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final int size;
 | 
						private final int size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Gets the size of this data type.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return the size (in bytes) of this type
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public int getSize() {
 | 
						public int getSize() {
 | 
				
			||||||
		return size;
 | 
							return size;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -16,6 +31,14 @@ public enum SimpleType {
 | 
				
			||||||
		size = 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) {
 | 
						public static SimpleType fromToken(int token) {
 | 
				
			||||||
		switch (token) {
 | 
							switch (token) {
 | 
				
			||||||
		case INTTYPE:
 | 
							case INTTYPE:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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<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}.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param stream
 | 
				
			||||||
 | 
						 *            the charstream to compile
 | 
				
			||||||
 | 
						 * @return a pair of the ILOC program and the logger
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static Pair<Program, List<LogRecord>> compile(CharStream stream) {
 | 
				
			||||||
 | 
							Logger logger = Logger.getAnonymousLogger();
 | 
				
			||||||
 | 
							List<LogRecord> 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()) + "<br/>");
 | 
				
			||||||
 | 
									if (annotater.registers.get(ctx) != null)
 | 
				
			||||||
 | 
										sb.append("reg: <b>" + escape(annotater.registers.get(ctx).getName()) + "</b><br/>");
 | 
				
			||||||
 | 
									if (annotater.variables.get(ctx) != null)
 | 
				
			||||||
 | 
										sb.append("var: " + escape(annotater.variables.get(ctx).toString()) + "<br/>");
 | 
				
			||||||
 | 
									if (annotater.types.get(ctx) != null)
 | 
				
			||||||
 | 
										sb.append("type: " + escape(annotater.types.get(ctx).name()) + "<br/>");
 | 
				
			||||||
 | 
									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();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,49 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					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 <T>
 | 
				
			||||||
 | 
					 *            the typing class
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class Variable<T> {
 | 
					public class Variable<T> {
 | 
				
			||||||
	private final T type;
 | 
						private final T type;
 | 
				
			||||||
	private final int offset;
 | 
						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) {
 | 
						public Variable(T type, int offset) {
 | 
				
			||||||
		this.type = type;
 | 
							this.type = type;
 | 
				
			||||||
		this.offset = offset;
 | 
							this.offset = offset;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Gets the type of this variable instance.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return the type
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public T getType() {
 | 
						public T getType() {
 | 
				
			||||||
		return type;
 | 
							return type;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Gets the memory offset of this variable instance.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return the offset
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public int getOffset() {
 | 
						public int getOffset() {
 | 
				
			||||||
		return offset;
 | 
							return offset;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		return type.toString()+" @ "+offset;
 | 
							return String.format("%s:%X@%d", type.toString(), hashCode(), offset);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,123 +3,116 @@ package pp.s1184725.boppi.test;
 | 
				
			||||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
					import static org.hamcrest.MatcherAssert.assertThat;
 | 
				
			||||||
import static org.hamcrest.Matchers.*;
 | 
					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.List;
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.apache.commons.lang3.tuple.Pair;
 | 
					import org.antlr.v4.runtime.CharStream;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.s1184725.boppi.*;
 | 
					import pp.s1184725.boppi.ToolChain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class CheckerTest {
 | 
					public class CheckerTest {
 | 
				
			||||||
	static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/");
 | 
					 | 
				
			||||||
	private List<LogRecord> log;
 | 
						private List<LogRecord> log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private List<LogRecord> checkAndGetLog(String code) {
 | 
						private void checkString(String code) {
 | 
				
			||||||
		Logger logger = Logger.getAnonymousLogger();
 | 
							check(ToolChain.getCharStream(code));
 | 
				
			||||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, logger);
 | 
					 | 
				
			||||||
		new BoppiBasicChecker(logger, new Annotations()).visit(pair.getLeft().program());
 | 
					 | 
				
			||||||
		return pair.getRight();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private List<LogRecord> checkAndGetLog(Path code) throws IOException {
 | 
						private void checkFile(String file) {
 | 
				
			||||||
		Logger logger = Logger.getAnonymousLogger();
 | 
							check(ToolChain.getCharStream(AllTests.directory.resolve(file)));
 | 
				
			||||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, logger);
 | 
					 | 
				
			||||||
		new BoppiBasicChecker(logger, new Annotations()).visit(pair.getLeft().program());
 | 
					 | 
				
			||||||
		return pair.getRight();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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
 | 
						@Test
 | 
				
			||||||
	public void correctExpressionTest() throws Exception {
 | 
						public void correctExpressionTest() {
 | 
				
			||||||
		log = checkAndGetLog(directory.resolve("simpleExpression.boppi"));
 | 
							checkFile("simpleExpression.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void wrongExpressionTest() {
 | 
						public void wrongExpressionTest() {
 | 
				
			||||||
		log = checkAndGetLog("+true");
 | 
							checkString("+true");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = checkAndGetLog("5 || true");
 | 
							checkString("5 || true");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = checkAndGetLog("6 + 'c'");
 | 
							checkString("6 + 'c'");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctVariableTest() throws Exception {
 | 
						public void correctVariableTest() {
 | 
				
			||||||
		log = checkAndGetLog(directory.resolve("simpleVariable.boppi"));
 | 
							checkFile("simpleVariable.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void wrongVariableTest() {
 | 
						public void wrongVariableTest() {
 | 
				
			||||||
		log = checkAndGetLog("var bool name; name := 5");
 | 
							checkString("var bool name; name := 5");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = checkAndGetLog("undefinedName");
 | 
							checkString("undefinedName");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = checkAndGetLog("var bool endsWithDeclaration;");
 | 
							checkString("var bool endsWithDeclaration;");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctScopeTest() throws Exception {
 | 
						public void correctScopeTest() {
 | 
				
			||||||
		log = checkAndGetLog(directory.resolve("simpleScope.boppi"));
 | 
							checkFile("simpleScope.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void wrongScopeTest() {
 | 
						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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctConditionalTest() throws Exception {
 | 
						public void correctConditionalTest() {
 | 
				
			||||||
		log = checkAndGetLog(directory.resolve("if.boppi"));
 | 
							checkFile("if.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = checkAndGetLog(directory.resolve("while.boppi"));
 | 
							checkFile("while.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,111 +1,83 @@
 | 
				
			||||||
package pp.s1184725.boppi.test;
 | 
					package pp.s1184725.boppi.test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
					import static org.hamcrest.MatcherAssert.assertThat;
 | 
				
			||||||
import static org.hamcrest.Matchers.containsString;
 | 
					import static org.hamcrest.Matchers.*;
 | 
				
			||||||
import static org.hamcrest.Matchers.empty;
 | 
					 | 
				
			||||||
import static org.hamcrest.Matchers.hasSize;
 | 
					 | 
				
			||||||
import static org.hamcrest.Matchers.is;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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.List;
 | 
				
			||||||
import java.util.logging.LogRecord;
 | 
					import java.util.logging.*;
 | 
				
			||||||
import java.util.logging.Logger;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.antlr.v4.runtime.ParserRuleContext;
 | 
					import org.antlr.v4.runtime.CharStream;
 | 
				
			||||||
import org.apache.commons.lang3.tuple.Pair;
 | 
					import org.antlr.v4.runtime.tree.ParseTree;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.Simulator;
 | 
					import pp.iloc.model.Program;
 | 
				
			||||||
import pp.s1184725.boppi.Annotations;
 | 
					import pp.s1184725.boppi.*;
 | 
				
			||||||
import pp.s1184725.boppi.BasicParser;
 | 
					 | 
				
			||||||
import pp.s1184725.boppi.BasicParserHelper;
 | 
					 | 
				
			||||||
import pp.s1184725.boppi.BoppiBasicChecker;
 | 
					 | 
				
			||||||
import pp.s1184725.boppi.BoppiBasicGenerator;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class GeneratorTest {
 | 
					public class GeneratorTest {
 | 
				
			||||||
	static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/");
 | 
						private List<LogRecord> log;
 | 
				
			||||||
	private Pair<String, List<LogRecord>> result;
 | 
						private String[] out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private Pair<String, List<LogRecord>> 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();
 | 
							Logger logger = Logger.getAnonymousLogger();
 | 
				
			||||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, logger);
 | 
							log = ToolChain.makeListLog(logger);
 | 
				
			||||||
		return derp(logger, pair, input);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private Pair<String, List<LogRecord>> generateAndGetLog(Path code, String input) throws IOException {
 | 
							ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
 | 
				
			||||||
		Logger logger = Logger.getAnonymousLogger();
 | 
							Annotations annotations = ToolChain.getAnnotations(ast, logger);
 | 
				
			||||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, logger);
 | 
							Program program = ToolChain.getILOC(ast, annotations);
 | 
				
			||||||
		return derp(logger, pair, input);
 | 
							out = ToolChain.execute(program, logger, input).split("\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private Pair<String, List<LogRecord>> derp(Logger logger, Pair<BasicParser, List<LogRecord>> 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
 | 
						@Test
 | 
				
			||||||
	public void correctExpressionTest() throws IOException {
 | 
						public void correctExpressionTest() {
 | 
				
			||||||
		result = generateAndGetLog(directory.resolve("simpleExpression.boppi"), "");
 | 
							compileAndRunFile("simpleExpression.boppi");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void wrongExpressionTest() {
 | 
						public void wrongExpressionTest() {
 | 
				
			||||||
		result = generateAndGetLog("1/0", "");
 | 
							compileAndRunString("1/0");
 | 
				
			||||||
		assertThat(result.getRight(), hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(result.getRight().get(0).getMessage(), containsString("zero"));
 | 
							assertThat(log.get(0).getMessage(), containsString("zero"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctVariableTest() throws IOException {
 | 
						public void correctVariableTest() {
 | 
				
			||||||
		result = generateAndGetLog(directory.resolve("simpleVariable.boppi"), "");
 | 
							compileAndRunFile("simpleVariable.boppi");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void basicPrograms() throws IOException {
 | 
						public void basicPrograms() {
 | 
				
			||||||
		result = generateAndGetLog("print(5*3)", "");
 | 
							compileAndRunString("print(5*3)");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
		assertThat(result.getLeft(), is("15\n"));
 | 
							assertThat(out, is(arrayContaining("15")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		result = generateAndGetLog("print('T', 'e', 's', 't', '!')", "");
 | 
							compileAndRunString("print({var int x; x := 8; print(x)})");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
		assertThat(result.getLeft(), is("T\ne\ns\nt\n!\n"));
 | 
							assertThat(out, is(arrayContaining("8", "8")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		result = generateAndGetLog("var int x; var int y; x := 3*(y := 4); print(x,y)", "");
 | 
							compileAndRunString("print('T', 'e', 's', 't', '!')");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
		assertThat(result.getLeft(), is("12\n4\n"));
 | 
							assertThat(out, is(arrayContaining("T", "e", "s", "t", "!")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		result = generateAndGetLog(directory.resolve("basicProgram1.boppi"), "1\nT\n");
 | 
							compileAndRunString("var int x; var int y; x := 3*(y := 4); print(x,y)");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
		assertThat(result.getLeft(), is("T\nT\n"));
 | 
							assertThat(out, is(arrayContaining("12", "4")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		result = generateAndGetLog(directory.resolve("fibonacciIterative.boppi"), "6\n");
 | 
							compileAndRunFile("basicProgram1.boppi", "1", "T");
 | 
				
			||||||
		assertThat(result.getRight(), empty());
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
		assertThat(result.getLeft(), is("13\n"));
 | 
							assertThat(out, is(arrayContaining("T", "T")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							compileAndRunFile("fibonacciIterative.boppi", "6");
 | 
				
			||||||
 | 
							assertThat(log, is(empty()));
 | 
				
			||||||
 | 
							assertThat(out, is(arrayContaining("13")));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,99 +1,97 @@
 | 
				
			||||||
package pp.s1184725.boppi.test;
 | 
					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.List;
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.apache.commons.lang3.tuple.Pair;
 | 
					import org.antlr.v4.runtime.CharStream;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
					import static org.hamcrest.MatcherAssert.assertThat;
 | 
				
			||||||
import static org.hamcrest.Matchers.*;
 | 
					import static org.hamcrest.Matchers.*;
 | 
				
			||||||
import pp.s1184725.boppi.*;
 | 
					import pp.s1184725.boppi.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ParserTest {
 | 
					public class ParserTest {
 | 
				
			||||||
	static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/");
 | 
					 | 
				
			||||||
	private List<LogRecord> log;
 | 
						private List<LogRecord> log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private List<LogRecord> parseAndGetLog(String code) {
 | 
						private void parseString(String code) {
 | 
				
			||||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger());
 | 
							parse(ToolChain.getCharStream(code));
 | 
				
			||||||
		pair.getLeft().program();
 | 
					 | 
				
			||||||
		return pair.getRight();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static private List<LogRecord> parseAndGetLog(Path code) throws IOException {
 | 
						private void parseFile(String file) {
 | 
				
			||||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger());
 | 
							parse(ToolChain.getCharStream(AllTests.directory.resolve(file)));
 | 
				
			||||||
		pair.getLeft().program();
 | 
						}
 | 
				
			||||||
		return pair.getRight();
 | 
					
 | 
				
			||||||
 | 
						private void parse(CharStream stream) {
 | 
				
			||||||
 | 
							Logger logger = Logger.getAnonymousLogger();
 | 
				
			||||||
 | 
							log = ToolChain.makeListLog(logger);
 | 
				
			||||||
 | 
							ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctExpressionTest() throws Exception {
 | 
						public void correctExpressionTest() {
 | 
				
			||||||
		log = parseAndGetLog(directory.resolve("simpleExpression.boppi"));
 | 
							parseFile("simpleExpression.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, empty());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void wrongExpressionTest() {
 | 
						public void wrongExpressionTest() {
 | 
				
			||||||
		log = parseAndGetLog("");
 | 
							parseString("");
 | 
				
			||||||
		assertThat(log, not(empty()));
 | 
							assertThat(log, not(empty()));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog("~");
 | 
							parseString("~");
 | 
				
			||||||
		assertThat(log, not(empty()));
 | 
							assertThat(log, not(empty()));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog("0A");
 | 
							parseString("0A");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog("do");
 | 
							parseString("do");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog("true true");
 | 
							parseString("true true");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctVariableTest() throws Exception {
 | 
						public void correctVariableTest() {
 | 
				
			||||||
		log = parseAndGetLog(directory.resolve("simpleVariable.boppi"));
 | 
							parseFile("simpleVariable.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, empty());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void wrongVariableTest() {
 | 
						public void wrongVariableTest() {
 | 
				
			||||||
		log = parseAndGetLog("var");
 | 
							parseString("var");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog("var bool 5");
 | 
							parseString("var bool 5");
 | 
				
			||||||
		assertThat(log, hasSize(1));
 | 
							assertThat(log, hasSize(1));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog("var 'c' varname");
 | 
							parseString("var 'c' varname");
 | 
				
			||||||
		assertThat(log, not(empty()));
 | 
							assertThat(log, not(empty()));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							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, hasSize(2));
 | 
				
			||||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
							assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctScopeTest() throws Exception {
 | 
						public void correctScopeTest() {
 | 
				
			||||||
		log = parseAndGetLog(directory.resolve("simpleScope.boppi"));
 | 
							parseFile("simpleScope.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, empty());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctConditionalTest() throws Exception {
 | 
						public void correctConditionalTest() {
 | 
				
			||||||
		log = parseAndGetLog(directory.resolve("if.boppi"));
 | 
							parseFile("if.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, empty());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log = parseAndGetLog(directory.resolve("while.boppi"));
 | 
							parseFile("while.boppi");
 | 
				
			||||||
		assertThat(log, empty());
 | 
							assertThat(log, empty());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue