You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
2.8 KiB
95 lines
2.8 KiB
package chocopy.pa2;
|
|
|
|
import chocopy.common.analysis.AbstractNodeAnalyzer;
|
|
import chocopy.common.analysis.SymbolTable;
|
|
import chocopy.common.analysis.types.Type;
|
|
import chocopy.common.analysis.types.ValueType;
|
|
import chocopy.common.astnodes.*;
|
|
|
|
import static chocopy.common.analysis.types.Type.INT_TYPE;
|
|
import static chocopy.common.analysis.types.Type.OBJECT_TYPE;
|
|
|
|
/**
|
|
* Analyzer that performs ChocoPy type checks on all nodes. Applied after collecting declarations.
|
|
*/
|
|
public class TypeChecker extends AbstractNodeAnalyzer<Type> {
|
|
|
|
/** The current symbol table (changes depending on the function being analyzed). */
|
|
private final SymbolTable<Type> sym;
|
|
/** Collector for errors. */
|
|
private final Errors errors;
|
|
|
|
/**
|
|
* Creates a type checker using GLOBALSYMBOLS for the initial global symbol table and ERRORS0 to
|
|
* receive semantic errors.
|
|
*/
|
|
public TypeChecker(SymbolTable<Type> globalSymbols, Errors errors0) {
|
|
sym = globalSymbols;
|
|
errors = errors0;
|
|
}
|
|
|
|
/**
|
|
* Inserts an error message in NODE if there isn't one already. The message is constructed with
|
|
* MESSAGE and ARGS as for String.format.
|
|
*/
|
|
private void err(Node node, String message, Object... args) {
|
|
errors.semError(node, message, args);
|
|
}
|
|
|
|
@Override
|
|
public Type analyze(Program program) {
|
|
for (Declaration decl : program.declarations) {
|
|
decl.dispatch(this);
|
|
}
|
|
for (Stmt stmt : program.statements) {
|
|
stmt.dispatch(this);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Type analyze(ExprStmt s) {
|
|
s.expr.dispatch(this);
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Type analyze(IntegerLiteral i) {
|
|
return i.setInferredType(Type.INT_TYPE);
|
|
}
|
|
|
|
@Override
|
|
public Type analyze(BinaryExpr e) {
|
|
Type t1 = e.left.dispatch(this);
|
|
Type t2 = e.right.dispatch(this);
|
|
|
|
switch (e.operator) {
|
|
case "-":
|
|
case "*":
|
|
case "//":
|
|
case "%":
|
|
if (INT_TYPE.equals(t1) && INT_TYPE.equals(t2)) {
|
|
return e.setInferredType(INT_TYPE);
|
|
} else {
|
|
err(e, "Cannot apply operator `%s` on types `%s` and `%s`", e.operator, t1, t2);
|
|
return e.setInferredType(INT_TYPE);
|
|
}
|
|
default:
|
|
return e.setInferredType(OBJECT_TYPE);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Type analyze(Identifier id) {
|
|
String varName = id.name;
|
|
Type varType = sym.get(varName);
|
|
|
|
if (varType != null && varType.isValueType()) {
|
|
return id.setInferredType(varType);
|
|
}
|
|
|
|
err(id, "Not a variable: %s", varName);
|
|
return id.setInferredType(ValueType.OBJECT_TYPE);
|
|
}
|
|
}
|