package de.fuberlin.projecta.analysis;
import de.fuberlin.commons.lexer.TokenType;
import de.fuberlin.commons.parser.ISymbol;
import de.fuberlin.commons.parser.ISyntaxTree;
import de.fuberlin.projecta.analysis.ast.AbstractSyntaxTree;
import de.fuberlin.projecta.analysis.ast.Args;
import de.fuberlin.projecta.analysis.ast.Array;
import de.fuberlin.projecta.analysis.ast.ArrayCall;
import de.fuberlin.projecta.analysis.ast.BasicType;
import de.fuberlin.projecta.analysis.ast.BinaryOp;
import de.fuberlin.projecta.analysis.ast.Block;
import de.fuberlin.projecta.analysis.ast.BoolLiteral;
import de.fuberlin.projecta.analysis.ast.Break;
import de.fuberlin.projecta.analysis.ast.Declaration;
import de.fuberlin.projecta.analysis.ast.Do;
import de.fuberlin.projecta.analysis.ast.FuncCall;
import de.fuberlin.projecta.analysis.ast.FuncDef;
import de.fuberlin.projecta.analysis.ast.Id;
import de.fuberlin.projecta.analysis.ast.If;
import de.fuberlin.projecta.analysis.ast.IfElse;
import de.fuberlin.projecta.analysis.ast.IntLiteral;
import de.fuberlin.projecta.analysis.ast.Params;
import de.fuberlin.projecta.analysis.ast.Print;
import de.fuberlin.projecta.analysis.ast.Program;
import de.fuberlin.projecta.analysis.ast.RealLiteral;
import de.fuberlin.projecta.analysis.ast.Record;
import de.fuberlin.projecta.analysis.ast.RecordVarCall;
import de.fuberlin.projecta.analysis.ast.Return;
import de.fuberlin.projecta.analysis.ast.Statement;
import de.fuberlin.projecta.analysis.ast.StringLiteral;
import de.fuberlin.projecta.analysis.ast.Type;
import de.fuberlin.projecta.analysis.ast.UnaryOp;
import de.fuberlin.projecta.analysis.ast.While;
import de.fuberlin.projecta.parser.NonTerminal;
import de.fuberlin.projecta.parser.Symbol;
import de.fuberlin.projecta.parser.Symbol.Reserved;
/**
* Converts a parse tree to an AST
*/
public class SemanticAnalyzer {
private static final String L_ATTRIBUTE = "L_ATTRIBUTE";
private final SymbolTableStack tables = new SymbolTableStack();
private ISyntaxTree parseTree;
private AbstractSyntaxTree AST;
/**
* Create SemanticAnalyzer instance
*
* @param tree Parse tree
*/
public SemanticAnalyzer(ISyntaxTree tree) {
this.parseTree = tree;
}
/**
* Analyze the parse tree
* Converts the parse tree to an AST, then semantically analyzes it
*
* @throws SemanticException
*/
public void analyze() throws SemanticException {
toAST(parseTree);
AST.buildSymbolTable(tables);
AST.checkSemantics();
AST.checkTypes();
}
/**
* Start the parse tree to AST conversion
*
* @param tree Parse tree
*/
private void toAST(ISyntaxTree tree) {
toAST(tree, null);
}
/**
* Translate ISymbol to our internal Symbol class
*
* This function is there to be able to parse ISymbols from other groups
* Symbol has a much cleaner API, so we try to use this
*
* @param symbol Generic ISymbol instance
* @return Our symbol representation (Symbol class)
*/
private Symbol translate(ISymbol symbol) {
if (symbol instanceof Symbol)
return (Symbol) symbol;
else {
// let Symbol figure out which type this is
return new Symbol(symbol.getName());
}
}
/**
* Traverses through the parse tree in depth-first-search and adds new nodes
* to the given insertNode. Only l-attributed nodes must be passed through
* node-attributes!
*
* The idea is that one node knows where it should get added to.
*
* @param tree
* current parseTree-node (with symbol)
* @param insertNode
* syntaxTree-Node, in which new nodes get added
*/
private void toAST(ISyntaxTree tree, AbstractSyntaxTree insertNode) {
Symbol symbol = translate(tree.getSymbol());
// pass the parse tree token to the AST node if possible.
// needed for later error reporting!
if (insertNode != null) {
insertNode.setToken(tree.getToken());
}
if (symbol.isNonTerminal()) {
NonTerminal nonT = symbol.asNonTerminal();
switch (nonT) {
case program:
AST = new Program();
toAST(tree.getChild(0), AST);
return;
case funcs:
for (int i = 0; i < tree.getChildrenCount(); i++) {
toAST(tree.getChild(i), insertNode);
}
return;
case func:
FuncDef func = new FuncDef();
for (int i = 0; i < tree.getChildrenCount(); i++) {
toAST(tree.getChild(i), func);
}
insertNode.addChild(func);
return;
case type:
if (translate(tree.getChild(0).getSymbol()).asTerminal() == TokenType.BASIC) {
// this is type_ and it must exist!
if (tree.getChild(1).getChildrenCount() != 0) {
Type array = new Array();
// the basic node gets added to this temporary node
// and is passed to the new array
// it doesn't matter which node to take as long as
// it is a treenode.
AbstractSyntaxTree tmp = new Program();
toAST(tree.getChild(0), tmp);
array.addAttribute(L_ATTRIBUTE);
// this is already the BasicType node
boolean success = array.setAttribute(L_ATTRIBUTE,
tmp.getChild(0));
assert (success);
insertNode.addChild(array);
toAST(tree.getChild(1), array);
} else
// type_ is empty, hook the basic node in the parent
toAST(tree.getChild(0), insertNode);
} else { // we have a record! *CONGRATS*
Record record = new Record();
// test if we have an array of this record
if (tree.getChild(4).getChildrenCount() != 0) {
Type array = new Array();
toAST(tree.getChild(2), record); // <-- decls!!!
array.addAttribute(L_ATTRIBUTE);
array.setAttribute(L_ATTRIBUTE, record);
toAST(tree.getChild(4), array);
insertNode.addChild(array);
} else // this is simply one record
{
toAST(tree.getChild(2), record); // <-- decls!!!
insertNode.addChild(record);
}
}
return;
case type_:
// TODO: Width of array!!!
toAST(tree.getChild(1), insertNode);
if (tree.getChild(3).getChildrenCount() != 0) {
Array array = new Array();
array.addAttribute(L_ATTRIBUTE);
array.setAttribute(L_ATTRIBUTE,
insertNode.getAttribute(L_ATTRIBUTE));
toAST(tree.getChild(3), array);
insertNode.addChild(array);
} else // array declaration stopped here...
{
insertNode.addChild((ISyntaxTree) insertNode
.getAttribute(L_ATTRIBUTE));
}
return;
case optparams:
Params params = new Params();
for (int i = 0; i < tree.getChildrenCount(); i++) {
toAST(tree.getChild(i), params);
}
insertNode.addChild(params);
return;
case block:
Block block = new Block();
for (int i = 0; i < tree.getChildrenCount(); i++) {
toAST(tree.getChild(i), block);
}
if (block.getChildrenCount() != 0) {
insertNode.addChild(block);
} // else func_ -> ;
return;
case decl:
Declaration decl = new Declaration();
for (int i = 0; i < tree.getChildrenCount(); i++) {
toAST(tree.getChild(i), decl);
}
insertNode.addChild(decl);
return;
case stmt: {
Statement stmt = null;
ISyntaxTree firstChild = tree.getChild(0);
Symbol firstChildSymbol = translate(firstChild.getSymbol());
if (firstChildSymbol.isTerminal()) {
TokenType terminal = translate(firstChild.getSymbol())
.asTerminal();
if (terminal == TokenType.IF) {
ISyntaxTree stmt_ = tree.getChild(5); //
if (stmt_.getChildrenCount() == 0)
stmt = new If();
else
stmt = new IfElse();
} else if (terminal == TokenType.WHILE) {
stmt = new While();
} else if (terminal == TokenType.DO) {
stmt = new Do();
} else if (terminal == TokenType.BREAK) {
stmt = new Break();
} else if (terminal == TokenType.RETURN) {
stmt = new Return();
} else if (terminal == TokenType.PRINT) {
stmt = new Print();
}
} else if (firstChildSymbol.isNonTerminal()) {
toAST(firstChild, insertNode);
}
if (stmt != null) {
for (int i = 0; i < tree.getChildrenCount(); i++) {
toAST(tree.getChild(i), stmt);
}
insertNode.addChild(stmt);
}
return;
}
case assign:
case bool:
case join:
case equality:
case rel:
case expr:
case term:
if (tree.getChild(1).getChildrenCount() > 0) {
AbstractSyntaxTree tmp = new Program();
toAST(tree.getChild(0), tmp);
tree.getChild(1).addAttribute(L_ATTRIBUTE);
tree.getChild(1).setAttribute(L_ATTRIBUTE, tmp);
toAST(tree.getChild(1), insertNode);
} else {
toAST(tree.getChild(0), insertNode);
}
return;
case assign_:
case bool_:
case join_:
case equality_:
case expr_:
case term_:
if(tree.getChildrenCount() == 0){
insertNode.addChild(((ISyntaxTree)tree.getAttribute(L_ATTRIBUTE)).getChild(0));
} else {
ISyntaxTree tmp = new Program();
BinaryOp bOp = new BinaryOp(translate(
tree.getChild(0).getSymbol()).asTerminal());
// ((ISyntaxTree)tree.getAttribute(L_ATTRIBUTE)) = tmp
bOp.addChild(((ISyntaxTree)tree.getAttribute(L_ATTRIBUTE)).getChild(0));
toAST(tree.getChild(1), bOp);
tmp.addChild(bOp);
// simply hang in both children trees
tree.getChild(2).addAttribute(L_ATTRIBUTE);
tree.getChild(2).setAttribute(L_ATTRIBUTE, tmp);
toAST(tree.getChild(2), insertNode);
}
return;
case rel_:
if(tree.getChildrenCount() == 0){
insertNode.addChild(((ISyntaxTree)tree.getAttribute(L_ATTRIBUTE)).getChild(0));
} else {
BinaryOp bOp = new BinaryOp(translate(
tree.getChild(0).getSymbol()).asTerminal());
// ((ISyntaxTree)tree.getAttribute(L_ATTRIBUTE)) = tmp
bOp.addChild(((ISyntaxTree)tree.getAttribute(L_ATTRIBUTE)).getChild(0));
toAST(tree.getChild(1), bOp);
insertNode.addChild(bOp);
}
return;
case unary: {
Symbol firstChildSymbol = translate(tree.getChild(0)
.getSymbol());
if (firstChildSymbol.isNonTerminal()) {
toAST(tree.getChild(0), insertNode);
} else {
UnaryOp uOp = new UnaryOp(firstChildSymbol.asTerminal());
// simply hang in both children trees
toAST(tree.getChild(1), uOp); // unary
insertNode.addChild(uOp);
}
}
case factor:
if (translate(tree.getChild(0).getSymbol()).isNonTerminal()) {
if (tree.getChildrenCount() >= 2) {
if (tree.getChild(1).getChildrenCount() == 0) {
toAST(tree.getChild(0), insertNode);
} else {
FuncCall call = new FuncCall();
for (ISyntaxTree tmp : tree.getChildren()) {
toAST(tmp, call);
}
insertNode.addChild(call);
}
}
} else {
for (ISyntaxTree tmp : tree.getChildren()) {
toAST(tmp, insertNode);
}
}
return;
case factor_:
if (tree.getChild(1).getChildrenCount() != 0) {
Args args = new Args();
toAST(tree.getChild(1), args);
insertNode.addChild(args);
}
return;
case loc:
if (tree.getChild(1).getChildrenCount() == 0) {
toAST(tree.getChild(0), insertNode);
} else {
AbstractSyntaxTree tmp = new Program();
toAST(tree.getChild(0), tmp);
tree.getChild(1).addAttribute(L_ATTRIBUTE);
// there is only one child (the id itself)!
tree.getChild(1).setAttribute(L_ATTRIBUTE, tmp.getChild(0));
toAST(tree.getChild(1), insertNode);
}
return;
case loc__:
tree.getChild(0).addAttribute(L_ATTRIBUTE);
tree.getChild(0).setAttribute(L_ATTRIBUTE,
tree.getAttribute(L_ATTRIBUTE));
if (tree.getChild(1).getChildrenCount() == 0) {
toAST(tree.getChild(0), insertNode);
} else {
AbstractSyntaxTree tmp = new Program();
toAST(tree.getChild(0), tmp);
if (tmp.getChildrenCount() == 1) {
tree.getChild(1).addAttribute(L_ATTRIBUTE);
tree.getChild(1).setAttribute(L_ATTRIBUTE,
tmp.getChild(0));
toAST(tree.getChild(1), insertNode);
}
}
return;
case loc_:
// TODO: not everything is well thought atm...
if (translate(tree.getChild(0).getSymbol()).asTerminal() == TokenType.OP_DOT) {
RecordVarCall varCall = new RecordVarCall();
varCall.addChild((ISyntaxTree) tree
.getAttribute(L_ATTRIBUTE));
toAST(tree.getChild(1), varCall);
insertNode.addChild(varCall);
} else {
ArrayCall array = new ArrayCall();
toAST(tree.getChild(1), array);
array.addChild((ISyntaxTree) tree.getAttribute(L_ATTRIBUTE));
insertNode.addChild(array);
}
return;
// list of nodes which use the default case:
// assign_, bool_, decls, equality_, expr_, factor_,
// func_, join_,
// params, params_, rel_, stmt_, stmt__, stmts,
default:
// nothing to do here just pass it through
for (ISyntaxTree tmp : tree.getChildren()) {
toAST(tmp, insertNode);
}
}
} else if (symbol.isTerminal()) {
TokenType t = symbol.asTerminal();
switch (t) {
case BASIC:
String typeString = (String) tree
.getToken().getAttribute();
BasicTokenType type = BasicTokenType.valueOf(typeString.toUpperCase());
insertNode.addChild(new BasicType(type));
return;
case INT_LITERAL:
insertNode.addChild(new IntLiteral((Integer) tree
.getToken().getAttribute()));
return;
case STRING_LITERAL:
insertNode.addChild(new StringLiteral((String) tree
.getToken().getAttribute()));
return;
case REAL_LITERAL:
insertNode.addChild(new RealLiteral((Double) tree
.getToken().getAttribute()));
return;
case BOOL_LITERAL:
insertNode.addChild(new BoolLiteral((Boolean) tree
.getToken().getAttribute()));
return;
case ID:
insertNode.addChild(new Id((String) tree
.getToken().getAttribute()));
return;
default:// everything, which has no class member in its node uses
// the default.
}
} else if (symbol.isReservedTerminal()) {
Reserved res = symbol.asReservedTerminal();
switch (res) {
case EPSILON:
if (tree.getChildrenCount() == 1) {
toAST(tree.getChild(0));
} else {
// this should never occur
throw new SemanticException(
"Epsilon in other position than head?", null);
}
case FP:
// this should never occur
// throw new SemanticException("Stack pointer in parsetree?");
}
}
}
public AbstractSyntaxTree getAST() {
return AST;
}
}