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