package de.fuberlin.projecta.analysis.ast;
import java.util.ArrayList;
import java.util.List;
import de.fuberlin.commons.lexer.TokenType;
import de.fuberlin.projecta.analysis.BasicTokenType;
import de.fuberlin.projecta.analysis.EntryType;
import de.fuberlin.projecta.analysis.SemanticException;
import de.fuberlin.projecta.analysis.SymbolTableStack;
public class FuncDef extends Type {
@Override
public void checkSemantics() {
for (int i = 0; i < this.getChildrenCount(); i++) {
((AbstractSyntaxTree) this.getChild(i)).checkSemantics();
// if not, we have an empty method. The semantics of empty methods
// is defined as correct
if (this.getChild(this.getChildrenCount() - 1) instanceof Block) {
Block block = (Block) this
.getChild(this.getChildrenCount() - 1);
if (!(block.getChild(block.getChildrenCount() - 1) instanceof Return)) {
if (((BasicType) this.getChild(0)).getTokenType() != BasicTokenType.VOID) {
if (!canInsertReturn(block)) {
throw new SemanticException("Methods needs to return a value", this);
}
} else {
// no return type. so just add empty return
Return r = new Return();
block.addChild(r);
}
}
} else if (((BasicType) this.getChild(0)).getTokenType() != BasicTokenType.VOID) {
// we do not have any statements but a return type => BAD!!!
throw new SemanticException("Methods needs to return a value", this);
}
}
}
@Override
public void buildSymbolTable(SymbolTableStack stack) {
Type type = (Type) getChild(0);
Id id = (Id) getChild(1);
stack.push();
// these are parameters
((AbstractSyntaxTree) getChild(2)).buildSymbolTable(stack);
List<EntryType> parameters = stack.pop().getEntries();
if (this.getChildrenCount() == 4) {
// this is the block, it can
// handle everything itself
((AbstractSyntaxTree) getChild(3)).buildSymbolTable(stack);
for (EntryType entry : parameters) {
((AbstractSyntaxTree) getChild(3)).getTable()
.insertEntry(entry);
}
}
EntryType entry = new EntryType(id, type, parameters);
stack.top().insertEntry(entry);
}
@Override
public String genCode() {
String ret = "";
String blockCode = "";
if (getChildrenCount() > 3) {
if (!this.toTypeString().equals(BasicType.TYPE_VOID_STRING)) {
ret = "%" + ((Block) getChild(3)).getNewVar() + " = alloca "
+ this.fromTypeStringToLLVMType() + "\n";
}
blockCode = ((Block) getChild(3)).genCode();
ret += blockCode;
ret += "br label %return\nreturn:\n";
if (!this.toTypeString().equals(BasicType.TYPE_VOID_STRING)) {
int n = ((Block) getChild(3)).getNewVar();
ret += "%" + n + " = load "
+ this.fromTypeStringToLLVMType()
+ "* %1\n" + "ret "
+ this.fromTypeStringToLLVMType() + " %"
+ n + "\n";
} else {
ret += "ret " + this.fromTypeStringToLLVMType() + "\n";
}
}
return "define " + ((Type) getChild(0)).genCode() + " @"
+ ((Id) getChild(1)).genCode() + "("
+ ((Params) getChild(2)).genCode() + ") nounwind { \n" + ret
+ "}";
}
/**
* Nodes that should produce an error: break, print Nodes that cannot pop up
* here: BasicType, Declaration, Params, Program, FuncDef, Record, (Return)
*
* @param methodBlock
* @return
*/
private boolean canInsertReturn(AbstractSyntaxTree methodBlock) {
// We don't have return statement, so just insert one
// which one depends on the last statement...
AbstractSyntaxTree lastStatement = (AbstractSyntaxTree) methodBlock
.getChild(methodBlock.getChildrenCount() - 1);
ArrayList<Class<? extends Statement>> showStoppers = new ArrayList<Class<? extends Statement>>();
showStoppers.add(Break.class);
showStoppers.add(Print.class);
showStoppers.add(If.class); // It doesn't matter whether 'If'-block has
// return statement or not. Condition could
// be 'false' -> no return can be implied
showStoppers.add(While.class); // see above
if (showStoppers.contains(lastStatement.getClass())) {
// or throw SemanticException...
return false;
}
// We have to go through for possible return statements
if (lastStatement instanceof Block) {
Block block = (Block) lastStatement;
if (!block.hasReturnStatement()) {
return block.couldAmmendReturnStatement();
} else {
return true;
}
}
if (lastStatement instanceof Do) {
Do doLoop = (Do) lastStatement;
if (!doLoop.hasReturnStatement()) {
return doLoop.couldAmmendReturnStatement();
} else {
return true;
}
}
if (lastStatement instanceof IfElse) {
IfElse ifElse = (IfElse) lastStatement;
if (!ifElse.hasReturnStatement()) {
return ifElse.couldAmmendReturnStatement();
} else {
return true;
}
}
if (lastStatement instanceof BinaryOp) {
BinaryOp binOp = (BinaryOp) lastStatement;
if (binOp.getOp() == TokenType.OP_ASSIGN) {
// first child has to be an identifier. This is checked
// beforehand!
Return r = new Return();
r.addChild(binOp.getChild(0));
methodBlock.addChild(r);
return true;
} // it is an operation. A return statement will be created with
// this operation
}
// last statement is no control structure nor is it a show stopper =>
// just hang last statement node under a new return node
Return r = new Return();
methodBlock.removeChild(methodBlock.getChildrenCount() - 1);
r.addChild(lastStatement);
methodBlock.addChild(r);
return false;
}
@Override
public String toTypeString() {
return ((Type) getChild(0)).toTypeString();
}
}