compiler-design-eth/src/cd/frontend/semantic/SemanticAnalyzer.java

168 lines
4.7 KiB
Java

package cd.frontend.semantic;
import cd.Main;
import cd.frontend.semantic.SemanticFailure.Cause;
import cd.ir.Ast.ClassDecl;
import cd.ir.Ast.MethodDecl;
import cd.ir.Symbol;
import cd.ir.Symbol.*;
import java.util.ArrayList;
import java.util.List;
public class SemanticAnalyzer {
public final Main main;
public SemanticAnalyzer(Main main) {
this.main = main;
}
public void check(List<ClassDecl> classDecls)
throws SemanticFailure {
{
SymTable<TypeSymbol> typeSymbols = createSymbols(classDecls);
checkInheritance(classDecls);
checkStartPoint(typeSymbols);
checkMethodBodies(typeSymbols, classDecls);
{
rewriteMethodBodies(classDecls);
main.allTypeSymbols = typeSymbols.allSymbols();
}
}
}
/**
* Creates a symbol table with symbols for all built-in types,
* as well as all classes and their fields and methods. Also
* creates a corresponding array symbol for every type
* (named {@code type[]}).
*
* @see SymbolCreator
*/
private SymTable<TypeSymbol> createSymbols(List<ClassDecl> classDecls) {
// Start by creating a symbol for all built-in types.
SymTable<TypeSymbol> typeSymbols = new SymTable<TypeSymbol>(null);
typeSymbols.add(PrimitiveTypeSymbol.intType);
typeSymbols.add(PrimitiveTypeSymbol.booleanType);
typeSymbols.add(PrimitiveTypeSymbol.voidType);
typeSymbols.add(ClassSymbol.objectType);
// Add symbols for all declared classes.
for (ClassDecl ast : classDecls) {
// Check for classes named Object
if (ast.name.equals(ClassSymbol.objectType.name))
throw new SemanticFailure(Cause.OBJECT_CLASS_DEFINED);
ast.sym = new ClassSymbol(ast);
typeSymbols.add(ast.sym);
}
// Create symbols for arrays of each type.
for (Symbol sym : new ArrayList<Symbol>(typeSymbols.localSymbols())) {
Symbol.ArrayTypeSymbol array =
new Symbol.ArrayTypeSymbol((TypeSymbol) sym);
typeSymbols.add(array);
}
// For each class, create symbols for each method and field
SymbolCreator sc = new SymbolCreator(main, typeSymbols);
for (ClassDecl ast : classDecls)
sc.createSymbols(ast);
return typeSymbols;
}
/**
* Check for errors related to inheritance:
* circular inheritance, invalid super
* classes, methods with different types, etc.
* Note that this must be run early because other code assumes
* that the inheritance is correct, for type checking etc.
*
* @see InheritanceChecker
*/
private void checkInheritance(List<ClassDecl> classDecls) {
for (ClassDecl cd : classDecls)
new InheritanceChecker().visit(cd, null);
}
/**
* Guarantee there is a class Main which defines a method main
* with no arguments.
*/
private void checkStartPoint(SymTable<TypeSymbol> typeSymbols) {
Symbol mainClass = typeSymbols.get("Main");
if (mainClass != null && mainClass instanceof ClassSymbol) {
ClassSymbol cs = (ClassSymbol) mainClass;
MethodSymbol mainMethod = cs.getMethod("main");
if (mainMethod != null && mainMethod.parameters.size() == 0 &&
mainMethod.returnType == PrimitiveTypeSymbol.voidType) {
main.mainType = cs;
return; // found the main() method!
}
}
throw new SemanticFailure(Cause.INVALID_START_POINT, "No Main class with method 'void main()' found");
}
/**
* Check the bodies of methods for errors, particularly type errors
* but also undefined identifiers and the like.
*
* @see TypeChecker
*/
private void checkMethodBodies(
SymTable<TypeSymbol> typeSymbols,
List<ClassDecl> classDecls) {
TypeChecker tc = new TypeChecker(typeSymbols);
for (ClassDecl classd : classDecls) {
SymTable<VariableSymbol> fldTable = new SymTable<VariableSymbol>(null);
// add all fields of this class, or any of its super classes
for (ClassSymbol p = classd.sym; p != null; p = p.superClass)
for (VariableSymbol s : p.fields.values())
if (!fldTable.contains(s.name))
fldTable.add(s);
// type check any method bodies and final locals
for (MethodDecl md : classd.methods()) {
boolean hasReturn = new ReturnCheckerVisitor().visit(md.body(), null);
if (!md.returnType.equals("void") && !hasReturn) {
throw new SemanticFailure(Cause.MISSING_RETURN,
"Method %s.%s is missing a return statement",
classd.name,
md.name);
}
SymTable<VariableSymbol> mthdTable = new SymTable<VariableSymbol>(fldTable);
mthdTable.add(classd.sym.thisSymbol);
for (VariableSymbol p : md.sym.parameters) {
mthdTable.add(p);
}
for (VariableSymbol l : md.sym.locals.values()) {
mthdTable.add(l);
}
tc.checkMethodDecl(md, mthdTable);
}
}
}
private void rewriteMethodBodies(List<ClassDecl> classDecls) {
for (ClassDecl cd : classDecls)
new FieldQualifier().rewrite(cd);
}
}