package edu.parser.QL.typechecker;
import edu.exceptions.TypeCheckException;
import edu.nodes.QuestionType;
import edu.parser.QL.QLVisitorImpl;
import edu.parser.QL.nodes.AbstractNode;
import edu.parser.QL.nodes.Form;
import edu.parser.QL.nodes.expression.Expression;
import edu.parser.QL.nodes.expression.ExpressionVisitor;
import edu.parser.QL.nodes.expression.QLIdentifier;
import edu.parser.QL.nodes.question.Label;
import edu.parser.QL.nodes.question.Question;
import edu.parser.QL.nodes.statement.ElseClause;
import edu.parser.QL.nodes.statement.IfStatement;
import edu.parser.QL.nodes.statement.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Created by Steven Kok on 21/02/2015.
*/
public class TypeChecker extends QLVisitorImpl {
public static final String ALREADY_DECLARED_QUESTION_DIFFERENT_TYPE = "Question identifier: [%s] was already declared with type: [%s].";
public static final String ALREADY_DECLARED_QUESTION = "Duplicate question declaration. Identifier: [%s] Type: [%s].";
public static final String EXPRESSION_EXPECTS_BOOLEAN = "Expression expects boolean operands for type: [%s], but found: [%s]";
public static final String EXPRESSION_EXPECTS_NON_BOOLEAN = "Expression does not expect boolean operands for type: [%s], but found: [%s]";
private final Map<QLIdentifier, Question> questions;
private static final Logger logger = Logger.getLogger(TypeChecker.class.getName());
private final ExpressionVisitor expressionVisitor;
public TypeChecker() {
this.questions = new HashMap<>();
expressionVisitor = new TypeCheckerExpressionValidator(questions);
}
@Override
public AbstractNode visit(Form form) {
visitStatements(form.getElements());
checkDuplicatedQuestionLabels();
return form;
}
private void checkDuplicatedQuestionLabels() {
Set<Label> duplicatedLabels = getDuplicatedLabels();
duplicatedLabels.forEach(
label -> logger.warning(
String.format("Found duplicate name declaration for: [%s]", label.getLabel())));
}
private Set<Label> getDuplicatedLabels() {
List<Label> labels = getLabels();
return labels
.stream()
.filter(n -> labels.stream()
.filter(label -> label.equals(n))
.count() > 1)
.collect(Collectors.toSet());
}
private List<Label> getLabels() {
return questions.entrySet()
.stream()
.map(entry -> entry.getValue().getLabel())
.collect(Collectors.toList());
}
@Override
public AbstractNode visit(IfStatement ifStatement) {
visit(ifStatement.getExpression());
visitStatements(ifStatement.getStatements());
return ifStatement;
}
private AbstractNode visit(Expression expression) {
return expressionVisitor.visit(expression);
}
@Override
public AbstractNode visit(Statement statement) {
return statement.accept(this);
}
@Override
public AbstractNode visit(Question question) {
if (questionAlreadyFound(question)) {
return throwExceptionForDuplicateQuestion(question);
} else {
return questions.put(question.getQLIdentifier(), question);
}
}
private AbstractNode throwExceptionForDuplicateQuestion(Question question) {
if (foundQuestionHasSameType(question)) {
throw new TypeCheckException(
String.format(ALREADY_DECLARED_QUESTION,
question.getQLIdentifier().getIdentifier(), question.getQuestionType().name()));
} else {
throw new TypeCheckException(
String.format(ALREADY_DECLARED_QUESTION_DIFFERENT_TYPE,
question.getQLIdentifier().getIdentifier(), questions.get(question.getQLIdentifier()).getQuestionType().name()));
}
}
private boolean questionAlreadyFound(Question question) {
return questions.containsKey(question.getQLIdentifier());
}
private boolean foundQuestionHasSameType(Question question) {
QuestionType questionType = questions.get(question.getQLIdentifier()).getQuestionType();
return questionType.equals(question.getQuestionType());
}
@Override
public AbstractNode visit(ElseClause elseClause) {
visitStatements(elseClause.getStatements());
return elseClause;
}
}