1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 #include "CBot/CBotInstr/CBotInstr.h"
21 
22 #include "CBot/CBotInstr/CBotBreak.h"
23 #include "CBot/CBotInstr/CBotDefArray.h"
24 #include "CBot/CBotInstr/CBotDefBoolean.h"
25 #include "CBot/CBotInstr/CBotDefClass.h"
26 #include "CBot/CBotInstr/CBotDefFloat.h"
27 #include "CBot/CBotInstr/CBotDefInt.h"
28 #include "CBot/CBotInstr/CBotDefString.h"
29 #include "CBot/CBotInstr/CBotDo.h"
30 #include "CBot/CBotInstr/CBotExpression.h"
31 #include "CBot/CBotInstr/CBotFor.h"
32 #include "CBot/CBotInstr/CBotIf.h"
33 #include "CBot/CBotInstr/CBotRepeat.h"
34 #include "CBot/CBotInstr/CBotReturn.h"
35 #include "CBot/CBotInstr/CBotSwitch.h"
36 #include "CBot/CBotInstr/CBotThrow.h"
37 #include "CBot/CBotInstr/CBotTry.h"
38 #include "CBot/CBotInstr/CBotWhile.h"
39 
40 #include "CBot/CBotVar/CBotVar.h"
41 
42 #include "CBot/CBotClass.h"
43 #include "CBot/CBotStack.h"
44 
45 #include <cassert>
46 
47 namespace CBot
48 {
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 int CBotInstr::m_LoopLvl = 0;
52 std::vector<std::string> CBotInstr::m_labelLvl = std::vector<std::string>();
53 
54 ////////////////////////////////////////////////////////////////////////////////
CBotInstr()55 CBotInstr::CBotInstr()
56 {
57     m_next   = nullptr;
58     m_next2b = nullptr;
59     m_next3  = nullptr;
60     m_next3b = nullptr;
61 }
62 
63 ////////////////////////////////////////////////////////////////////////////////
~CBotInstr()64 CBotInstr::~CBotInstr()
65 {
66     delete m_next;
67     delete m_next2b;
68     delete m_next3;
69     delete m_next3b;
70 }
71 
72 ////////////////////////////////////////////////////////////////////////////////
IncLvl(std::string & label)73 void CBotInstr::IncLvl(std::string& label)
74 {
75     m_labelLvl.resize(m_LoopLvl+1);
76     m_labelLvl[m_LoopLvl] = label;
77     m_LoopLvl++;
78 }
79 
80 ////////////////////////////////////////////////////////////////////////////////
IncLvl()81 void CBotInstr::IncLvl()
82 {
83     m_labelLvl.resize(m_LoopLvl+1);
84     m_labelLvl[m_LoopLvl] = "#SWITCH";
85     m_LoopLvl++;
86 }
87 
88 ////////////////////////////////////////////////////////////////////////////////
DecLvl()89 void CBotInstr::DecLvl()
90 {
91     m_LoopLvl--;
92     m_labelLvl[m_LoopLvl].clear();
93 }
94 
95 ////////////////////////////////////////////////////////////////////////////////
ChkLvl(const std::string & label,int type)96 bool CBotInstr::ChkLvl(const std::string& label, int type)
97 {
98     int    i = m_LoopLvl;
99     while (--i>=0)
100     {
101         if ( type == ID_CONTINUE && m_labelLvl[i] == "#SWITCH") continue;
102         if (label.empty()) return true;
103         if (m_labelLvl[i] == label) return true;
104     }
105     return false;
106 }
107 
108 ////////////////////////////////////////////////////////////////////////////////
SetToken(CBotToken * p)109 void CBotInstr::SetToken(CBotToken* p)
110 {
111     m_token = *p;
112 }
113 
114 ////////////////////////////////////////////////////////////////////////////////
GetTokenType()115 int CBotInstr::GetTokenType()
116 {
117     return m_token.GetType();
118 }
119 
120 ////////////////////////////////////////////////////////////////////////////////
GetToken()121 CBotToken* CBotInstr::GetToken()
122 {
123     return &m_token;
124 }
125 
126 ////////////////////////////////////////////////////////////////////////////////
AddNext(CBotInstr * n)127 void CBotInstr::AddNext(CBotInstr* n)
128 {
129     CBotInstr*    p = this;
130     while (p->m_next != nullptr) p = p->m_next;
131     p->m_next = n;
132 }
133 
134 ////////////////////////////////////////////////////////////////////////////////
AddNext3(CBotInstr * n)135 void CBotInstr::AddNext3(CBotInstr* n)
136 {
137     CBotInstr*    p = this;
138     while (p->m_next3 != nullptr) p = p->m_next3;
139     p->m_next3 = n;
140 }
141 
142 ////////////////////////////////////////////////////////////////////////////////
AddNext3b(CBotInstr * n)143 void CBotInstr::AddNext3b(CBotInstr* n)
144 {
145     CBotInstr*    p = this;
146     while (p->m_next3b != nullptr) p = p->m_next3b;
147     p->m_next3b = n;
148 }
149 
150 ////////////////////////////////////////////////////////////////////////////////
GetNext()151 CBotInstr* CBotInstr::GetNext()
152 {
153     return m_next;
154 }
155 
156 ////////////////////////////////////////////////////////////////////////////////
GetNext3()157 CBotInstr* CBotInstr::GetNext3()
158 {
159     return m_next3;
160 }
161 
162 ////////////////////////////////////////////////////////////////////////////////
GetNext3b()163 CBotInstr* CBotInstr::GetNext3b()
164 {
165     return m_next3b;
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////
Compile(CBotToken * & p,CBotCStack * pStack)169 CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack)
170 {
171     CBotToken*    pp = p;
172 
173     if (p == nullptr) return nullptr;
174 
175     int type = p->GetType(); // what is the next token?
176     if (IsOfType(pp, TokenTypVar) && IsOfType(pp, ID_DOTS)) // is it a label?
177     {
178          type = pp->GetType();
179          // Allow only instructions that accept a label
180          if (!IsOfTypeList(pp, ID_WHILE, ID_FOR, ID_DO, ID_REPEAT, 0))
181          {
182              pStack->SetError(CBotErrLabel, pp->GetStart());
183              return nullptr;
184          }
185     }
186 
187     // Call Compile() function for the given token type
188     switch (type)
189     {
190     case ID_WHILE:
191         return CBotWhile::Compile(p, pStack);
192 
193     case ID_FOR:
194         return CBotFor::Compile(p, pStack);
195 
196     case ID_DO:
197         return CBotDo::Compile(p, pStack);
198 
199     case ID_REPEAT:
200         return CBotRepeat::Compile(p, pStack);
201 
202     case ID_BREAK:
203     case ID_CONTINUE:
204         return CBotBreak::Compile(p, pStack);
205 
206     case ID_SWITCH:
207         return CBotSwitch::Compile(p, pStack);
208 
209     case ID_TRY:
210         return CBotTry::Compile(p, pStack);
211 
212     case ID_THROW:
213         return CBotThrow::Compile(p, pStack);
214 
215     case ID_BYTE:
216     case ID_SHORT:
217     case ID_CHAR:
218     case ID_INT:
219     case ID_LONG:
220         return CBotDefInt::Compile(p, pStack);
221 
222     case ID_FLOAT:
223     case ID_DOUBLE:
224         return CBotDefFloat::Compile(p, pStack);
225 
226     case ID_STRING:
227         return CBotDefString::Compile(p, pStack);
228 
229     case ID_BOOLEAN:
230     case ID_BOOL:
231         return CBotDefBoolean::Compile(p, pStack);
232 
233     case ID_IF:
234         return CBotIf::Compile(p, pStack);
235 
236     case ID_RETURN:
237         return CBotReturn::Compile(p, pStack);
238 
239     case ID_ELSE:
240         pStack->SetStartError(p->GetStart());
241         pStack->SetError(CBotErrElseWhitoutIf, p->GetEnd());
242         return nullptr;
243 
244     case ID_CASE:
245         pStack->SetStartError(p->GetStart());
246         pStack->SetError(CBotErrCaseOut, p->GetEnd());
247         return nullptr;
248     }
249 
250     pStack->SetStartError(p->GetStart());
251 
252     // Should not be a reserved constant defined with DefineNum
253     if (p->GetType() == TokenTypDef)
254     {
255         pStack->SetError(CBotErrReserved, p);
256         return nullptr;
257     }
258 
259     // If not, this might be an instance of class definnition
260     CBotToken*    ppp = p;
261     if (IsOfType(ppp, TokenTypVar))
262     {
263         if (CBotClass::Find(p) != nullptr) // Does class with this name exist?
264         {
265             // Yes, compile the declaration of the instance
266             return CBotDefClass::Compile(p, pStack);
267         }
268     }
269 
270     // This can be an arithmetic expression
271     CBotInstr* inst = CBotExpression::Compile(p, pStack);
272     if (IsOfType(p, ID_SEP))
273     {
274         return inst;
275     }
276     pStack->SetError(CBotErrNoTerminator, p->GetStart());
277     delete inst;
278     return nullptr;
279 }
280 
281 ////////////////////////////////////////////////////////////////////////////////
Execute(CBotStack * & pj)282 bool CBotInstr::Execute(CBotStack* &pj)
283 {
284     assert(0);            // should never go through this routine
285                             // but use the routines of the subclasses
286     return false;
287 }
288 
289 ////////////////////////////////////////////////////////////////////////////////
Execute(CBotStack * & pj,CBotVar * pVar)290 bool CBotInstr::Execute(CBotStack* &pj, CBotVar* pVar)
291 {
292     if (!Execute(pj)) return false;
293     pVar->SetVal(pj->GetVar());
294     return true;
295 }
296 
297 ////////////////////////////////////////////////////////////////////////////////
RestoreState(CBotStack * & pj,bool bMain)298 void CBotInstr::RestoreState(CBotStack* &pj, bool bMain)
299 {
300     assert(0);            // should never go through this routine
301                            // but use the routines of the subclasses
302 }
303 
304 ////////////////////////////////////////////////////////////////////////////////
ExecuteVar(CBotVar * & pVar,CBotCStack * & pile)305 bool CBotInstr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile)
306 {
307     assert(0);            // dad do not know, see the girls
308     return false;
309 }
310 
311 ////////////////////////////////////////////////////////////////////////////////
ExecuteVar(CBotVar * & pVar,CBotStack * & pile,CBotToken * prevToken,bool bStep,bool bExtend)312 bool CBotInstr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, bool bStep, bool bExtend)
313 {
314     assert(0);            // dad do not know, see the girls
315     return false;
316 }
317 
318 ////////////////////////////////////////////////////////////////////////////////
RestoreStateVar(CBotStack * & pile,bool bMain)319 void CBotInstr::RestoreStateVar(CBotStack* &pile, bool bMain)
320 {
321     assert(0);            // dad do not know, see the girls
322 }
323 
CompileArray(CBotToken * & p,CBotCStack * pStack,CBotTypResult type,bool first)324 CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, bool first)
325 {
326     if (IsOfType(p, ID_OPBRK))
327     {
328         if (!IsOfType(p, ID_CLBRK))
329         {
330             pStack->SetError(CBotErrCloseIndex, p->GetStart());
331             return nullptr;
332         }
333 
334         CBotInstr*    inst = CompileArray(p, pStack, CBotTypResult(CBotTypArrayPointer, type), false);
335         if (inst != nullptr || !pStack->IsOk()) return inst;
336     }
337 
338     // compiles an array declaration
339     if (first) return nullptr ;
340 
341     CBotInstr* inst = CBotDefArray::Compile(p, pStack, type);
342     if (inst == nullptr) return nullptr;
343 
344     if (IsOfType(p,  ID_COMMA)) // several definitions
345     {
346         if (nullptr != ( inst->m_next2b = CBotDefArray::CompileArray(p, pStack, type, false)))    // compiles next one
347         {
348             return inst;
349         }
350         delete inst;
351         return nullptr;
352     }
353 
354     if (IsOfType(p,  ID_SEP))                            // end of instruction
355     {
356         return inst;
357     }
358 
359     delete inst;
360     pStack->SetError(CBotErrNoTerminator, p->GetStart());
361     return nullptr;
362 }
363 
HasReturn()364 bool CBotInstr::HasReturn()
365 {
366     if (m_next != nullptr) return m_next->HasReturn();
367     return false; // end of the list
368 }
369 
GetDebugLinks()370 std::map<std::string, CBotInstr*> CBotInstr::GetDebugLinks()
371 {
372     return {
373         {"m_next", m_next},
374         {"m_next2b", m_next2b},
375         {"m_next3", m_next3},
376         {"m_next3b", m_next3b}
377     };
378 }
379 
380 } // namespace CBot
381