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

134 lines
3.6 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.Ast.VarDecl;
import cd.ir.AstVisitor;
import cd.ir.Symbol;
import cd.ir.Symbol.*;
import cd.ir.Symbol.VariableSymbol.Kind;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A helper class for {@link SemanticAnalyzer} which iterates through
* a class definition and creates symbols for all of its fields and
* methods. For each method, we also create symbols for parameters
* and local variables.
*/
public class SymbolCreator extends Object {
final Main main;
final SymTable<TypeSymbol> typesTable;
public SymbolCreator(Main main, SymTable<TypeSymbol> typesTable) {
this.main = main;
this.typesTable = typesTable;
}
public void createSymbols(ClassDecl cd) {
// lookup the super class. the grammar guarantees that this
// will refer to a class, if the lookup succeeds.
cd.sym.superClass = (ClassSymbol) typesTable.getType(cd.superClass);
new ClassSymbolCreator(cd.sym).visitChildren(cd, null);
}
/**
* Useful method which adds a symbol to a map, checking to see
* that there is not already an entry with the same name.
* If a symbol with the same name exists, throws an exception.
*/
public <S extends Symbol> void add(Map<String, S> map, S sym) {
if (map.containsKey(sym.name))
throw new SemanticFailure(
Cause.DOUBLE_DECLARATION,
"Symbol '%s' was declared twice in the same scope",
sym.name);
map.put(sym.name, sym);
}
/**
* Creates symbols for all fields/constants/methods in a class.
* Uses {@link MethodSymbolCreator} to create symbols for all
* parameters and local variables to each method as well.
* Checks for duplicate members.
*/
public class ClassSymbolCreator extends AstVisitor<Void, Void> {
final ClassSymbol classSym;
public ClassSymbolCreator(ClassSymbol classSym) {
this.classSym = classSym;
}
@Override
public Void varDecl(VarDecl ast, Void arg) {
ast.sym = new VariableSymbol(
ast.name, typesTable.getType(ast.type), Kind.FIELD);
add(classSym.fields, ast.sym);
return null;
}
@Override
public Void methodDecl(MethodDecl ast, Void arg) {
ast.sym = new MethodSymbol(ast);
ast.sym.owner = classSym;
add(classSym.methods, ast.sym);
// create return type symbol
if (ast.returnType.equals("void")) {
ast.sym.returnType = PrimitiveTypeSymbol.voidType;
} else {
ast.sym.returnType = typesTable.getType(ast.returnType);
}
// create symbols for each parameter
Set<String> pnames = new HashSet<String>();
for (int i = 0; i < ast.argumentNames.size(); i++) {
String argumentName = ast.argumentNames.get(i);
String argumentType = ast.argumentTypes.get(i);
if (pnames.contains(argumentName))
throw new SemanticFailure(
Cause.DOUBLE_DECLARATION,
"Method '%s' has two parameters named '%s'",
ast.sym, argumentName);
pnames.add(argumentName);
VariableSymbol vs = new VariableSymbol(
argumentName, typesTable.getType(argumentType));
ast.sym.parameters.add(vs);
}
// create symbols for the local variables
new MethodSymbolCreator(ast.sym).visitChildren(ast.decls(), null);
return null;
}
}
public class MethodSymbolCreator extends AstVisitor<Void, Void> {
final MethodSymbol methodSym;
public MethodSymbolCreator(MethodSymbol methodSym) {
this.methodSym = methodSym;
}
@Override
public Void varDecl(VarDecl ast, Void arg) {
ast.sym = new VariableSymbol(
ast.name, typesTable.getType(ast.type), Kind.LOCAL);
add(methodSym.locals, ast.sym);
return null;
}
}
}