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; }
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* CBotClassInst::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* pClass) { // seeks the corresponding classes if ( pClass == NULL ) { pStack->SetStartError(p->GetStart()); pClass = CBotClass::Find(p); if ( pClass == NULL ) { // not found? is bizare pStack->SetError(TX_NOCLASS, p); return NULL; } p = p->GetNext(); } bool bIntrinsic = pClass->IsIntrinsic(); CBotTypResult type = CBotTypResult( bIntrinsic ? CBotTypIntrinsic : CBotTypPointer, pClass ); CBotClassInst* inst = static_cast<CBotClassInst*>(CompileArray(p, pStack, type)); if ( inst != NULL || !pStack->IsOk() ) return inst; CBotCStack* pStk = pStack->TokenStack(); inst = new CBotClassInst(); /// \TODO Need to be revised and fixed after adding unit tests CBotToken token(pClass->GetName(), CBotString(), p->GetStart(), p->GetEnd()); inst->SetToken(&token); CBotToken* vartoken = p; if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) { (static_cast<CBotLeftExprVar*>(inst->m_var))->m_typevar = type; if (pStk->CheckVarLocal(vartoken)) // redefinition of the variable { pStk->SetStartError(vartoken->GetStart()); pStk->SetError(TX_REDEFVAR, vartoken->GetEnd()); goto error; } if (IsOfType(p, ID_OPBRK)) // with any clues? { delete inst; // is not type CBotInt p = vartoken; // returns to the variable name // compiles declaration an array inst = static_cast<CBotClassInst*>(CBotInstArray::Compile( p, pStk, type )); if (!pStk->IsOk() ) { pStk->SetError(TX_CLBRK, p->GetStart()); goto error; } goto suite; // no assignment, variable already created } CBotVar* var; var = CBotVar::Create(vartoken->GetString(), type); // creates the instance // var->SetClass(pClass); var->SetUniqNum( (static_cast<CBotLeftExprVar*>(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); // its attribute a unique number pStack->AddVar(var); // placed on the stack // look if there are parameters inst->m_hasParams = (p->GetType() == ID_OPENPAR); CBotVar* ppVars[1000]; inst->m_Parameters = CompileParams(p, pStk, ppVars); if ( !pStk->IsOk() ) goto error; // if there are parameters, is the equivalent to the stament "new" // CPoint A ( 0, 0 ) is equivalent to // CPoint A = new CPoint( 0, 0 ) // if ( NULL != inst->m_Parameters ) if ( inst->m_hasParams ) { // the constructor is there? // CBotString noname; CBotTypResult r = pClass->CompileMethode(pClass->GetName(), var, ppVars, pStk, inst->m_nMethodeIdent); delete pStk->TokenStack(); // releases the supplement stack int typ = r.GetType(); if (typ == TX_UNDEFCALL) { // si le constructeur n'existe pas if (inst->m_Parameters != NULL) // with parameters { pStk->SetError(TX_NOCONST, vartoken); goto error; } typ = 0; } if (typ>20) { pStk->SetError(typ, vartoken->GetEnd()); goto error; } } if (IsOfType(p, ID_ASS)) // with a assignment? { if (inst->m_hasParams) { pStk->SetError(TX_ENDOF, p->GetStart()); goto error; } if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) { goto error; } CBotClass* result = pStk->GetClass(); if ( !pStk->GetTypResult(1).Eq(CBotTypNullPointer) && ( !pStk->GetTypResult(1).Eq(CBotTypPointer) || ( result != NULL && !pClass->IsChildOf(result) ))) // type compatible ? { pStk->SetError(TX_BADTYPE, p->GetStart()); goto error; } // if ( !bIntrinsic ) var->SetPointer(pStk->GetVar()->GetPointer()); if ( !bIntrinsic ) { // does not use the result on the stack, to impose the class CBotVar* pvar = CBotVar::Create("", pClass); var->SetPointer( pvar ); // variable already declared instance pointer delete pvar; // removes the second pointer } var->SetInit(true); // marks the pointer as init } else if (inst->m_hasParams) { // creates the object on the "job" (\TODO "tas") // with a pointer to the object if ( !bIntrinsic ) { CBotVar* pvar = CBotVar::Create("", pClass); var->SetPointer( pvar ); // variable already declared instance pointer delete pvar; // removes the second pointer } var->SetInit(2); // marks the pointer as init } suite: if (IsOfType(p, ID_COMMA)) // several chained definitions { if ( NULL != ( inst->m_next = CBotClassInst::Compile(p, pStk, pClass) )) // compiles the following { return pStack->Return(inst, pStk); } } if (IsOfType(p, ID_SEP)) // complete instruction { return pStack->Return(inst, pStk); } pStk->SetError(TX_ENDOF, p->GetStart()); } error: delete inst; return pStack->Return(NULL, pStk); }
CBotInstr* CBotDefArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type) { CBotCStack* pStk = pStack->TokenStack(p); CBotDefArray* inst = new CBotDefArray(); CBotToken* vartoken = p; inst->SetToken(vartoken); // determinse the expression is valid for the item on the left side if (nullptr != (inst->m_var = CBotLeftExprVar::Compile( p, pStk ))) { if (pStk->CheckVarLocal(vartoken)) // redefinition of the variable? { pStk->SetError(CBotErrRedefVar, vartoken); goto error; } CBotInstr* i; while (IsOfType(p, ID_OPBRK)) { pStk->SetStartError(p->GetStart()); if (p->GetType() != ID_CLBRK) { i = CBotExpression::Compile(p, pStk); // expression for the value if (i == nullptr || pStk->GetType() != CBotTypInt) // must be a number { pStk->SetError(CBotErrBadIndex, p->GetStart()); goto error; } } else i = new CBotEmpty(); // if no special formula inst->AddNext3b(i); // construct a list type = CBotTypResult(CBotTypArrayPointer, type); if (IsOfType(p, ID_CLBRK)) continue; pStk->SetError(CBotErrCloseIndex, p->GetStart()); goto error; } CBotVar* var = CBotVar::Create(*vartoken, type); // create an instance inst->m_typevar = type; var->SetUniqNum( (static_cast<CBotLeftExprVar*>(inst->m_var))->m_nIdent = CBotVar::NextUniqNum()); pStack->AddVar(var); // place it on the stack if (IsOfType(p, ID_ASS)) // with an assignment { pStk->SetStartError(p->GetStart()); if ( IsOfType(p, ID_SEP) ) { pStk->SetError(CBotErrNoExpression, p->GetPrev()); goto error; } if ( nullptr == (inst->m_listass = CBotListArray::Compile(p, pStk, type.GetTypElem())) ) { if (pStk->IsOk()) { inst->m_listass = CBotTwoOpExpr::Compile(p, pStk); if (inst->m_listass == nullptr || !pStk->GetTypResult().Compare(type)) // compatible type ? { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; } } } if (pStk->IsOk()) while (true) // mark initialized { var = var->GetItem(0, true); if (var == nullptr) break; if (var->GetType() == CBotTypArrayPointer) continue; if (var->GetType() <= CBotTypString) var->SetInit(CBotVar::InitType::DEF); break; } } if (pStk->IsOk()) return pStack->Return(inst, pStk); } error: delete inst; return pStack->Return(nullptr, pStk); }
CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) { CBotVar* ppVars[1000]; int i = 0; CBotToken* pp = p; p = p->GetNext(); pStack->SetStartError(p->GetStart()); CBotCStack* pile = pStack; if ( IsOfType(p, ID_OPENPAR) ) { int start, end; CBotInstrCall* inst = new CBotInstrCall(); inst->SetToken(pp); // compile la list of parameters if (!IsOfType(p, ID_CLOSEPAR)) while (true) { start = p->GetStart(); pile = pile->TokenStack(); // keeps the results on the stack CBotInstr* param = CBotExpression::Compile(p, pile); end = p->GetStart(); if ( inst->m_Parameters == NULL ) inst->m_Parameters = param; else inst->m_Parameters->AddNext(param); // constructs the list if ( !pile->IsOk() ) { delete inst; return pStack->Return(NULL, pile); } if ( param != NULL ) { if ( pile->GetTypResult().Eq(99) ) { delete pStack->TokenStack(); pStack->SetError(TX_VOID, p->GetStart()); delete inst; return NULL; } 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(TX_CLOSEPAR, p->GetStart()); delete pStack->TokenStack(); delete inst; return NULL; } ppVars[i] = NULL; // the routine is known? // CBotClass* pClass = NULL; inst->m_typRes = pStack->CompileCall(pp, ppVars, inst->m_nFuncIdent); if ( inst->m_typRes.GetType() >= 20 ) { // if (pVar2!=NULL) pp = pVar2->RetToken(); pStack->SetError( inst->m_typRes.GetType(), pp ); delete pStack->TokenStack(); delete inst; return NULL; } delete pStack->TokenStack(); if ( inst->m_typRes.GetType() > 0 ) { CBotVar* pRes = CBotVar::Create("", inst->m_typRes); pStack->SetVar(pRes); // for knowing the type of the result } else pStack->SetVar(NULL); // routine returns void return inst; } p = pp; delete pStack->TokenStack(); return NULL; }