CBotInstr* CBotTry::Compile(CBotToken* &p, CBotCStack* pStack) { CBotTry* inst = new CBotTry(); // creates the object CBotToken* pp = p; // preserves at the ^ token (starting position) inst->SetToken(p); if (!IsOfType(p, ID_TRY)) return nullptr; // should never happen CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk ); CBotCatch** pn = &inst->m_ListCatch; while (pStk->IsOk() && p->GetType() == ID_CATCH) { CBotCatch* i = CBotCatch::Compile(p, pStk); *pn = i; pn = &i->m_next; } if (pStk->IsOk() && IsOfType( p, ID_FINALLY) ) { inst->m_FinalInst = CBotBlock::CompileBlkOrInst( p, pStk ); } if (pStk->IsOk()) { return pStack->Return(inst, pStk); // return an object to the application } delete inst; // error, frees up return pStack->Return(nullptr, pStk); // no object, the error is on the stack }
CBotInstr* CBotReturn::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; if (!IsOfType(p, ID_RETURN)) return NULL; // should never happen CBotReturn* inst = new CBotReturn(); // creates the object inst->SetToken( pp ); CBotTypResult type = pStack->GetRetType(); if ( type.GetType() == 0 ) // returned void ? { if ( IsOfType( p, ID_SEP ) ) return inst; pStack->SetError( TX_BADTYPE, pp ); return NULL; } inst->m_Instr = CBotExpression::Compile(p, pStack); if ( pStack->IsOk() ) { CBotTypResult retType = pStack->GetTypResult(2); if (TypeCompatible(retType, type, ID_ASS)) { if ( IsOfType( p, ID_SEP ) ) return inst; pStack->SetError(TX_ENDOF, p->GetStart()); } pStack->SetError(TX_BADTYPE, p->GetStart()); } delete inst; return NULL; // no object, the error is on the stack }
CBotCatch* CBotCatch::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCatch* inst = new CBotCatch(); // creates the object pStack->SetStartError(p->GetStart()); inst->SetToken(p); if (!IsOfType(p, ID_CATCH)) return nullptr; // should never happen if (IsOfType(p, ID_OPENPAR)) { inst->m_Cond = CBotExpression::Compile(p, pStack); if (( pStack->GetType() < CBotTypLong || pStack->GetTypResult().Eq(CBotTypBoolean) )&& pStack->IsOk() ) { if (IsOfType(p, ID_CLOSEPAR)) { inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStack ); if ( pStack->IsOk() ) return inst; // return an object to the application } pStack->SetError(TX_CLOSEPAR, p->GetStart()); } pStack->SetError(TX_BADTYPE, p->GetStart()); } pStack->SetError(TX_OPENPAR, p->GetStart()); delete inst; // error, frees up return nullptr; // no object, the error is on the stack }
CBotInstr* CBotListInstr::Compile(CBotToken* &p, CBotCStack* pStack, bool bLocal) { CBotCStack* pStk = pStack->TokenStack(p, bLocal); // variables are local CBotListInstr* inst = new CBotListInstr(); while (true) { if (p == nullptr) break; if (IsOfType(p, ID_SEP)) continue; // empty statement ignored if (p->GetType() == ID_CLBLK) break; if (IsOfType(p, 0)) { pStack->SetError(CBotErrCloseBlock, p->GetStart()); delete inst; return pStack->Return(nullptr, pStk); } CBotInstr* i = CBotBlock::CompileBlkOrInst(p, pStk); // compiles next if (!pStk->IsOk()) { delete inst; return pStack->Return(nullptr, pStk); } if (inst->m_instr == nullptr) inst->m_instr = i; else inst->m_instr->AddNext(i); // added a result } return pStack->Return(inst, pStk); }
CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCase* inst = new CBotCase(); // creates the object CBotToken* pp = p; // preserves at the ^ token (starting position) inst->SetToken(p); if (!IsOfType(p, ID_CASE, ID_DEFAULT)) return nullptr; // should never happen if ( pp->GetType() == ID_CASE ) { pp = p; inst->m_Value = CBotExprNum::Compile(p, pStack); if ( inst->m_Value == nullptr ) { pStack->SetError( TX_BADNUM, pp ); delete inst; return nullptr; } } if ( !IsOfType( p, ID_DOTS )) { pStack->SetError( TX_MISDOTS, p->GetStart() ); delete inst; return nullptr; } return inst; }
// compiles a list of parameters CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack) { // mainly not pStack->TokenStack here // declared variables must remain visible thereafter pStack->SetStartError(p->GetStart()); if (IsOfType(p, ID_OPENPAR)) { CBotDefParam* list = NULL; while (!IsOfType(p, ID_CLOSEPAR)) { CBotDefParam* param = new CBotDefParam(); if (list == NULL) list = param; else list->AddNext(param); // added to the list // CBotClass* pClass = NULL;//= CBotClass::Find(p); param->m_typename = p->GetString(); CBotTypResult type = param->m_type = TypeParam(p, pStack); // if ( type == CBotTypPointer ) type = CBotTypClass; // we must create a new object if (param->m_type.GetType() > 0) { CBotToken* pp = p; param->m_token = *p; if (pStack->IsOk() && IsOfType(p, TokenTypVar) ) { // variable already declared? if (pStack->CheckVarLocal(pp)) { pStack->SetError(TX_REDEFVAR, pp); break; } if ( type.Eq(CBotTypArrayPointer) ) type.SetType(CBotTypArrayBody); CBotVar* var = CBotVar::Create(pp->GetString(), type); // creates the variable // if ( pClass ) var->SetClass(pClass); var->SetInit(2); // mark initialized param->m_nIdent = CBotVar::NextUniqNum(); var->SetUniqNum(param->m_nIdent); pStack->AddVar(var); // place on the stack if (IsOfType(p, ID_COMMA) || p->GetType() == ID_CLOSEPAR) continue; } pStack->SetError(TX_CLOSEPAR, p->GetStart()); } pStack->SetError(TX_NOTYP, p); delete list; return NULL; } return list; } pStack->SetError(TX_OPENPAR, p->GetStart()); return NULL; }
CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars) { bool first = true; CBotInstr* ret = nullptr; // to return to the list CBotCStack* pile = pStack; int i = 0; if (IsOfType(p, ID_OPENPAR)) { int start, end; if (!IsOfType(p, ID_CLOSEPAR)) while (true) { start = p->GetStart(); pile = pile->TokenStack(); // keeps the result on the stack if (first) pStack->SetStartError(start); first = false; CBotInstr* param = CBotExpression::Compile(p, pile); end = p->GetStart(); if (!pile->IsOk()) { return pStack->Return(nullptr, pile); } if (ret == nullptr) ret = param; else ret->AddNext(param); // construct the list if (param != nullptr) { if (pile->GetTypResult().Eq(99)) { delete pStack->TokenStack(); pStack->SetError(CBotErrVoid, p->GetStart()); return nullptr; } ppVars[i] = pile->GetVar(); ppVars[i]->GetToken()->SetPos(start, end); i++; if (IsOfType(p, ID_COMMA)) continue; // skips the comma if (IsOfType(p, ID_CLOSEPAR)) break; } pStack->SetError(CBotErrClosePar, p->GetStart()); delete pStack->TokenStack(); return nullptr; } } ppVars[i] = nullptr; return ret; }
CBotTypResult ArrayType(CBotToken* &p, CBotCStack* pile, CBotTypResult type) { while ( IsOfType( p, ID_OPBRK ) ) { if ( !IsOfType( p, ID_CLBRK ) ) { pile->SetError(CBotErrCloseIndex, p->GetStart()); return CBotTypResult( -1 ); } type = CBotTypResult( CBotTypArrayPointer, type ); } return type; }
TEST_F(WorldChunkTests,PlacedCubeIsSaved) { // Set three cubes pChunk->put( CubeData( EMATERIAL_GRASS ), Point( 2, 5, 6 ) ); pChunk->put( CubeData( EMATERIAL_WOOD ), Point( 24, 5, 0 ) ); pChunk->put( CubeData( EMATERIAL_ROCK ), Point( 31, 0, 12 ) ); // Check type, make sure set (in random order) EXPECT_FALSE( IsOfType( pChunk, EMATERIAL_DIRT, Point( 24, 5, 0 ) ) ); EXPECT_TRUE( IsOfType( pChunk, EMATERIAL_WOOD, Point( 24, 5, 0 ) ) ); EXPECT_TRUE( IsOfType( pChunk, EMATERIAL_ROCK, Point( 31, 0, 12 ) ) ); EXPECT_TRUE( IsOfType( pChunk, EMATERIAL_GRASS, Point( 2, 5, 6 ) ) ); }
CBotInstr* CBotStartDebugDD::Compile(CBotToken* &p, CBotCStack* pStack) { if (!IsOfType(p, ID_DEBUGDD)) return nullptr; // should never happen return new CBotStartDebugDD(); // creates the object }
CBotInstr* CBotFor::Compile(CBotToken* &p, CBotCStack* pStack) { CBotFor* inst = new CBotFor(); // creates the object CBotToken* pp = p; // preserves at the ^ token (starting position) if ( IsOfType( p, TokenTypVar ) && IsOfType( p, ID_DOTS ) ) { inst->m_label = pp->GetString(); // register the name of label } inst->SetToken(p); if (!IsOfType(p, ID_FOR)) return nullptr; // should never happen if ( !IsOfType(p, ID_OPENPAR)) // missing parenthesis ? { pStack->SetError(TX_OPENPAR, p->GetStart()); return nullptr; } CBotCStack* pStk = pStack->TokenStack(pp, true); // un petit bout de pile svp // compiles instructions for initialization inst->m_Init = CBotListExpression::Compile( p, pStk ); if ( pStk->IsOk() ) { if ( !IsOfType(p, ID_SEP)) // lack the semicolon? { pStack->SetError(TX_OPENPAR, p->GetStart()); delete inst; return pStack->Return(nullptr, pStk); // no object, the error is on the stack } inst->m_Test = CBotBoolExpr::Compile( p, pStk ); if ( pStk->IsOk() ) { if ( !IsOfType(p, ID_SEP)) // lack the semicolon? { pStack->SetError(TX_OPENPAR, p->GetStart()); delete inst; return pStack->Return(nullptr, pStk); // no object, the error is on the stack } inst->m_Incr = CBotListExpression::Compile( p, pStk ); if ( pStk->IsOk() ) { if ( IsOfType(p, ID_CLOSEPAR)) // missing parenthesis ? { IncLvl(inst->m_label); inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, true ); DecLvl(); if ( pStk->IsOk() ) return pStack->Return(inst, pStk);; } pStack->SetError(TX_CLOSEPAR, p->GetStart()); } } } delete inst; // error, frees up return pStack->Return(nullptr, pStk); // no object, the error is on the stack }
CBotClass* CBotClass::Compile(CBotToken* &p, CBotCStack* pStack) { if ( !IsOfType(p, ID_PUBLIC) ) return NULL; if ( !IsOfType(p, ID_CLASS) ) return NULL; CBotString name = p->GetString(); // a name for the class is there? if (IsOfType(p, TokenTypVar)) { // the class was created by Compile1 CBotClass* pOld = CBotClass::Find(name); if ( IsOfType( p, ID_EXTENDS ) ) { IsOfType(p, TokenTypVar); // necessarily } IsOfType( p, ID_OPBLK); // necessarily while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) ) { pOld->CompileDefItem(p, pStack, true); } pOld->m_IsDef = true; // complete definition if (pStack->IsOk()) return pOld; } pStack->SetError(TX_ENDOF, p); return NULL; }
CBotInstr* CBotIf::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; // conserve le ^au token (début instruction) if (!IsOfType(p, ID_IF)) return NULL; // ne doit jamais arriver CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp CBotIf* inst = new CBotIf(); // crée l'object inst->SetToken( pp ); if ( NULL != (inst->m_Condition = CBotCondition::Compile( p, pStk )) ) { // la condition existe bel et bien inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); if ( pStk->IsOk() ) { // le bloc d'instruction est ok (peut être vide) // regarde si l'instruction suivante est le token "else" if (IsOfType(p, ID_ELSE)) { // si oui, compile le bloc d'instruction qui suit inst->m_BlockElse = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); if (!pStk->IsOk()) { // il n'y a pas de bloc correct après le else // libère l'objet, et transmet l'erreur qui est sur la pile delete inst; return pStack->Return(NULL, pStk); } } // rend l'object correct à qui le demande. return pStack->Return(inst, pStk); } } // erreur, libère l'objet delete inst; // et transmet l'erreur qui se trouve sur la pile. return pStack->Return(NULL, pStk); }
CBotInstr* CBotIf::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; // preserves at the ^ token (starting instruction) if (!IsOfType(p, ID_IF)) return nullptr; // should never happen CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp CBotIf* inst = new CBotIf(); // create the object inst->SetToken( pp ); if ( nullptr != (inst->m_condition = CBotCondition::Compile(p, pStk )) ) { // the condition does exist inst->m_block = CBotBlock::CompileBlkOrInst(p, pStk, true ); if ( pStk->IsOk() ) { // the statement block is ok (can be empty) // see if the next instruction is the token "else" if (IsOfType(p, ID_ELSE)) { // if so, compiles the following statement block inst->m_blockElse = CBotBlock::CompileBlkOrInst(p, pStk, true ); if (!pStk->IsOk()) { // there is no correct block after the else // frees the object, and transmits the error that is on the stack delete inst; return pStack->Return(nullptr, pStk); } } // return the corrent object to the application return pStack->Return(inst, pStk); } } // error, frees the object delete inst; // and transmits the error that is on the stack. return pStack->Return(nullptr, pStk); }
CBotInstr* CBotDo::Compile(CBotToken* &p, CBotCStack* pStack) { CBotDo* inst = new CBotDo(); // creates the object CBotToken* pp = p; // preserves at the ^ token (starting position) if ( IsOfType( p, TokenTypVar ) && IsOfType( p, ID_DOTS ) ) { inst->m_label = pp->GetString(); // register the name of label } inst->SetToken(p); if (!IsOfType(p, ID_DO)) return nullptr; // should never happen CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp // looking for a statement block after the do IncLvl(inst->m_label); inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, true ); DecLvl(); if ( pStk->IsOk() ) { if (IsOfType(p, ID_WHILE)) { if ( nullptr != (inst->m_Condition = CBotCondition::Compile( p, pStk )) ) { // the condition exists if (IsOfType(p, ID_SEP)) { return pStack->Return(inst, pStk); // return an object to the application } pStk->SetError(TX_ENDOF, p->GetStart()); } } pStk->SetError(TX_WHILE, p->GetStart()); } delete inst; // error, frees up return pStack->Return(nullptr, pStk); // no object, the error is on the stack }
CBotInstr* CBotBlock::Compile(CBotToken* &p, CBotCStack* pStack, bool bLocal) { pStack->SetStartError(p->GetStart()); if (IsOfType(p, ID_OPBLK)) { CBotInstr* inst = CBotListInstr::Compile(p, pStack, bLocal); if (IsOfType(p, ID_CLBLK)) { return inst; } pStack->SetError(CBotErrCloseBlock, p->GetStart()); // missing parenthesis delete inst; return nullptr; } pStack->SetError(CBotErrOpenBlock, p->GetStart()); return nullptr; }
CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack) { if ( !IsOfType(p, ID_PUBLIC) ) { pStack->SetError(TX_NOPUBLIC, p); return NULL; } if ( !IsOfType(p, ID_CLASS) ) return NULL; CBotString name = p->GetString(); CBotClass* pOld = CBotClass::Find(name); if ( pOld != NULL && pOld->m_IsDef ) { pStack->SetError( TX_REDEFCLASS, p ); return NULL; } // a name of the class is there? if (IsOfType(p, TokenTypVar)) { CBotClass* pPapa = NULL; if ( IsOfType( p, ID_EXTENDS ) ) { CBotString name = p->GetString(); pPapa = CBotClass::Find(name); if (!IsOfType(p, TokenTypVar) || pPapa == NULL ) { pStack->SetError( TX_NOCLASS, p ); return NULL; } } CBotClass* classe = (pOld == NULL) ? new CBotClass(name, pPapa) : pOld; classe->Purge(); // emptythe old definitions classe->m_IsDef = false; // current definition if ( !IsOfType( p, ID_OPBLK) ) { pStack->SetError(TX_OPENBLK, p); return NULL; } while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) ) { classe->CompileDefItem(p, pStack, false); } if (pStack->IsOk()) return classe; } pStack->SetError(TX_ENDOF, p); return NULL; }
CBotInstr* CBotWhile::Compile(CBotToken* &p, CBotCStack* pStack) { CBotWhile* inst = new CBotWhile(); // creates the object CBotToken* pp = p; // preserves at the ^ token (starting position) if ( IsOfType( p, TokenTypVar ) && IsOfType( p, ID_DOTS ) ) { inst->m_label = pp->GetString(); // records the name of the label } inst->SetToken(p); if (!IsOfType(p, ID_WHILE)) return nullptr; // should never happen CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp // a bit of battery please (??) if ( nullptr != (inst->m_condition = CBotCondition::Compile(p, pStk )) ) { // the condition exists IncLvl(inst->m_label); inst->m_block = CBotBlock::CompileBlkOrInst(p, pStk, true ); DecLvl(); if ( pStk->IsOk() ) { // the statement block is ok (it may be empty! return pStack->Return(inst, pStk); // return an object to the application // makes the object to which the application } } delete inst; // error, frees the place return pStack->Return(nullptr, pStk); // no object, the error is on the stack }
CBotInstr* CBotBreak::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; // preserves at the ^ token (starting position) int type = p->GetType(); if (!IsOfType(p, ID_BREAK, ID_CONTINUE)) return nullptr; // should never happen if ( !ChkLvl(CBotString(), type ) ) { pStack->SetError(TX_BREAK, pp); return nullptr; // no object, the error is on the stack } CBotBreak* inst = new CBotBreak(); // creates the object inst->SetToken(pp); // keeps the operation pp = p; if ( IsOfType( p, TokenTypVar ) ) { inst->m_label = pp->GetString(); // register the name of label if ( !ChkLvl(inst->m_label, type ) ) { delete inst; pStack->SetError(TX_NOLABEL, pp); return nullptr; // no object, the error is on the stack } } if (IsOfType(p, ID_SEP)) { return inst; // return what it wants } delete inst; pStack->SetError(TX_ENDOF, p->GetStart()); return nullptr; // no object, the error is on the stack }
CBotInstr* CBotThrow::Compile(CBotToken* &p, CBotCStack* pStack) { pStack->SetStartError(p->GetStart()); CBotThrow* inst = new CBotThrow(); // creates the object inst->SetToken(p); CBotToken* pp = p; // preserves at the ^ token (starting position) if (!IsOfType(p, ID_THROW)) return nullptr; // should never happen inst->m_Value = CBotExpression::Compile( p, pStack ); if (pStack->GetType() < CBotTypLong && pStack->IsOk()) { return inst; // return an object to the application } pStack->SetError(TX_BADTYPE, pp); delete inst; // error, frees up return nullptr; // no object, the error is on the stack }
CBotInstr* CBotListExpression::Compile(CBotToken* &p, CBotCStack* pStack) { CBotListExpression* inst = new CBotListExpression(); inst->m_Expr = CompileInstrOrDefVar( p, pStack ); // compile the first expression in a list if (pStack->IsOk()) { while ( IsOfType(p, ID_COMMA) ) // more instructions? { CBotInstr* i = CompileInstrOrDefVar( p, pStack ); // Is this a declaration of an integer? inst->m_Expr->AddNext(i); // added after if ( !pStack->IsOk() ) { delete inst; return nullptr; // no object, the error is on the stack } } return inst; } delete inst; return nullptr; }
bool NzLuaInstance::IsOfType(int index, const NzString& tname) const { return IsOfType(index, tname.GetConstBuffer()); }
bool LuaState::IsOfType(int index, const String& tname) const { return IsOfType(index, tname.GetConstBuffer()); }
CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) { CBotToken* pp = p; if (!IsOfType(p, ID_NEW)) return nullptr; // verifies that the token is a class name if (p->GetType() != TokenTypVar) { pStack->SetError(CBotErrBadNew, p); return nullptr; } CBotClass* pClass = CBotClass::Find(p); if (pClass == nullptr) { pStack->SetError(CBotErrBadNew, p); return nullptr; } CBotNew* inst = new CBotNew(); inst->SetToken(pp); inst->m_vartoken = *p; p = p->GetNext(); // creates the object on the stack // with a pointer to the object CBotVar* pVar = CBotVar::Create("", pClass); // do the call of the creator CBotCStack* pStk = pStack->TokenStack(); { // check if there are parameters CBotVar* ppVars[1000]; inst->m_parameters = CompileParams(p, pStk, ppVars); if (!pStk->IsOk()) goto error; // constructor exist? CBotTypResult r = pClass->CompileMethode(pClass->GetName(), pVar, ppVars, pStk, inst->m_nMethodeIdent); delete pStk->TokenStack(); // release extra stack int typ = r.GetType(); // if there is no constructor, and no parameters either, it's ok if (typ == CBotErrUndefCall && inst->m_parameters == nullptr) typ = 0; pVar->SetInit(CBotVar::InitType::DEF); // mark the instance as init if (typ>20) { pStk->SetError(static_cast<CBotError>(typ), inst->m_vartoken.GetEnd()); goto error; } // if the constructor does not exist, but there are parameters if (typ<0 && inst->m_parameters != nullptr) { pStk->SetError(CBotErrNoConstruct, &inst->m_vartoken); goto error; } // makes pointer to the object on the stack pStk->SetVar(pVar); pp = p; // chained method ? if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true))) { inst->m_exprRetVar->SetToken(pp); delete pStk->TokenStack(); } if (pStack->IsOk()) return pStack->Return(inst, pStk); } error: delete inst; return pStack->Return(nullptr, pStk); }
bool CBotProgram::Compile(const std::string& program, std::vector<std::string>& externFunctions, void* pUser) { // Cleanup the previously compiled program Stop(); for (CBotClass* c : m_classes) c->Purge(); // purge the old definitions of classes // but without destroying the object m_classes.clear(); for (CBotFunction* f : m_functions) delete f; m_functions.clear(); externFunctions.clear(); m_error = CBotNoErr; // Step 1. Process the code into tokens auto tokens = CBotToken::CompileTokens(program); if (tokens == nullptr) return false; auto pStack = std::unique_ptr<CBotCStack>(new CBotCStack(nullptr)); CBotToken* p = tokens.get()->GetNext(); // skips the first token (separator) pStack->SetProgram(this); // defined used routines m_externalCalls->SetUserPtr(pUser); // Step 2. Find all function and class definitions while ( pStack->IsOk() && p != nullptr && p->GetType() != 0) { if ( IsOfType(p, ID_SEP) ) continue; // semicolons lurking if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { m_classes.push_back(CBotClass::Compile1(p, pStack.get())); } else { m_functions.push_back(CBotFunction::Compile1(p, pStack.get(), nullptr)); } } // Define fields and pre-compile methods for each class in this program if (pStack->IsOk()) CBotClass::DefineClasses(m_classes, pStack.get()); if ( !pStack->IsOk() ) { m_error = pStack->GetError(m_errorStart, m_errorEnd); for (CBotFunction* f : m_functions) delete f; m_functions.clear(); return false; } // Step 3. Real compilation std::list<CBotFunction*>::iterator next = m_functions.begin(); p = tokens.get()->GetNext(); // returns to the beginning while ( pStack->IsOk() && p != nullptr && p->GetType() != 0 ) { if ( IsOfType(p, ID_SEP) ) continue; // semicolons lurking if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { CBotClass::Compile(p, pStack.get()); // completes the definition of the class } else { CBotFunction::Compile(p, pStack.get(), *next); if ((*next)->IsExtern()) externFunctions.push_back((*next)->GetName()/* + next->GetParams()*/); if ((*next)->IsPublic()) CBotFunction::AddPublic(*next); (*next)->m_pProg = this; // keeps pointers to the module ++next; } } if ( !pStack->IsOk() ) { m_error = pStack->GetError(m_errorStart, m_errorEnd); for (CBotFunction* f : m_functions) delete f; m_functions.clear(); } return !m_functions.empty(); }
CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type) { CBotCStack* pStk = pStack->TokenStack(p); CBotToken* pp = p; if (IsOfType( p, ID_NULL ) || (IsOfType(p, ID_OPBLK) && IsOfType(p, ID_CLBLK))) { CBotInstr* inst = new CBotExprLitNull(); inst->SetToken(pp); return pStack->Return(inst, pStk); // ok with empty element } p = pp; CBotListArray* inst = new CBotListArray(); if (IsOfType( p, ID_OPBLK )) { // each element takes the one after the other if (type.Eq( CBotTypArrayPointer )) { pStk->SetStartError(p->GetStart()); if (nullptr == ( inst->m_expr = CBotListArray::Compile( p, pStk, type.GetTypElem() ) )) { if (pStk->IsOk()) { inst->m_expr = CBotTwoOpExpr::Compile(p, pStk); if (inst->m_expr == nullptr || !pStk->GetTypResult().Compare(type)) // compatible type ? { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; } } } while (IsOfType( p, ID_COMMA )) // other elements? { pStk->SetStartError(p->GetStart()); CBotInstr* i = nullptr; if (nullptr == ( i = CBotListArray::Compile(p, pStk, type.GetTypElem() ) )) { if (pStk->IsOk()) { i = CBotTwoOpExpr::Compile(p, pStk); if (i == nullptr || !pStk->GetTypResult().Compare(type)) // compatible type ? { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; } } } inst->m_expr->AddNext3b(i); if ( p->GetType() == ID_COMMA ) continue; if ( p->GetType() == ID_CLBLK ) break; pStk->SetError(CBotErrClosePar, p); goto error; } } else { pStk->SetStartError(p->GetStart()); if (nullptr == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk ))) { goto error; } CBotTypResult valType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); if (!TypeCompatible(valType, type, ID_ASS) ) { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; } while (IsOfType( p, ID_COMMA )) // other elements? { pStk->SetStartError(p->GetStart()); CBotInstr* i = CBotTwoOpExpr::Compile(p, pStk) ; if (nullptr == i) { goto error; } CBotTypResult valType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); if (!TypeCompatible(valType, type, ID_ASS) ) { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; } inst->m_expr->AddNext3b(i); if (p->GetType() == ID_COMMA) continue; if (p->GetType() == ID_CLBLK) break; pStk->SetError(CBotErrClosePar, p); goto error; } } if (!IsOfType(p, ID_CLBLK) ) { pStk->SetError(CBotErrClosePar, p->GetStart()); goto error; } return pStack->Return(inst, pStk); } error: delete inst; return pStack->Return(nullptr, pStk); }
CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations) { int typeMask; if ( pOperations == nullptr ) pOperations = ListOp; int* pOp = pOperations; while ( *pOp++ != 0 ); // follows the table CBotCStack* pStk = pStack->TokenStack(); // one end of stack please // search the intructions that may be suitable to the left of the operation CBotInstr* left = (*pOp == 0) ? CBotParExpr::Compile( p, pStk ) : // expression (...) left CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B left if (left == nullptr) return pStack->Return(nullptr, pStk); // if error, transmit // did we expected the operand? int typeOp = p->GetType(); if ( IsInList(typeOp, pOperations, typeMask) ) { CBotTypResult type1, type2; type1 = pStk->GetTypResult(); // what kind of the first operand? if (typeOp == ID_LOGIC) // special case provided for: ? op1: op2; { if ( !type1.Eq(CBotTypBoolean) ) { pStk->SetError( CBotErrBadType1, p); return pStack->Return(nullptr, pStk); } CBotLogicExpr* inst = new CBotLogicExpr(); inst->m_condition = left; p = p->GetNext(); // skip the token of the operation inst->m_op1 = CBotExpression::Compile(p, pStk); CBotToken* pp = p; if ( inst->m_op1 == nullptr || !IsOfType( p, ID_DOTS ) ) { pStk->SetError( CBotErrNoDoubleDots, p->GetStart()); delete inst; return pStack->Return(nullptr, pStk); } type1 = pStk->GetTypResult(); inst->m_op2 = CBotExpression::Compile(p, pStk); if ( inst->m_op2 == nullptr ) { pStk->SetError( CBotErrNoTerminator, p->GetStart() ); delete inst; return pStack->Return(nullptr, pStk); } type2 = pStk->GetTypResult(); if (!TypeCompatible(type1, type2)) { pStk->SetError( CBotErrBadType2, pp ); delete inst; return pStack->Return(nullptr, pStk); } pStk->SetType(type1); // the greatest of 2 types return pStack->Return(inst, pStk); } CBotTwoOpExpr* inst = new CBotTwoOpExpr(); // element for operation inst->SetToken(p); // stores the operation p = p->GetNext(); // skip the token of the operation // looking statements that may be suitable for right if ( nullptr != (inst->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp )) ) // expression (...) right { // there is an second operand acceptable type2 = pStk->GetTypResult(); // what kind of results? if ( type1.Eq(99) || type2.Eq(99) ) // operand is void { pStack->SetError(CBotErrBadType2, &inst->m_token); delete inst; return nullptr; } // what kind of result? int TypeRes = std::max( type1.GetType(CBotTypResult::GetTypeMode::NULL_AS_POINTER), type2.GetType(CBotTypResult::GetTypeMode::NULL_AS_POINTER) ); if (typeOp == ID_ADD && type1.Eq(CBotTypString)) { TypeRes = CBotTypString; type2 = type1; // any type convertible chain } else if (typeOp == ID_ADD && type2.Eq(CBotTypString)) { TypeRes = CBotTypString; type1 = type2; // any type convertible chain } else if (!TypeOk(TypeRes, typeMask)) type1.SetType(99);// error of type switch (typeOp) { case ID_LOG_OR: case ID_LOG_AND: case ID_TXT_OR: case ID_TXT_AND: case ID_EQ: case ID_NE: case ID_HI: case ID_LO: case ID_HS: case ID_LS: TypeRes = CBotTypBoolean; } if ( TypeCompatible (type1, type2, typeOp) ) // the results are compatible { // ok so, saves the operand in the object inst->m_leftop = left; // special for evaluation of the operations of the same level from left to right while ( IsInList(p->GetType(), pOperations, typeMask) ) // same operation(s) follows? { typeOp = p->GetType(); CBotTwoOpExpr* i = new CBotTwoOpExpr(); // element for operation i->SetToken(p); // stores the operation i->m_leftop = inst; // left operand type1 = TypeRes; p = p->GetNext(); // advance after i->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp ); type2 = pStk->GetTypResult(); if ( !TypeCompatible (type1, type2, typeOp) ) // the results are compatible { pStk->SetError(CBotErrBadType2, &i->m_token); delete i; return pStack->Return(nullptr, pStk); } if ( TypeRes != CBotTypString ) // keep string conversion TypeRes = std::max(type1.GetType(), type2.GetType()); inst = i; } CBotTypResult t(type1); t.SetType(TypeRes); // is a variable on the stack for the type of result pStk->SetVar(CBotVar::Create("", t)); // and returns the requested object return pStack->Return(inst, pStk); } pStk->SetError(CBotErrBadType2, &inst->m_token); } // in case of error, releases the elements delete left; delete inst; // and transmits the error to the stack return pStack->Return(nullptr, pStk); } // if we are not dealing with an operation + or - // goes to that requested, the operand (left) found // instead of the object "addition" return pStack->Return(left, pStk); }
CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(p->GetStart()); // is it an expression in parentheses? if (IsOfType(p, ID_OPENPAR)) { CBotInstr* inst = CBotExpression::Compile(p, pStk); if (nullptr != inst) { if (IsOfType(p, ID_CLOSEPAR)) { return pStack->Return(inst, pStk); } pStk->SetError(CBotErrClosePar, p->GetStart()); } delete inst; return pStack->Return(nullptr, pStk); } // is this a unary operation? CBotInstr* inst = CBotExprUnaire::Compile(p, pStk); if (inst != nullptr || !pStk->IsOk()) return pStack->Return(inst, pStk); // is it a variable name? if (p->GetType() == TokenTypVar) { // this may be a method call without the "this." before inst = CBotExprVar::CompileMethode(p, pStk); if (inst != nullptr) return pStack->Return(inst, pStk); // is it a procedure call? inst = CBotInstrCall::Compile(p, pStk); if (inst != nullptr || !pStk->IsOk()) return pStack->Return(inst, pStk); CBotToken* pvar = p; // no, it an "ordinaty" variable inst = CBotExprVar::Compile(p, pStk); CBotToken* pp = p; // post incremented or decremented? if (IsOfType(p, ID_INC, ID_DEC)) { if (pStk->GetType() >= CBotTypBoolean) { pStk->SetError(CBotErrBadType1, pp); delete inst; return pStack->Return(nullptr, pStk); } // recompile the variable for read-only delete inst; p = pvar; inst = CBotExprVar::Compile(p, pStk, CBotVar::ProtectionLevel::ReadOnly); p = p->GetNext(); CBotPostIncExpr* i = new CBotPostIncExpr(); i->SetToken(pp); i->m_instr = inst; // associated statement return pStack->Return(i, pStk); } return pStack->Return(inst, pStk); } // pre increpemted or pre decremented? CBotToken* pp = p; if (IsOfType(p, ID_INC, ID_DEC)) { CBotPreIncExpr* i = new CBotPreIncExpr(); i->SetToken(pp); if (p->GetType() == TokenTypVar) { if (nullptr != (i->m_instr = CBotExprVar::Compile(p, pStk, CBotVar::ProtectionLevel::ReadOnly))) { if (pStk->GetType() >= CBotTypBoolean) { pStk->SetError(CBotErrBadType1, pp); delete inst; return pStack->Return(nullptr, pStk); } return pStack->Return(i, pStk); } delete i; return pStack->Return(nullptr, pStk); } } // is it a number or DefineNum? if (p->GetType() == TokenTypNum || p->GetType() == TokenTypDef ) { CBotInstr* inst = CBotExprLitNum::Compile(p, pStk); return pStack->Return(inst, pStk); } // is this a chaine? if (p->GetType() == TokenTypString) { CBotInstr* inst = CBotExprLitString::Compile(p, pStk); return pStack->Return(inst, pStk); } // is a "true" or "false" if (p->GetType() == ID_TRUE || p->GetType() == ID_FALSE ) { CBotInstr* inst = CBotExprLitBool::Compile(p, pStk); return pStack->Return(inst, pStk); } // is an object to be created with new if (p->GetType() == ID_NEW) { CBotInstr* inst = CBotNew::Compile(p, pStk); return pStack->Return(inst, pStk); } // is a null pointer if (IsOfType(p, ID_NULL)) { CBotInstr* inst = new CBotExprLitNull(); inst->SetToken(pp); CBotVar* var = CBotVar::Create("", CBotTypNullPointer); pStk->SetVar(var); return pStack->Return(inst, pStk); } // is a number nan if (IsOfType(p, ID_NAN)) { CBotInstr* inst = new CBotExprLitNan(); inst->SetToken(pp); CBotVar* var = CBotVar::Create("", CBotTypInt); var->SetInit(CBotVar::InitType::IS_NAN); pStk->SetVar(var); return pStack->Return(inst, pStk); } return pStack->Return(nullptr, pStk); }
CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); pStk->SetStartError(p->GetStart()); // is it a variable name? if (p->GetType() == TokenTypVar) { CBotLeftExpr* inst = new CBotLeftExpr(); // creates the object inst->SetToken(p); CBotVar* var; if (nullptr != (var = pStk->FindVar(p))) // seek if known variable { inst->m_nIdent = var->GetUniqNum(); if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) { if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, p); goto err; } // this is an element of the current class // adds the equivalent of this. before CBotToken pthis("this"); inst->SetToken(&pthis); inst->m_nIdent = -2; // indent for this CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(p); // keeps the name of the token inst->AddNext3(i); // add after var = pStk->FindVar(pthis); var = var->GetItem(p->GetString()); i->SetUniqNum(var->GetUniqNum()); } p = p->GetNext(); // next token while (true) { if (var->GetType() == CBotTypArrayPointer) { if (IsOfType( p, ID_OPBRK )) { CBotIndexExpr* i = new CBotIndexExpr(); i->m_expr = CBotExpression::Compile(p, pStk); inst->AddNext3(i); // add to the chain var = (static_cast<CBotVarArray*>(var))->GetItem(0,true); // gets the component [0] if (i->m_expr == nullptr) { pStk->SetError(CBotErrBadIndex, p->GetStart()); goto err; } if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK )) { pStk->SetError(CBotErrCloseIndex, p->GetStart()); goto err; } continue; } } if (var->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypPointer) // for classes { if (IsOfType(p, ID_DOT)) { CBotToken* pp = p; CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(pp); // keeps the name of the token inst->AddNext3(i); // adds after if (p->GetType() == TokenTypVar) // must be a name { CBotVar* preVar = var; var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, pp); goto err; } i->SetUniqNum(var->GetUniqNum()); p = p->GetNext(); // skips the name continue; } pStk->SetError(CBotErrUndefItem, p); } pStk->SetError(CBotErrUndefClass, p->GetStart()); goto err; } } break; } if (pStk->IsOk()) return static_cast<CBotLeftExpr*> (pStack->Return(inst, pStk)); } pStk->SetError(CBotErrUndefVar, p); err: delete inst; return static_cast<CBotLeftExpr*> ( pStack->Return(nullptr, pStk)); } return static_cast<CBotLeftExpr*> ( pStack->Return(nullptr, pStk)); }
CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly) { if (p->GetType() == ID_DOT) { CBotVar* var = pStack->GetVar(); if (var == nullptr) return nullptr; CBotCStack* pStk = pStack->TokenStack(); CBotInstr* inst = new CBotExprRetVar(); while (true) { pStk->SetStartError(p->GetStart()); if (var->GetType() == CBotTypArrayPointer) { if (bMethodsOnly) goto err; if (IsOfType( p, ID_OPBRK )) { CBotIndexExpr* i = new CBotIndexExpr(); i->m_expr = CBotExpression::Compile(p, pStk); inst->AddNext3(i); var = var->GetItem(0,true); if (i->m_expr == nullptr || pStk->GetType() != CBotTypInt) { pStk->SetError(CBotErrBadIndex, p->GetStart()); goto err; } if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK )) { pStk->SetError(CBotErrCloseIndex, p->GetStart()); goto err; } continue; } } if (var->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypPointer) { if (IsOfType(p, ID_DOT)) { CBotToken* pp = p; if (p->GetType() == TokenTypVar) { if (p->GetNext()->GetType() == ID_OPENPAR) { CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var, bMethodsOnly); if (!pStk->IsOk()) goto err; inst->AddNext3(i); return pStack->Return(inst, pStk); } else if (bMethodsOnly) { p = p->GetPrev(); goto err; } else { CBotFieldExpr* i = new CBotFieldExpr(); i->SetToken(pp); inst->AddNext3(i); CBotVar* preVar = var; var = var->GetItem(p->GetString()); if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var)) { pStk->SetError(CBotErrPrivate, pp); goto err; } } } if (var != nullptr) { p = p->GetNext(); continue; } pStk->SetError(CBotErrUndefItem, p); goto err; } pStk->SetError(CBotErrUndefClass, p); goto err; } } break; } pStk->SetCopyVar(var); if (pStk->IsOk()) return pStack->Return(inst, pStk); pStk->SetError(CBotErrUndefVar, p); err: delete inst; return pStack->Return(nullptr, pStk); } return nullptr; }