CAstStatCall* CParser::subroutineCall(CAstScope *s, CToken ident) { // // subroutineCall ::= ident "(" [ expression {"," expression} ] ")". // CToken t; Consume(tLBrak); CSymtab *symtab = s->GetSymbolTable(); CAstFunctionCall *call = NULL; // Check undefined procedure/function call. if(symtab->FindSymbol(ident.GetValue(), sLocal) == NULL && symtab->FindSymbol(ident.GetValue(), sGlobal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(ident.GetValue(), sGlobal)) == NULL) SetError(ident, "invalid procedure/function identifier."); call = new CAstFunctionCall(ident, dynamic_cast<const CSymProc *>(symtab->FindSymbol(ident.GetValue(), sGlobal))); } else if(symtab->FindSymbol(ident.GetValue(), sLocal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(ident.GetValue(), sLocal)) == NULL) SetError(ident, "invalid procedure/function identifier."); call = new CAstFunctionCall(ident, dynamic_cast<const CSymProc *>(symtab->FindSymbol(ident.GetValue(), sLocal))); } else SetError(ident, "undefined identifier."); // Add expressions. if(isExpr(_scanner->Peek())) { CAstExpression *expr = expression(s); assert(expr != NULL); // Arrays are addressed. if(expr->GetType()->IsArray()) expr = new CAstSpecialOp(expr->GetToken(), opAddress, expr, NULL); call->AddArg(expr); while(_scanner->Peek().GetType() == tComma) { Consume(tComma); expr = expression(s); assert(expr != NULL); // Arrays are addressed. if(expr->GetType()->IsArray()) expr = new CAstSpecialOp(expr->GetToken(), opAddress, expr, NULL); call->AddArg(expr); } } Consume(tRBrak); return new CAstStatCall(ident, call); }
CTacAddr* CAstUnaryOp::ToTac(CCodeBlock *cb, CTacLabel *ltrue, CTacLabel *lfalse) { assert(cb != NULL); CTacAddr *ret = NULL; EOperation oper = GetOperation(); CAstExpression *operand = GetOperand(); CTacAddr *tOperand = NULL; if(oper == opNot) { // boolean CTacLabel *c_true = ltrue; CTacLabel *c_false = lfalse; if(!c_true || !c_false) { c_true = cb->CreateLabel(); c_false = cb->CreateLabel(); } tOperand = operand->ToTac(cb, c_false, c_true); // swap // last sequence 'not' unary op if(CAstConstant* boolConst = dynamic_cast<CAstConstant*>(operand)) { // operand is bool const if(boolConst->GetValue() == 1) // true cb->AddInstr(new CTacInstr(opGoto, c_false)); // !true = false else // false cb->AddInstr(new CTacInstr(opGoto, c_true)); // !false = true } else if(tOperand) { // if operand is id or func call, then tOperand returns not null // operand is id cb->AddInstr(new CTacInstr(opEqual, c_false, tOperand, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, c_true)); } if(ltrue || lfalse) { // not first of sequence 'not' unary op ret = NULL; } else { // first of sequence 'not' unary op CTacLabel *next = cb->CreateLabel(); cb->AddInstr(c_true); ret = cb->CreateTemp(operand->GetType()); cb->AddInstr(new CTacInstr(opAssign, ret, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, next)); cb->AddInstr(c_false); cb->AddInstr(new CTacInstr(opAssign, ret, new CTacConst(0))); cb->AddInstr(new CTacInstr(opGoto, next)); cb->AddInstr(next); } } else { // opPos || opNeg - integer tOperand = operand->ToTac(cb); ret = cb->CreateTemp(operand->GetType()); cb->AddInstr(new CTacInstr(oper, ret, tOperand)); } return ret; }
bool CAstBinaryOp::TypeCheck(CToken *t, string *msg) const { EOperation oper = GetOperation(); CAstExpression *left = GetLeft(); CAstExpression *right = GetRight(); if(!left->TypeCheck(t, msg)) return false; if(!right->TypeCheck(t, msg)) return false; switch(oper) { case opAdd: case opSub: case opMul: case opDiv: if(left->GetType() != CTypeManager::Get()->GetInt() || right->GetType() != CTypeManager::Get()->GetInt()) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "Operand types should be integer for '+','-','*',or '/'."; return false; } break; case opAnd: case opOr: if(left->GetType() != CTypeManager::Get()->GetBool() || right->GetType() != CTypeManager::Get()->GetBool()) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "Operand types should be boolean for '&&',or '||'."; return false; } break; case opEqual: case opNotEqual: if(left->GetType() != right->GetType()) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "Operand types should be same for '=',or '#'."; return false; } else if(left->GetType() == CTypeManager::Get()->GetNull()) { // left and right has same type, so we only check left type if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "Operand types should not be NULL."; return false; } break; case opLessThan: case opLessEqual: case opBiggerThan: case opBiggerEqual: if(left->GetType() != CTypeManager::Get()->GetInt() || right->GetType() != CTypeManager::Get()->GetInt()) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "Operand types should be integer for '<','<=','>',or '>='."; return false; } break; } return true; }
CAstExpression* CParser::addressExpression(CAstScope* s) { // // addressExpression ::= "&" expression // implicit type casting: array to pointer // CToken t = _scanner->Peek(); CAstExpression *e = expression(s); if (!e->GetType()) SetError(t, "NULL type"); else if (e->GetType()->IsArray()) return new CAstSpecialOp(t, opAddress, e, NULL); return e; }
bool CAstArrayDesignator::TypeCheck(CToken *t, string *msg) const { bool result = true; assert(_done); if(!GetType()) { // if GetType is NULL then it is an invalid array expression if(t != NULL) *t = GetToken(); if(msg != NULL) *msg = "invalid array expression."; return false; } int i; for(i = 0; i < GetNIndices(); i++) { CAstExpression *e = GetIndex(i); if(!e->TypeCheck(t, msg)) return false; // Do TypeCheck on indexing expression if(!e->GetType()->IsInt()) { // The indexing expression should be Int type if(t != NULL) *t = e->GetToken(); if(msg != NULL) *msg = "invalid array index expression."; return false; } } return result; }
bool CAstStatIf::TypeCheck(CToken *t, string *msg) const { CAstExpression *cond = GetCondition(); bool result = true; if (!cond->TypeCheck(t, msg)) return false; if (cond->GetType() != CTypeManager::Get()->GetBool()) { if (t != NULL) *t = cond->GetToken(); if (msg != NULL) *msg = "boolean expression expected."; return false; } try { CAstStatement *ifBody = GetIfBody(); CAstStatement *elseBody = GetElseBody(); while (result && (ifBody != NULL)) { result = ifBody->TypeCheck(t, msg); ifBody = ifBody->GetNext(); } while (result && (elseBody != NULL)) { result = elseBody->TypeCheck(t, msg); elseBody = elseBody->GetNext(); } } catch (...) { result = false; } return result; }
bool CAstStatReturn::TypeCheck(CToken *t, string *msg) const { const CType *st = GetScope()->GetType(); CAstExpression *e = GetExpression(); if (st->Match(CTypeManager::Get()->GetNull())) { if (e != NULL) { if (t != NULL) *t = e->GetToken(); if (msg != NULL) *msg = "superfluous expression after return."; return false; } } else { if (e == NULL) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "expression expected after return."; return false; } if (!e->TypeCheck(t, msg)) return false; if (!st->Match(e->GetType())) { if (t != NULL) *t = e->GetToken(); if (msg != NULL) *msg = "return type mismatch."; return false; } } return true; }
bool CAstFunctionCall::TypeCheck(CToken *t, string *msg) const { const CSymProc *symbol = GetSymbol(); // check the number of procedure/function arguments. if (GetNArgs() != symbol->GetNParams()) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "the number of arguments mismatch."; return false; } // first, type check for expression of arguments. // then, check the types of procedure/function arguments. // in this project, arguments type is only integer. for (int i = 0; i < GetNArgs(); i++) { CAstExpression *arg = GetArg(i); if (!arg->TypeCheck(t, msg)) return false; if (arg->GetType() != symbol->GetParam(i)->GetDataType()) { if (t != NULL) *t = arg->GetToken(); if (msg != NULL) *msg = "the type of arguments mismatch."; return false; } } return true; }
bool CAstUnaryOp::TypeCheck(CToken *t, string *msg) const { CAstExpression *e = GetOperand(); if (!e->TypeCheck(t, msg)) return false; if( ((GetOperation() == opPos || GetOperation() == opNeg) && (e->GetType() != CTypeManager::Get()->GetInt())) || ((GetOperation() == opNot) && (e->GetType() != CTypeManager::Get()->GetBool())) ) { if (t != NULL) *t = GetToken(); if (msg != NULL) *msg = "unary operation type mismatch."; return false; } return true; }
const CType* CAstUnaryOp::GetType(void) const { CTypeManager *tm = CTypeManager::Get(); EOperation oper = GetOperation(); CAstExpression *e = GetOperand(); if(e->GetType() == NULL) return NULL; if(oper == opNeg || oper == opPos) { //case '+' || '-' if(e->GetType()->IsInt()) return tm->GetInt(); else return NULL; } else { // case '!' if(e->GetType()->IsBoolean()) return tm->GetBool(); else return NULL; } }
bool CAstStatAssign::TypeCheck(CToken *t, string *msg) const { CAstDesignator *id = GetLHS(); CAstExpression *e = GetRHS(); if (!id->TypeCheck(t, msg)) return false; if (!e->TypeCheck(t, msg)) return false; if (!id->GetType()->Match(e->GetType())) { if (t != NULL) *t = e->GetToken(); if (msg != NULL) *msg = "assignment type mismatch."; return false; } return true; }
CAstExpression* CParser::factor(CAstScope *s) { // // factor ::= qualident | number | boolean | char | string | "(" expression ")" | subroutineCall | "!" factor. // FIRST(factor) = { tIdent, tNumber, tTrue, tFalse, tCharacter, tString, tLBrak, tEMark }. // CToken t; EToken tt = _scanner->Peek().GetType(); CTypeManager *tm = CTypeManager::Get(); CAstExpression *n = NULL; CSymtab *symtab = s->GetSymbolTable(); switch(tt) { case tTrue: Consume(tTrue, &t); n = new CAstConstant(t, tm->GetBool(), 1); break; case tFalse: Consume(tFalse, &t); n = new CAstConstant(t, tm->GetBool(), 0); break; case tNumber: { Consume(tNumber, &t); errno = 0; long long v = strtoll(t.GetValue().c_str(), NULL, 10); if (errno != 0) SetError(t, "invalid number."); // integer range validation check. if(v > 2147483648) SetError(t, "integer constant outside valid range."); n = new CAstConstant(t, tm->GetInt(), v); break; } case tIdent: { Consume(tIdent, &t); if(_scanner->Peek().GetType() == tLBrak) { // subroutineCall Consume(tLBrak); CAstFunctionCall *f; // If subroutineCall calls undefined procedure/function, then set error. if(symtab->FindSymbol(t.GetValue(),sLocal) == NULL && symtab->FindSymbol(t.GetValue(), sGlobal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sGlobal)) == NULL) SetError(t, "invalid procedure/function identifier."); f = new CAstFunctionCall(t, dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sGlobal))); } else if(symtab->FindSymbol(t.GetValue(), sLocal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sLocal)) == NULL) SetError(t, "invalid procedure/function identifier."); f = new CAstFunctionCall(t, dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sLocal))); } else SetError(t, "undefined identifier."); assert(f != NULL); if(isExpr(_scanner->Peek())) { CAstExpression *expr = expression(s); assert(expr != NULL); // If array, then addressed. if(expr->GetType()->IsArray()) expr = new CAstSpecialOp(expr->GetToken(), opAddress, expr, NULL); f->AddArg(expr); while(_scanner->Peek().GetType() == tComma) { Consume(tComma); expr = expression(s); assert(expr != NULL); // If array, then addressed. if(expr->GetType()->IsArray()) expr = new CAstSpecialOp(expr->GetToken(), opAddress, expr, NULL); f->AddArg(expr); } } Consume(tRBrak); n = f; } else { // qualident if(_scanner->Peek().GetType() == tLSBrak) { // It means array CAstArrayDesignator *f; if(symtab->FindSymbol(t.GetValue(), sLocal) == NULL && symtab->FindSymbol(t.GetValue(), sGlobal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sGlobal)) != NULL) SetError(t, "designator expected."); f = new CAstArrayDesignator(t, symtab->FindSymbol(t.GetValue(), sGlobal)); } else if(symtab->FindSymbol(t.GetValue(), sLocal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sLocal)) != NULL) SetError(t, "designator expected"); f = new CAstArrayDesignator(t, symtab->FindSymbol(t.GetValue(), sLocal)); } else { SetError(t, "undefined identifier."); } while(_scanner->Peek().GetType() == tLSBrak) { Consume(tLSBrak); CAstExpression *expr = expression(s); assert(expr != NULL); f->AddIndex(expr); Consume(tRSBrak); } f->IndicesComplete(); n = f; } // non-array case. else { if(symtab->FindSymbol(t.GetValue(), sLocal) == NULL && symtab->FindSymbol(t.GetValue(), sGlobal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sGlobal)) != NULL) { SetError(t, "designator expected."); } n = new CAstDesignator(t, symtab->FindSymbol(t.GetValue(), sGlobal)); } else if(symtab->FindSymbol(t.GetValue(), sLocal) != NULL) { // Check if symbol is procedure. if(dynamic_cast<const CSymProc *>(symtab->FindSymbol(t.GetValue(), sLocal)) != NULL) { SetError(t, "designator expected."); } n = new CAstDesignator(t, symtab->FindSymbol(t.GetValue(), sLocal)); } else SetError(t, "undefined identifier."); } } break; } case tString: Consume(tString, &t); n = new CAstStringConstant(t, t.GetValue(), s); break; case tCharacter: { Consume(tCharacter, &t); char res; // If character length is 0, it means character is '\0' because invalid character cases are handled in scanner. if(t.GetValue().length() == 0) res = '\0'; else if(t.GetValue().length() == 1) { if(t.GetValue().at(0) != '\\') res = t.GetValue().at(0); else SetError(t, "wrong character"); } // unescape. else if(t.GetValue().length() == 2) { if(t.GetValue().at(0) == '\\') { if(t.GetValue().at(1) == 'n') { res = '\n'; } else if(t.GetValue().at(1) == 't') { res = '\t'; } else if(t.GetValue().at(1) == '"') { res = '"'; } else if(t.GetValue().at(1) == '\'') { res = '\''; } else if(t.GetValue().at(1) == '\\') { res = '\\'; } else if(t.GetValue().at(1) == '0') { res = '\0'; } else SetError(t, "wrong character"); } } else SetError(t, "wrong character"); n = new CAstConstant(t, tm->GetChar(), (long long)res); break; } case tLBrak: Consume(tLBrak); n = expression(s); Consume(tRBrak); break; case tEMark: Consume(tEMark, &t); n = new CAstUnaryOp(t, opNot, factor(s)); break; default: // cout << "got " << _scanner->Peek() << endl; SetError(_scanner->Peek(), "factor expected."); break; } return n; }
CAstExpression* CParser::simpleexpr(CAstScope *s) { // // simpleexpr ::= ["+" | "-"] term { termOp term }. // termOp = {+, -, ||}. // CAstExpression *n = NULL; EOperation eOp; CToken tt; // unary operation case. if(_scanner->Peek().GetType() == tTermOp) { CToken t, tOp; Consume(tTermOp, &tOp); if(tOp.GetValue() == "+") eOp = opPos; else if(tOp.GetValue() == "-") eOp = opNeg; else SetError(tOp, "invalid term operator"); CAstExpression *tmp = term(s); // If term is constant, then negate it. -> Simple. if(dynamic_cast<CAstConstant *>(tmp) != NULL && tmp->GetType()->IsInt()) { if(eOp == opNeg) dynamic_cast<CAstConstant *>(tmp)->SetValue(-dynamic_cast<CAstConstant *>(tmp)->GetValue()); n = tmp; } else { n = new CAstUnaryOp(tOp, eOp, tmp); } while(_scanner->Peek().GetType() == tTermOp) { CAstExpression *l = n, *r; Consume(tTermOp, &tt); r = term(s); if(tt.GetValue() == "+") { n = new CAstBinaryOp(tt, opAdd, l, r); } else if(tt.GetValue() == "-") { n = new CAstBinaryOp(tt, opSub, l, r); } else { n = new CAstBinaryOp(tt, opOr, l, r); } } return n; } else { // non-unary case. n = term(s); while (_scanner->Peek().GetType() == tTermOp) { CToken t; CAstExpression *l = n, *r; Consume(tTermOp, &tt); r = term(s); if(tt.GetValue() == "+") { n = new CAstBinaryOp(tt, opAdd, l, r); } else if(tt.GetValue() == "-") { n = new CAstBinaryOp(tt, opSub, l, r); } else { n = new CAstBinaryOp(tt, opOr, l, r); } } return n; } }
CTacAddr* CAstBinaryOp::ToTac(CCodeBlock *cb, CTacLabel *ltrue, CTacLabel *lfalse) { assert(cb != NULL); CAstExpression *left = GetLeft(); CAstExpression *right = GetRight(); CTacAddr *ret = NULL; CTacAddr *tLeft = NULL; CTacAddr *tRight = NULL; // depends on operation type, ToTac differently. EOperation oper = GetOperation(); if(IsRelOp(oper) || oper == opOr || oper == opAnd) { // related with boolean // if no given label for true & false, create them. CTacLabel *l_true = (ltrue) ? ltrue : cb->CreateLabel(); CTacLabel *l_false = (lfalse) ? lfalse : cb->CreateLabel(); CTacLabel *l_next = NULL; CTacLabel *test_next = cb->CreateLabel(); if(!ltrue || !lfalse) { // it is most out binary ops // always return type is boolean. ret = cb->CreateTemp(CTypeManager::Get()->GetBool()); // l_next is needed. l_next = cb->CreateLabel(); } switch(oper) { case opOr: tLeft = left->ToTac(cb, l_true, test_next); // left term is boolean constant. if(CAstConstant* boolConst = dynamic_cast<CAstConstant*>(left)) { if(boolConst->GetValue() == 1) // true cb->AddInstr(new CTacInstr(opGoto, l_true)); else // false cb->AddInstr(new CTacInstr(opGoto, test_next)); } //else if(dynamic_cast<CAstDesignator*>(left)) { // left term is id. else if(tLeft) { // if left term is id or func call, then tLeft is not null cb->AddInstr(new CTacInstr(opEqual, l_true, tLeft, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, test_next)); } cb->AddInstr(test_next); tRight = right->ToTac(cb, l_true, l_false); // right term is boolean constant. if(CAstConstant* boolConst = dynamic_cast<CAstConstant*>(right)) { if(boolConst->GetValue() == 1) // true cb->AddInstr(new CTacInstr(opGoto, l_true)); else // false cb->AddInstr(new CTacInstr(opGoto, l_false)); } //else if(dynamic_cast<CAstDesignator*>(right)) { // right term is id. else if(tRight) { // if right term is id or func call, then tRight is not null cb->AddInstr(new CTacInstr(opEqual, l_true, tRight, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, l_false)); } break; case opAnd: tLeft = left->ToTac(cb, test_next, l_false); // left term is boolean constant. if(CAstConstant* boolConst = dynamic_cast<CAstConstant*>(left)) { if(boolConst->GetValue() == 1) // true cb->AddInstr(new CTacInstr(opGoto, test_next)); else // false cb->AddInstr(new CTacInstr(opGoto, l_false)); } //else if(dynamic_cast<CAstDesignator*>(left)) { // left term is id. else if(tLeft) { // if left term is id or func call, then tLeft is not null cb->AddInstr(new CTacInstr(opEqual, test_next, tLeft, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, l_false)); } cb->AddInstr(test_next); tRight = right->ToTac(cb, l_true, l_false); // right term is boolean constant. if(CAstConstant* boolConst = dynamic_cast<CAstConstant*>(right)) { if(boolConst->GetValue() == 1) // true cb->AddInstr(new CTacInstr(opGoto, l_true)); else // false cb->AddInstr(new CTacInstr(opGoto, l_false)); } //else if(dynamic_cast<CAstDesignator*>(right)) { // right term is id. else if(tRight) { // if right term is id or func call, then tRight is not null cb->AddInstr(new CTacInstr(opEqual, l_true, tRight, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, l_false)); } break; // rel Ops case opEqual: case opNotEqual: case opLessThan: case opLessEqual: case opBiggerThan: case opBiggerEqual: tLeft = left->ToTac(cb, l_true, l_false); tRight = right->ToTac(cb, l_true, l_false); cb->AddInstr(new CTacInstr(oper, l_true, tLeft, tRight)); cb->AddInstr(new CTacInstr(opGoto, l_false)); break; } if(!ltrue) { // assign temp as true cb->AddInstr(l_true); cb->AddInstr(new CTacInstr(opAssign, ret, new CTacConst(1))); cb->AddInstr(new CTacInstr(opGoto, l_next)); } if(!lfalse) { // assign temp as false cb->AddInstr(l_false); cb->AddInstr(new CTacInstr(opAssign, ret, new CTacConst(0))); cb->AddInstr(new CTacInstr(opGoto, l_next)); } if(!ltrue || !lfalse) cb->AddInstr(l_next); } else { // related with integer // no need for label of true & false tLeft = left->ToTac(cb); tRight = right->ToTac(cb); ret = cb->CreateTemp(left->GetType()); // left & right same type. cb->AddInstr(new CTacInstr(oper, ret, tLeft, tRight)); } return ret; }