basic checker and checker test
This commit is contained in:
		
							parent
							
								
									c4f5c77fc4
								
							
						
					
					
						commit
						21dca82f4a
					
				| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.model.Reg;
 | 
			
		||||
 | 
			
		||||
public class Annotations {
 | 
			
		||||
	public ParseTreeProperty<Reg> registers;
 | 
			
		||||
	public CachingSymbolTable<SimpleType> symbols;
 | 
			
		||||
	public ParseTreeProperty<SimpleType> types;
 | 
			
		||||
	public ParseTreeProperty<Variable<SimpleType>> variables;
 | 
			
		||||
 | 
			
		||||
	public Annotations() {
 | 
			
		||||
		registers = new ParseTreeProperty<>();
 | 
			
		||||
		symbols = new CachingSymbolTable<>();
 | 
			
		||||
		types = new ParseTreeProperty<>();
 | 
			
		||||
		variables = new ParseTreeProperty<>();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,270 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.EmptyStackException;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import org.antlr.v4.runtime.ParserRuleContext;
 | 
			
		||||
import org.antlr.v4.runtime.tree.ParseTree;
 | 
			
		||||
 | 
			
		||||
import pp.s1184725.boppi.BasicParser.*;
 | 
			
		||||
 | 
			
		||||
public class BoppiBasicChecker extends BasicBaseVisitor<SimpleType> {
 | 
			
		||||
	private Annotations an;
 | 
			
		||||
	private Logger log;
 | 
			
		||||
 | 
			
		||||
	public BoppiBasicChecker(Logger logger, Annotations annotations) {
 | 
			
		||||
		an = annotations;
 | 
			
		||||
		log = logger;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visit(ParseTree tree) {
 | 
			
		||||
		SimpleType type = super.visit(tree);
 | 
			
		||||
		an.types.put(tree, type);
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitAssign(AssignContext ctx) {
 | 
			
		||||
		SimpleType expr = visit(ctx.singleExpr());
 | 
			
		||||
		SimpleType var = visit(ctx.variable());
 | 
			
		||||
		checkConstraint(expr, var, ctx);
 | 
			
		||||
		return expr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitBool(BoolContext ctx) {
 | 
			
		||||
		return SimpleType.BOOL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitBlock(BlockContext ctx) {
 | 
			
		||||
		return inScope(() -> visit(ctx.expr()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitChar(CharContext ctx) {
 | 
			
		||||
		return SimpleType.CHAR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitExpr(ExprContext ctx) {
 | 
			
		||||
		if (ctx.singleExpr(ctx.singleExpr().size() - 1) instanceof DeclareContext)
 | 
			
		||||
			log.severe(getError(ctx, "Compound expression ends with declaration."));
 | 
			
		||||
 | 
			
		||||
		return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitDeclare(DeclareContext ctx) {
 | 
			
		||||
		try {
 | 
			
		||||
			Variable<SimpleType> var = an.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."));
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		}
 | 
			
		||||
		return SimpleType.VOID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitIf(IfContext ctx) {
 | 
			
		||||
		return inScope(() -> {
 | 
			
		||||
			visit(ctx.cond);
 | 
			
		||||
			if (ctx.onFalse != null) {
 | 
			
		||||
				SimpleType trueType = inScope(() -> visit(ctx.onTrue));
 | 
			
		||||
				SimpleType falseType = inScope(() -> visit(ctx.onFalse));
 | 
			
		||||
 | 
			
		||||
				return (trueType.equals(falseType)) ? trueType : SimpleType.VOID;
 | 
			
		||||
			} else {
 | 
			
		||||
				inScope(() -> visit(ctx.onTrue));
 | 
			
		||||
				return SimpleType.VOID;
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitInfix1(Infix1Context ctx) {
 | 
			
		||||
		checkConstraint(visit(ctx.lhs), SimpleType.INT, ctx.lhs);
 | 
			
		||||
		checkConstraint(visit(ctx.rhs), SimpleType.INT, ctx.rhs);
 | 
			
		||||
 | 
			
		||||
		return SimpleType.INT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitInfix2(Infix2Context ctx) {
 | 
			
		||||
		checkConstraint(visit(ctx.lhs), SimpleType.INT, ctx.lhs);
 | 
			
		||||
		checkConstraint(visit(ctx.rhs), SimpleType.INT, ctx.rhs);
 | 
			
		||||
 | 
			
		||||
		return SimpleType.INT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitInfix3(Infix3Context ctx) {
 | 
			
		||||
		SimpleType lht = visit(ctx.lhs);
 | 
			
		||||
		SimpleType rht = visit(ctx.rhs);
 | 
			
		||||
		checkConstraint(lht, rht, ctx);
 | 
			
		||||
 | 
			
		||||
		switch (ctx.op.getType()) {
 | 
			
		||||
		case BasicLexer.LT:
 | 
			
		||||
		case BasicLexer.LEQ:
 | 
			
		||||
		case BasicLexer.GTE:
 | 
			
		||||
		case BasicLexer.GT:
 | 
			
		||||
			checkConstraint(lht, SimpleType.INT, ctx);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return SimpleType.BOOL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitInfix4(Infix4Context ctx) {
 | 
			
		||||
		checkConstraint(visit(ctx.lhs), SimpleType.BOOL, ctx.lhs);
 | 
			
		||||
		checkConstraint(visit(ctx.rhs), SimpleType.BOOL, ctx.rhs);
 | 
			
		||||
 | 
			
		||||
		return SimpleType.BOOL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitInfix5(Infix5Context ctx) {
 | 
			
		||||
		checkConstraint(visit(ctx.lhs), SimpleType.BOOL, ctx.lhs);
 | 
			
		||||
		checkConstraint(visit(ctx.rhs), SimpleType.BOOL, ctx.rhs);
 | 
			
		||||
 | 
			
		||||
		return SimpleType.BOOL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitNumber(NumberContext ctx) {
 | 
			
		||||
		return SimpleType.INT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitParens(ParensContext ctx) {
 | 
			
		||||
		return visit(ctx.expr());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitPrefix1(Prefix1Context ctx) {
 | 
			
		||||
		SimpleType type = visit(ctx.singleExpr());
 | 
			
		||||
 | 
			
		||||
		switch (ctx.op.getType()) {
 | 
			
		||||
		case BasicLexer.NOT:
 | 
			
		||||
			checkConstraint(type, SimpleType.BOOL, ctx.singleExpr());
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case BasicLexer.PLUS:
 | 
			
		||||
		case BasicLexer.MINUS:
 | 
			
		||||
			checkConstraint(type, SimpleType.INT, ctx.singleExpr());
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitProgram(ProgramContext ctx) {
 | 
			
		||||
		inScope(() -> super.visitProgram(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitRead(ReadContext ctx) {
 | 
			
		||||
		if (ctx.variable().size() == 1) {
 | 
			
		||||
			return visit(ctx.variable(0));
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.variable().forEach(this::visit);
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitType(TypeContext ctx) {
 | 
			
		||||
		if (ctx.variable() != null)
 | 
			
		||||
			return visit(ctx.variable());
 | 
			
		||||
		else
 | 
			
		||||
			return SimpleType.fromToken(ctx.staticType.getType());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitVar(VarContext ctx) {
 | 
			
		||||
		return visit(ctx.variable());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitVariable(VariableContext ctx) {
 | 
			
		||||
		try {
 | 
			
		||||
			Variable<SimpleType> var = an.symbols.get(ctx.getText());
 | 
			
		||||
			an.variables.put(ctx, var);
 | 
			
		||||
			return var.getType();
 | 
			
		||||
		} catch (EmptyStackException e) {
 | 
			
		||||
			log.severe(getError(ctx, "Using variable outside a program."));
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return SimpleType.VOID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitWhile(WhileContext ctx) {
 | 
			
		||||
		return inScope(() -> {
 | 
			
		||||
			checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond);
 | 
			
		||||
			inScope(() -> visit(ctx.onTrue));
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public SimpleType visitWrite(WriteContext ctx) {
 | 
			
		||||
		if (ctx.expr().size() == 1) {
 | 
			
		||||
			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) -> {
 | 
			
		||||
				if (SimpleType.VOID.equals(type))
 | 
			
		||||
					log.severe(getError(ctx, "Cannot print argument of type %s.", type));
 | 
			
		||||
			});
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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> 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.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param ctx
 | 
			
		||||
	 *            the parse tree node at which the error occurred
 | 
			
		||||
	 * @param message
 | 
			
		||||
	 *            the error message
 | 
			
		||||
	 * @param args
 | 
			
		||||
	 *            arguments for the message, see {@link String#format}
 | 
			
		||||
	 */
 | 
			
		||||
	protected 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));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.eval.Machine;
 | 
			
		||||
 | 
			
		||||
public class CachingSymbolTable<T> {
 | 
			
		||||
	protected Stack<Map<String,Variable<T>>> symbolMapStack;
 | 
			
		||||
	protected Stack<Integer> offsets;
 | 
			
		||||
	protected Map<String,Variable<T>> symbolCache;
 | 
			
		||||
	protected int offset;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates an empty symbol table with no open scope.
 | 
			
		||||
	 */
 | 
			
		||||
	public CachingSymbolTable() {
 | 
			
		||||
		symbolMapStack = new Stack<>();
 | 
			
		||||
		symbolCache = new HashMap<>();
 | 
			
		||||
		offsets = new Stack<>();
 | 
			
		||||
		offset = 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a lexical scope.
 | 
			
		||||
	 */
 | 
			
		||||
	public void openScope() {
 | 
			
		||||
		symbolMapStack.push(new HashMap<>());
 | 
			
		||||
		offsets.push(offset);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Closes a lexical scope, removing all definitions within this scope.
 | 
			
		||||
	 * @throws EmptyStackException if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public void closeScope() throws EmptyStackException {
 | 
			
		||||
		Map<String,Variable<T>> deletions = symbolMapStack.pop();
 | 
			
		||||
		for (String identifier : deletions.keySet()) {
 | 
			
		||||
			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));
 | 
			
		||||
			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
 | 
			
		||||
	 * @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
 | 
			
		||||
	 */
 | 
			
		||||
	public Variable<T> 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<T> var = new Variable<>(type, offset);
 | 
			
		||||
		offset += Machine.INT_SIZE;
 | 
			
		||||
		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
 | 
			
		||||
	 * @return true if the identifier has a defined type
 | 
			
		||||
	 * @throws EmptyStackException if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean has(String id) throws EmptyStackException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new EmptyStackException();
 | 
			
		||||
 | 
			
		||||
		return symbolCache.containsKey(id);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Retrieves the type of an identifier, if any.
 | 
			
		||||
	 * @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
 | 
			
		||||
	 */
 | 
			
		||||
	public Variable<T> get(String id) throws Exception, EmptyStackException {
 | 
			
		||||
		if (!this.has(id))
 | 
			
		||||
			throw new Exception(String.format("Identifier '%s' is undefined.", id));
 | 
			
		||||
 | 
			
		||||
		return symbolCache.get(id);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
public enum SimpleType {
 | 
			
		||||
	INT, CHAR, BOOL, VOID;
 | 
			
		||||
	
 | 
			
		||||
	public static SimpleType fromToken(int token) {
 | 
			
		||||
		switch (token) {
 | 
			
		||||
		case BasicLexer.INTTYPE:
 | 
			
		||||
			return SimpleType.INT;
 | 
			
		||||
 | 
			
		||||
		case BasicLexer.CHARTYPE:
 | 
			
		||||
			return SimpleType.CHAR;
 | 
			
		||||
 | 
			
		||||
		case BasicLexer.BOOLTYPE:
 | 
			
		||||
			return SimpleType.BOOL;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
public class Variable<T> {
 | 
			
		||||
	private final T type;
 | 
			
		||||
	private final int offset;
 | 
			
		||||
	
 | 
			
		||||
	public Variable(T type, int offset) {
 | 
			
		||||
		this.type = type;
 | 
			
		||||
		this.offset = offset;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public T getType() {
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public int getOffset() {
 | 
			
		||||
		return offset;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return type.toString()+" @ "+offset;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,135 @@
 | 
			
		|||
package pp.s1184725.boppi.test;
 | 
			
		||||
 | 
			
		||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
			
		||||
import static org.hamcrest.Matchers.empty;
 | 
			
		||||
import static org.hamcrest.Matchers.hasSize;
 | 
			
		||||
import static org.hamcrest.Matchers.is;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.nio.file.Paths;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
import java.util.logging.LogRecord;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.tuple.Pair;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import pp.s1184725.boppi.Annotations;
 | 
			
		||||
import pp.s1184725.boppi.BasicParser;
 | 
			
		||||
import pp.s1184725.boppi.BasicParserHelper;
 | 
			
		||||
import pp.s1184725.boppi.BoppiBasicChecker;
 | 
			
		||||
 | 
			
		||||
public class CheckerTest {
 | 
			
		||||
	static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/");
 | 
			
		||||
	private List<LogRecord> log;
 | 
			
		||||
 | 
			
		||||
	static private List<LogRecord> checkAndGetLog(String code) {
 | 
			
		||||
		Logger logger = Logger.getAnonymousLogger();
 | 
			
		||||
		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 {
 | 
			
		||||
		Logger logger = Logger.getAnonymousLogger();
 | 
			
		||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, logger);
 | 
			
		||||
		new BoppiBasicChecker(logger, new Annotations()).visit(pair.getLeft().program());
 | 
			
		||||
		return pair.getRight();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected void debug() {
 | 
			
		||||
		log.forEach(entry -> System.out.println(entry.getMessage()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void correctExpressionTest() throws Exception {
 | 
			
		||||
		log = checkAndGetLog(directory.resolve("simpleExpression.boppi"));
 | 
			
		||||
		assertThat(log, empty());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrongExpressionTest() {
 | 
			
		||||
		log = checkAndGetLog("+true");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("5 || true");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("6 + 'c'");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("4 + print(5, 6)");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("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());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrongVariableTest() {
 | 
			
		||||
		log = checkAndGetLog("var bool name; name := 5");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("undefinedName");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("var undefinedType name; 1");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("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' ");
 | 
			
		||||
		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());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrongScopeTest() {
 | 
			
		||||
		log = checkAndGetLog("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");
 | 
			
		||||
		assertThat(log, hasSize(1));
 | 
			
		||||
		assertThat(log.get(0).getLevel(), is(Level.SEVERE));
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog("{ 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());
 | 
			
		||||
 | 
			
		||||
		log = checkAndGetLog(directory.resolve("while.boppi"));
 | 
			
		||||
		assertThat(log, empty());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue