4 #include "script_compiler.h"
\r
8 using namespace NLMISC;
\r
9 using namespace AIVM;
\r
10 using namespace AICOMP;
\r
12 // Define this to output verbose debug parsing info
\r
13 //#define AI_COMP_DEBUG
\r
15 extern int aierrorline (const char *s, int);
\r
16 extern int aierror (const char *s);
\r
17 extern int ailex ();
\r
18 extern int aiparse ();
\r
19 extern void addSignature (char *dest, char *src);
\r
20 extern int aiErrorCount;
\r
22 extern char aiFile[512];
\r
23 extern const char *aiInputScript;
\r
24 extern uint aiInputScriptLength;
\r
25 extern void aiClean ();
\r
26 extern int aidebug; /* nonzero means print parse trace */
\r
27 #define fprintf myfprintf
\r
28 int myfprintf (FILE *file, const char *format, ...)
\r
32 va_start( args, format );
\r
36 result = vsnprintf(buffer, 1024, format, args);
\r
41 result = vfprintf (file, format, args);
\r
47 std::vector<size_t> *aiRoot;
\r
48 string aiErrorMessage;
\r
51 bool aiCompile (std::vector<size_t> &dest, const char *script, const char *scriptName, bool win32Report = false)
\r
53 #ifdef AI_COMP_DEBUG
\r
55 #else // AI_COMP_DEBUG
\r
57 #endif // AI_COMP_DEBUG
\r
60 aiInputScript = script;
\r
61 aiInputScriptLength = (uint)strlen (script);
\r
62 strcpy (aiFile, scriptName);
\r
65 nldebug("script compilation of %s", scriptName);
\r
66 aiErrorMessage = toString ("script compilation of %s\n", scriptName);
\r
67 int err = aiparse();
\r
68 bool error = err || aiErrorCount;
\r
71 nlwarning ("compilation failed for %s - %d error(s)", scriptName, aiErrorCount);
\r
72 aiErrorMessage += toString ("compilation failed for %s - %d error(s)\n", scriptName, aiErrorCount);
\r
77 nldebug ("compilation success. (code size %d)", aiRoot->size()*4);
\r
78 aiErrorMessage += toString ("compilation success. (code size %d)\n", aiRoot->size()*4);
\r
82 #ifdef NL_OS_WINDOWS
\r
84 MessageBox (NULL, aiErrorMessage.c_str (), "AI Script Compiler", MB_OK|(error?MB_ICONEXCLAMATION:MB_ICONINFORMATION));
\r
85 #endif // NL_OS_WINDOWS
\r
92 /* The parsing tree is composed of different types of node:
\r
93 - Opcode for terminal opcode node
\r
94 - ByteCode for a bunch of code node
\r
95 - ByteCodeList for bunch of code when maintening substructure is important (for rand)
\r
96 - Case for storing case index and code
\r
97 - Cases for storing list of case
\r
99 Opcode, ByteCode and ByteCodeList can be appended to a ByteCode node.
\r
102 // Memory to delete
\r
103 vector<vector<size_t> *> NodeToClear;
\r
104 vector<list<vector<size_t> *> *> ListToClear;
\r
105 vector<CCase *> CaseToClear;
\r
106 vector<map<size_t, CCase *> *> SwitchToClear;
\r
111 for (i=0; i<NodeToClear.size (); i++)
\r
112 delete NodeToClear[i];
\r
113 for (i=0; i<SwitchToClear.size (); i++)
\r
114 delete SwitchToClear[i];
\r
115 for (i=0; i<CaseToClear.size (); i++)
\r
116 delete CaseToClear[i];
\r
117 for (i=0; i<ListToClear.size (); i++)
\r
118 delete ListToClear[i];
\r
119 NodeToClear.clear ();
\r
120 ListToClear.clear ();
\r
121 CaseToClear.clear ();
\r
122 SwitchToClear.clear ();
\r
125 void aiOutputError(int line, const char *format, ...)
\r
129 va_start (args, format);
\r
131 vsnprintf (buffer, sizeof (buffer), format, args);
\r
133 nlwarning ("%s(%d):%s", aiFile, line, buffer);
\r
134 aiErrorMessage += toString ("%s(%d):%s\n", aiFile, line, buffer);
\r
139 void createNode (CByteCodeYacc &dest)
\r
141 dest.ByteCode = new vector<size_t>;
\r
142 dest.Signature[0] = 0;
\r
143 NodeToClear.push_back (dest.ByteCode);
\r
146 void addNode (CByteCodeYacc &dest, size_t src)
\r
148 dest.ByteCode->push_back (src);
\r
151 void addNode (CByteCodeYacc &dest, const AICOMP::COpcodeYacc &src)
\r
153 dest.ByteCode->push_back (src.Opcode);
\r
156 void addNode (CByteCodeYacc &dest, const CByteCodeYacc &src)
\r
158 dest.ByteCode->insert (dest.ByteCode->end(), src.ByteCode->begin (), src.ByteCode->end ());
\r
161 void addNode (CByteCodeYacc &dest, const CByteCodeListYacc &src)
\r
163 list<vector<size_t> * >::iterator ite = src.ByteCodeList->begin();
\r
164 while (ite != src.ByteCodeList->end())
\r
166 dest.ByteCode->insert (dest.ByteCode->end(), (*ite)->begin (), (*ite)->end ());
\r
173 void createList (CByteCodeListYacc &dest)
\r
175 dest.ByteCodeList = new list<vector<size_t> *>;
\r
176 dest.Signature[0] = 0;
\r
177 ListToClear.push_back (dest.ByteCodeList);
\r
180 void addNode (CByteCodeListYacc &dest, const CByteCodeYacc &src)
\r
182 dest.ByteCodeList->push_back (src.ByteCode);
\r
185 // Returns the size of the children bytecode
\r
186 uint getChildrenByteCodeSize (const list<vector<size_t> * > *l)
\r
189 list<vector<size_t> * >::const_iterator ite = l->begin();
\r
190 while (ite != l->end())
\r
192 size += (uint)(*ite)->size ();
\r
200 void createCase (CCaseYacc &dest, COpcodeYacc &_case, CByteCodeYacc &byteCode)
\r
202 dest.Case = new CCase;
\r
203 dest.Case->Case = _case.Opcode;
\r
204 dest.Case->ByteCode = byteCode.ByteCode;
\r
205 dest.Case->Line = _case.Line;
\r
206 strcpy (dest.Signature, _case.Signature);
\r
207 strcpy (dest.Case->Signature, _case.Signature);
\r
208 CaseToClear.push_back (dest.Case);
\r
213 void createSwitch (CSwitchYacc &dest)
\r
215 dest.Cases = new map<size_t, CCase *>;
\r
216 dest.Signature[0] = 0;
\r
217 SwitchToClear.push_back (dest.Cases);
\r
220 void addNode (CSwitchYacc &dest, CCase *src)
\r
222 dest.Cases->insert (map<size_t, CCase*>::value_type (src->Case, src));
\r
225 // Returns the size of the children bytecode
\r
226 uint getChildrenByteCodeSize (const map<size_t, CCase *> *l)
\r
229 map<size_t, CCase *>::const_iterator ite = l->begin ();
\r
230 while (ite != l->end ())
\r
232 size += (uint)ite->second->ByteCode->size();
\r
238 // Write native function code
\r
239 void nativeFunc (CByteCodeYacc &dest, size_t name, const char *in, const char *out)
\r
241 string funcName = CStringMapper::unmap ((TStringId)name);
\r
242 string inParamsSig = in;
\r
243 string outParamsSig = out;
\r
245 // Get the native function
\r
246 CScriptNativeFuncParams *funcParam=CCompiler::getNativeFunc(funcName, inParamsSig, outParamsSig);
\r
249 string signature = funcName + "_" + inParamsSig + "_" + outParamsSig;
\r
250 aiOutputError (aiLine, "Unknown function name or bad parameters %s", signature.c_str ());
\r
255 if (funcParam->_va)
\r
256 mode |= 1; // :KLUDGE: Hardcoded 1 :TODO: replace with a named constant
\r
259 TStringId outStrId;
\r
260 inStrId = CStringMapper::map(inParamsSig);
\r
261 outStrId = CStringMapper::map(outParamsSig);
\r
264 addNode (dest, CScriptVM::NATIVE_CALL);
\r
265 addNode (dest, name);
\r
266 addNode (dest, mode);
\r
267 addNode (dest, *((size_t*)&inStrId));
\r
268 addNode (dest, *((size_t*)&outStrId));
\r
272 #define NODE0(dest) createNode (dest);
\r
273 #define NODE1(dest,a) createNode (dest); addNode (dest, a);
\r
274 #define NODE2(dest,a,b) createNode (dest); addNode (dest, a); addNode (dest, b);
\r
275 #define NODE3(dest,a,b,c) createNode (dest); addNode (dest, a); addNode (dest, b); addNode (dest, c);
\r
276 #define NODE4(dest,a,b,c,d) createNode (dest); addNode (dest, a); addNode (dest, b); addNode (dest, c); addNode (dest, d);
\r
277 #define NODE5(dest,a,b,c,d,e) createNode (dest); addNode (dest, a); addNode (dest, b); addNode (dest, c); addNode (dest, d); addNode (dest, e);
\r
278 #define NODE6(dest,a,b,c,d,e,f) createNode (dest); addNode (dest, a); addNode (dest, b); addNode (dest, c); addNode (dest, d); addNode (dest, e); addNode (dest, f);
\r
279 #define NODE7(dest,a,b,c,d,e,f,g) createNode (dest); addNode (dest, a); addNode (dest, b); addNode (dest, c); addNode (dest, d); addNode (dest, e); addNode (dest, f); addNode (dest, g);
\r
280 #define NODE8(dest,a,b,c,d,e,f,g,h) createNode (dest); addNode (dest, a); addNode (dest, b); addNode (dest, c); addNode (dest, d); addNode (dest, e); addNode (dest, f); addNode (dest, g); addNode (dest, h);
\r
282 #define TYPEF(dest) dest.Signature[0] = 'f'; dest.Signature[1] = 0;
\r
283 #define TYPEL(dest) dest.Signature[0] = 'l'; dest.Signature[1] = 0;
\r
284 #define TYPES(dest) dest.Signature[0] = 's'; dest.Signature[1] = 0;
\r
285 #define TYPEC(dest) dest.Signature[0] = 'c'; dest.Signature[1] = 0;
\r
286 #define TYPE1(dest,a) strcpy (dest.Signature, a.Signature);
\r
287 #define TYPE2(dest,a,b) strcpy (dest.Signature, a.Signature); addSignature (dest.Signature, b.Signature);
\r
289 #define ERROR_DETECTED(a,b) NODE0 (a); aiOutputError (aiLine, b)
\r
296 AICOMP::COpcodeYacc Opcode;
\r
297 AICOMP::COperatorYacc Operator;
\r
298 AICOMP::CByteCodeYacc ByteCode;
\r
299 AICOMP::CByteCodeListYacc ByteCodeList;
\r
300 AICOMP::CCaseYacc Case;
\r
301 AICOMP::CSwitchYacc Switch;
\r
305 %token <Nothing> TOKEN_IF TOKEN_ELSE TOKEN_WHILE TOKEN_PRINT TOKEN_LOG TOKEN_CASE TOKEN_POINT
\r
306 %token <Nothing> TOKEN_SEPARATOR TOKEN_PV TOKEN_PP TOKEN_LP TOKEN_LA TOKEN_RP TOKEN_RA TOKEN_ASSIGNATOR
\r
307 %token <Nothing> TOKEN_SWITCH TOKEN_RAND
\r
309 %token <Opcode> TOKEN_NUMBER TOKEN_ONCHILDREN TOKEN_CHAIN TOKEN_NAME TOKEN_STRNAME TOKEN_CTXNAME TOKEN_LOGIC
\r
310 %token <Opcode> TOKEN_INCRDECR TOKEN_ADD TOKEN_SUB
\r
312 %token <Operator> TOKEN_COMP TOKEN_ASSIGN TOKEN_FACTOR
\r
314 %type <Opcode> caseIndex
\r
316 %type <ByteCode> script
\r
317 %type <ByteCode> condition
\r
318 %type <ByteCode> expression
\r
319 %type <ByteCode> setFunction
\r
320 %type <ByteCode> context
\r
321 %type <ByteCode> function
\r
322 %type <ByteCode> call
\r
323 %type <ByteCode> nativeFunc
\r
324 %type <ByteCode> nativeOtherFunc
\r
325 %type <ByteCode> setFromFuncGet
\r
326 %type <ByteCode> setFromFuncSet
\r
327 %type <ByteCode> setFromFunction
\r
328 %type <ByteCode> tupleElem
\r
329 %type <ByteCode> tuple
\r
330 %type <ByteCode> lValue
\r
331 %type <ByteCode> readVar
\r
332 %type <ByteCode> writeVar
\r
333 %type <ByteCode> expressions
\r
334 %type <ByteCode> params
\r
335 %type <ByteCode> exp
\r
336 %type <ByteCode> printContent
\r
337 %type <ByteCode> printString
\r
338 %type <ByteCode> logString
\r
339 %type <ByteCode> statement
\r
340 %type <ByteCode> openStatement
\r
341 %type <ByteCode> closedStatement
\r
342 %type <ByteCode> randEx
\r
343 %type <ByteCode> onChildren
\r
344 %type <ByteCode> switch
\r
346 %type <ByteCodeList> statements
\r
347 %type <ByteCodeList> statementBlock
\r
351 %type <Switch> cases
\r
355 %left TOKEN_ADD TOKEN_SUB
\r
360 script: statements { NODE1 ($$, $1); aiRoot = $$.ByteCode; }
\r
362 condition: condition TOKEN_LOGIC condition { NODE3 ($$, $1, $3, $2); TYPEL ($$); }
\r
363 | TOKEN_LP condition TOKEN_RP { $$ = $2; }
\r
364 | expression TOKEN_COMP expression
\r
366 if ($1.getType () != $3.getType ()) aiOutputError (aiLine, "the left and right '%s' expressions have not the same type : left is a %s and right is a %s",
\r
367 $2.Operator, $1.getType (), $3.getType ());
\r
368 NODE3 ($$, $1, $3, $2); TYPEL ($$);
\r
371 expression: expression TOKEN_ADD expression
\r
373 NODE3 ($$, $1, $3, $2); TYPEF ($$);
\r
375 | expression TOKEN_SUB expression
\r
377 if (!$1.isFloat ()) aiOutputError (aiLine, "the left '-' expression must be a float but it is a %s", $1.getType ());
\r
378 if (!$3.isFloat ()) aiOutputError (aiLine, "the right '-' expression must be a float but it is a %s", $3.getType ());
\r
379 NODE3 ($$, $1, $3, $2); TYPEF ($$);
\r
381 | expression TOKEN_FACTOR expression
\r
383 if (!$1.isFloat ()) aiOutputError (aiLine, "the left '%s' expression must be a float but it is a %s", $2.Operator, $1.getType ());
\r
384 if (!$3.isFloat ()) aiOutputError (aiLine, "the right '%s' expression must be a float but it is a %s", $2.Operator, $3.getType ());
\r
385 NODE3 ($$, $1, $3, $2); TYPEF ($$);
\r
387 | TOKEN_LP expression TOKEN_RP { $$ = $2; }
\r
388 | readVar { $$ = $1; }
\r
390 setFunction: TOKEN_NAME TOKEN_LP TOKEN_RP statementBlock
\r
392 // Get the size of the total byte code of statementBlock
\r
393 int sizeToJump = (int)getChildrenByteCodeSize ($4.ByteCodeList);
\r
394 sizeToJump++; // 1 jump offset
\r
395 sizeToJump++; // 1 final EOP to escape
\r
396 NODE6 ($$, CScriptVM::FUNCTION, $1, CScriptVM::JUMP, sizeToJump, $4, CScriptVM::EOP);
\r
398 | TOKEN_NAME TOKEN_LP TOKEN_RP TOKEN_LA TOKEN_RA
\r
400 int sizeToJump = + 2; // 1 jump instruction and EOP to escape
\r
401 NODE5 ($$, CScriptVM::FUNCTION, $1, CScriptVM::JUMP, sizeToJump, CScriptVM::EOP);
\r
404 context: TOKEN_NAME TOKEN_POINT { NODE2 ($$, CScriptVM::PUSH_GROUP, $1); }
\r
405 | TOKEN_CTXNAME TOKEN_POINT { NODE2 ($$, CScriptVM::PUSH_CTX_VAR_VAL, $1); }
\r
407 function: setFunction { NODE2 ($$, CScriptVM::PUSH_THIS, $1); }
\r
408 | context setFunction { NODE2 ($$, $1, $2); }
\r
410 call: TOKEN_NAME TOKEN_LP TOKEN_RP { NODE3 ($$, CScriptVM::PUSH_THIS, CScriptVM::CALL, $1); }
\r
411 | TOKEN_NAME TOKEN_LP { ERROR_DETECTED ($$, "missing ')' after the function name"); }
\r
412 | TOKEN_NAME TOKEN_RP { ERROR_DETECTED ($$, "missing '(' after the function name"); }
\r
413 | context TOKEN_NAME TOKEN_LP TOKEN_RP { NODE3 ($$, $1, CScriptVM::CALL, $2); }
\r
414 | context TOKEN_NAME TOKEN_LP { ERROR_DETECTED ($$, "missing ')' after the function name"); }
\r
415 | context TOKEN_NAME TOKEN_RP { ERROR_DETECTED ($$, "missinga '(' after the function name"); }
\r
418 nativeFunc: tuple TOKEN_NAME params
\r
420 NODE2 ($$, $3, CScriptVM::PUSH_THIS);
\r
421 nativeFunc ($$, $2.Opcode, $3.Signature, $1.Signature);
\r
426 nativeOtherFunc: tuple context TOKEN_NAME params
\r
428 NODE2 ($$, $4, $2);
\r
429 nativeFunc ($$, $3.Opcode, $4.Signature, $1.Signature);
\r
434 setFromFuncGet: TOKEN_NAME TOKEN_LP TOKEN_RP { NODE3 ($$, CScriptVM::PUSH_THIS, CScriptVM::PUSH_STRING, $1); }
\r
435 | context TOKEN_NAME TOKEN_LP TOKEN_RP { NODE3 ($$, $1, CScriptVM::PUSH_STRING, $2); }
\r
437 setFromFuncSet: TOKEN_NAME TOKEN_LP TOKEN_RP { NODE3 ($$, CScriptVM::PUSH_THIS, CScriptVM::PUSH_STRING, $1); }
\r
438 | context TOKEN_NAME TOKEN_LP TOKEN_RP { NODE3 ($$, $1, CScriptVM::PUSH_STRING, $2); }
\r
440 setFromFunction: setFromFuncSet TOKEN_ASSIGNATOR setFromFuncGet { NODE3 ($$, $1, $3, CScriptVM::ASSIGN_FUNC_FROM); }
\r
442 tupleElem: tupleElem TOKEN_SEPARATOR writeVar { NODE2 ($$, $1, $3); TYPE2 ($$, $1, $3); }
\r
443 | writeVar { $$ = $1; }
\r
445 tuple: TOKEN_LP tupleElem TOKEN_RP { $$ = $2; }
\r
446 | TOKEN_LP TOKEN_RP { NODE0 ($$); }
\r
448 lValue: writeVar { $$ = $1; }
\r
449 | tuple { $$ = $1; }
\r
451 readVar: TOKEN_NAME
\r
453 NODE2 ($$, CScriptVM::PUSH_VAR_VAL, $1);
\r
456 | context TOKEN_NAME
\r
458 NODE3 ($$, $1, CScriptVM::PUSH_CONTEXT_VAR_VAL, $2);
\r
463 NODE2 ($$, CScriptVM::PUSH_STR_VAR_VAL, $1);
\r
466 | context TOKEN_STRNAME
\r
468 NODE3 ($$, $1, CScriptVM::PUSH_CONTEXT_STR_VAR_VAL, $2);
\r
473 NODE2 ($$, CScriptVM::PUSH_CTX_VAR_VAL, $1);
\r
476 | context TOKEN_CTXNAME
\r
478 NODE3 ($$, $1, CScriptVM::PUSH_CONTEXT_CTX_VAR_VAL, $2);
\r
483 NODE2 ($$, CScriptVM::PUSH_STRING, $1);
\r
488 NODE2 ($$, CScriptVM::PUSH_ON_STACK, $1);
\r
492 writeVar: TOKEN_NAME { NODE2 ($$, CScriptVM::SET_VAR_VAL, $1); TYPEF ($$); }
\r
493 | context TOKEN_NAME { NODE3 ($$, $1, CScriptVM::SET_CONTEXT_VAR_VAL, $2); TYPEF ($$); }
\r
494 | TOKEN_STRNAME { NODE2 ($$, CScriptVM::SET_STR_VAR_VAL, $1); TYPES ($$); }
\r
495 | context TOKEN_STRNAME { NODE3 ($$, $1, CScriptVM::SET_CONTEXT_STR_VAR_VAL, $2); TYPES ($$); }
\r
496 | TOKEN_CTXNAME { NODE2 ($$, CScriptVM::SET_CTX_VAR_VAL, $1); TYPEC ($$); }
\r
497 | context TOKEN_CTXNAME { NODE3 ($$, $1, CScriptVM::SET_CONTEXT_CTX_VAR_VAL, $2); TYPEC ($$); }
\r
499 expressions: expressions TOKEN_SEPARATOR expression { NODE2 ($$, $1, $3); TYPE2 ($$, $1, $3); }
\r
500 | expressions expression { ERROR_DETECTED ($$, "missing ',' between two expressions"); }
\r
501 | expression { $$ = $1; }
\r
503 params: TOKEN_LP expressions TOKEN_RP { $$ = $2; }
\r
504 | TOKEN_LP expressions { ERROR_DETECTED ($$, "missing ')' at the end of the parameters"); }
\r
505 | TOKEN_LP TOKEN_RP { NODE0 ($$); }
\r
507 exp: lValue TOKEN_ASSIGNATOR expression
\r
509 // No need to check the types. All assignations are possibles.
\r
510 NODE2 ($$, $3, $1);
\r
512 | lValue TOKEN_ASSIGNATOR TOKEN_LP expression { ERROR_DETECTED ($$, "missing ')' at the end of the expression"); }
\r
513 | lValue TOKEN_ASSIGNATOR expression TOKEN_RP { ERROR_DETECTED ($$, "missing '(' at the beginning of the expression");}
\r
515 printContent: printContent TOKEN_SEPARATOR TOKEN_CHAIN { NODE3 ($$, $1, CScriptVM::PUSH_PRINT_STRING, $3); }
\r
516 | TOKEN_CHAIN { NODE2 ($$, CScriptVM::PUSH_PRINT_STRING, $1); }
\r
517 | printContent TOKEN_SEPARATOR TOKEN_NAME { NODE3 ($$, $1, CScriptVM::PUSH_PRINT_VAR, $3); }
\r
518 | TOKEN_NAME { NODE2 ($$, CScriptVM::PUSH_PRINT_VAR, $1); }
\r
519 | printContent TOKEN_SEPARATOR TOKEN_STRNAME { NODE3 ($$, $1, CScriptVM::PUSH_PRINT_STR_VAR, $3); }
\r
520 | TOKEN_STRNAME { NODE2 ($$, CScriptVM::PUSH_PRINT_STR_VAR, $1); }
\r
522 printString: TOKEN_PRINT TOKEN_LP printContent TOKEN_RP { NODE2 ($$, $3, CScriptVM::PRINT_STRING); }
\r
524 logString: TOKEN_LOG TOKEN_LP printContent TOKEN_RP { NODE2 ($$, $3, CScriptVM::LOG_STRING); }
\r
526 statement: openStatement { $$ = $1; }
\r
527 | closedStatement { $$ = $1; }
\r
529 openStatement: TOKEN_IF TOKEN_LP condition TOKEN_RP statement
\r
531 int sizeToJump = (int)$5.ByteCode->size() + 1; // 1 jump instruction to escape
\r
532 NODE4 ($$, $3, CScriptVM::JE, sizeToJump, $5);
\r
534 | TOKEN_IF TOKEN_LP condition statement {ERROR_DETECTED ($$, "missing ')' at the end of the if condition");}
\r
535 | TOKEN_IF condition TOKEN_RP statement {ERROR_DETECTED ($$, "missing '(' at the beginning of the if condition");}
\r
536 | TOKEN_IF TOKEN_LP condition TOKEN_RP closedStatement TOKEN_ELSE openStatement
\r
538 int sizeToJump0 = (int)$5.ByteCode->size() + 3; // 2 jump instructions to escape
\r
539 int sizeToJump1 = (int)$7.ByteCode->size() + 1; // 1 jump instruction to escape
\r
540 NODE7 ($$, $3, CScriptVM::JE, sizeToJump0, $5, CScriptVM::JUMP, sizeToJump1, $7);
\r
542 | TOKEN_IF TOKEN_LP condition closedStatement TOKEN_ELSE openStatement { ERROR_DETECTED ($$, "missing ')' at the end of the if condition");}
\r
543 | TOKEN_IF condition TOKEN_RP closedStatement TOKEN_ELSE openStatement { ERROR_DETECTED ($$, "missing '(' at the beginning of the if condition");}
\r
544 | TOKEN_WHILE TOKEN_LP condition TOKEN_RP openStatement
\r
546 int sizeToJump0 = (int)$5.ByteCode->size() + 3; // 2 jump instructions to escape
\r
547 int sizeToJump1 = -(int)$5.ByteCode->size() - 3 - (int)$3.ByteCode->size(); // 1 jump instruction to escape
\r
548 NODE6 ($$, $3, CScriptVM::JE, sizeToJump0, $5, CScriptVM::JUMP, sizeToJump1);
\r
550 | TOKEN_WHILE TOKEN_LP condition openStatement { ERROR_DETECTED ($$, "missing ')' at the end of the while condition");}
\r
551 | TOKEN_WHILE condition TOKEN_RP openStatement { ERROR_DETECTED ($$, "missing '(' at the beginning of the while condition");}
\r
553 closedStatement:TOKEN_IF TOKEN_LP condition TOKEN_RP closedStatement TOKEN_ELSE closedStatement
\r
555 int sizeToJump0 = (int)$5.ByteCode->size() + 3; // 2 jump instructions to escape
\r
556 int sizeToJump1 = (int)$7.ByteCode->size() + 1; // 1 jump instruction to escape
\r
557 NODE7 ($$, $3, CScriptVM::JE, sizeToJump0, $5, CScriptVM::JUMP, sizeToJump1, $7);
\r
559 | TOKEN_IF TOKEN_LP condition closedStatement TOKEN_ELSE closedStatement { ERROR_DETECTED ($$, "missing ')' at the end of the if condition");}
\r
560 | TOKEN_IF condition TOKEN_RP closedStatement TOKEN_ELSE closedStatement { ERROR_DETECTED ($$, "missing '(' at the end of the if condition");}
\r
561 | TOKEN_WHILE TOKEN_LP condition TOKEN_RP closedStatement
\r
563 int sizeToJump0 = (int)$5.ByteCode->size() + 3; // 2 jump instructions to escape
\r
564 int sizeToJump1 = -(int)$5.ByteCode->size() - 3 - (int)$3.ByteCode->size(); // 1 jump instruction to escape
\r
565 NODE6 ($$, $3, CScriptVM::JE, sizeToJump0, $5, CScriptVM::JUMP, sizeToJump1);
\r
567 | TOKEN_WHILE TOKEN_LP condition closedStatement { ERROR_DETECTED ($$, "missing ')' at the end of the while condition");}
\r
568 | TOKEN_WHILE condition TOKEN_RP closedStatement { ERROR_DETECTED ($$, "missing '(' at the beginning of the while condition");}
\r
569 | exp TOKEN_PV { $$ = $1; }
\r
570 | printString TOKEN_PV { $$ = $1; }
\r
571 | logString TOKEN_PV { $$ = $1; }
\r
572 | function { $$ = $1; }
\r
573 | call TOKEN_PV { $$ = $1; }
\r
574 | setFromFunction TOKEN_PV { $$ = $1; }
\r
575 | nativeFunc TOKEN_PV { $$ = $1; }
\r
576 | nativeOtherFunc TOKEN_PV { $$ = $1; }
\r
577 | randEx { $$ = $1; }
\r
578 | onChildren { $$ = $1; }
\r
579 | switch { $$ = $1; }
\r
580 | TOKEN_NAME TOKEN_INCRDECR TOKEN_PV { NODE5 ($$, CScriptVM::PUSH_VAR_VAL, $1, $2, CScriptVM::SET_VAR_VAL, $1); }
\r
581 | TOKEN_INCRDECR TOKEN_NAME TOKEN_PV { NODE5 ($$, CScriptVM::PUSH_VAR_VAL, $2, $1, CScriptVM::SET_VAR_VAL, $2); }
\r
582 | context TOKEN_NAME TOKEN_INCRDECR TOKEN_PV { NODE7 ($$, $1, CScriptVM::PUSH_CONTEXT_VAR_VAL, $2, $3, $1, CScriptVM::SET_CONTEXT_VAR_VAL, $2); }
\r
583 | TOKEN_INCRDECR context TOKEN_NAME TOKEN_PV { NODE7 ($$, $2, CScriptVM::PUSH_CONTEXT_VAR_VAL, $3, $1, $2, CScriptVM::SET_CONTEXT_VAR_VAL, $3); }
\r
584 | TOKEN_NAME TOKEN_ASSIGN expression TOKEN_PV { NODE6 ($$, CScriptVM::PUSH_VAR_VAL, $1, $3, $2, CScriptVM::SET_VAR_VAL, $1); }
\r
585 | TOKEN_NAME TOKEN_ASSIGN TOKEN_LP expression TOKEN_PV { ERROR_DETECTED ($$, "missing ')' at the end of the expression");}
\r
586 | TOKEN_NAME TOKEN_ASSIGN expression TOKEN_RP TOKEN_PV { ERROR_DETECTED ($$, "missing '(' at the beginning of the expression");}
\r
587 | context TOKEN_NAME TOKEN_ASSIGN expression TOKEN_PV { NODE8 ($$, $1, CScriptVM::PUSH_CONTEXT_VAR_VAL, $1, $4, $3, $1, CScriptVM::SET_CONTEXT_VAR_VAL, $2); }
\r
588 | context TOKEN_NAME TOKEN_ASSIGN TOKEN_LP expression TOKEN_PV { ERROR_DETECTED ($$, "missing ')' at the end of the expression");}
\r
589 | context TOKEN_NAME TOKEN_ASSIGN expression TOKEN_RP TOKEN_PV { ERROR_DETECTED ($$, "missing '(' at the beginning of the expression");}
\r
590 | statementBlock { NODE1 ($$, $1); }
\r
591 | error TOKEN_PV { NODE0 ($$); }
\r
593 statements: statement { createList ($$); addNode ($$, $1); }
\r
594 | statements statement { $$ = $1; addNode ($$, $2); }
\r
596 randEx: TOKEN_RAND statementBlock
\r
599 int childCount = $2.ByteCodeList->size ();
\r
601 // Sum all the children size
\r
602 uint sizeToJump = getChildrenByteCodeSize ($2.ByteCodeList);
\r
603 sizeToJump += 1*childCount; // One for the JUMP
\r
604 sizeToJump += 1*childCount; // One for the offset
\r
605 sizeToJump += 1*childCount; // One for the RET
\r
607 sizeToJump += 1; // One for the additionnal jump offset
\r
608 sizeToJump += 1; // One for the RANDEND
\r
610 addNode ($$, CScriptVM::RAND);
\r
611 addNode ($$, childCount);
\r
612 addNode ($$, CScriptVM::JUMP);
\r
613 addNode ($$, sizeToJump);
\r
616 // Write the jump table
\r
617 list<vector<size_t> * >::reverse_iterator rite = $2.ByteCodeList->rbegin();
\r
618 while (rite != $2.ByteCodeList->rend())
\r
620 sizeToJump -= (*rite)->size ();
\r
622 addNode ($$, CScriptVM::JUMP);
\r
623 addNode ($$, sizeToJump);
\r
624 sizeToJump -= 1; // One for the JUMP
\r
625 sizeToJump -= 1; // One for the offset
\r
626 sizeToJump -= 1; // One for the RET
\r
632 list<vector<size_t> * >::iterator ite = $2.ByteCodeList->begin();
\r
633 while (ite != $2.ByteCodeList->end())
\r
635 $$.ByteCode->insert ($$.ByteCode->end(), (*ite)->begin(), (*ite)->end());
\r
636 addNode ($$, CScriptVM::RET);
\r
641 addNode ($$, CScriptVM::RANDEND);
\r
644 caseIndex: TOKEN_CHAIN { $$ = $1; TYPES ($$); }
\r
645 | TOKEN_NUMBER { $$ = $1; TYPEF ($$); }
\r
647 case: TOKEN_CASE caseIndex TOKEN_PP statement
\r
649 createCase ($$, $2, $4); // Create a two entry list to keep track of the case index and the case code
\r
654 createSwitch ($$); // Create a new case list
\r
655 addNode ($$, $1.Case); // Add the first case
\r
656 TYPE1 ($$, $1); // Propagate the key type
\r
661 addNode ($$, $2.Case); // Add a case to the list
\r
664 switch: TOKEN_SWITCH TOKEN_LP expression TOKEN_RP TOKEN_LA cases TOKEN_RA
\r
670 int childCount = $6.Cases->size ();
\r
672 // Sum all the children size
\r
673 uint sizeChild = getChildrenByteCodeSize ($6.Cases);
\r
674 sizeChild += 1*childCount; // One for the RET
\r
676 uint sizeToJump = 0;
\r
677 sizeToJump += 1*childCount; // One for the case key
\r
678 sizeToJump += 1*childCount; // One for the offset
\r
680 sizeToJump += 1; // One for the additionnal jump offset
\r
682 addNode ($$, CScriptVM::SWITCH); // Switch opcode
\r
683 addNode ($$, childCount); // Number of switch cases
\r
684 addNode ($$, sizeToJump+sizeChild); // Return address
\r
687 // Write the jump table
\r
688 map<size_t, CCase *>::const_iterator ite = $6.Cases->begin ();
\r
689 while (ite != $6.Cases->end ())
\r
691 const CCase *_case = ite->second;
\r
694 if (_case->getType () != $3.getType ())
\r
695 aiOutputError (_case->Line, "case has not the same type than the switch expression: the case is %s and the switch is %s",
\r
696 _case->getType (), $3.getType ());
\r
698 addNode ($$, _case->Case);
\r
699 addNode ($$, sizeToJump);
\r
700 sizeToJump += (uint)_case->ByteCode->size ();
\r
701 sizeToJump += 1; // One for the RET
\r
702 sizeToJump -= 1; // One for the case key
\r
703 sizeToJump -= 1; // One for the offset
\r
708 ite = $6.Cases->begin ();
\r
709 while (ite != $6.Cases->end ())
\r
711 const CCase *_case = ite->second;
\r
712 $$.ByteCode->insert ($$.ByteCode->end(), _case->ByteCode->begin(), _case->ByteCode->end());
\r
713 addNode ($$, CScriptVM::RET);
\r
717 | TOKEN_SWITCH TOKEN_LP expression TOKEN_LA cases TOKEN_RA {ERROR_DETECTED ($$, "missing ')' at the end of the switch expression");}
\r
718 | TOKEN_SWITCH expression TOKEN_RP TOKEN_LA cases TOKEN_RA {ERROR_DETECTED ($$, "missing '(' at the beginning of the switch expression");}
\r
719 | TOKEN_SWITCH TOKEN_LP expression TOKEN_RP cases TOKEN_RA {ERROR_DETECTED ($$, "missing '{' at the beginning of the switch cases");}
\r
722 statementBlock: TOKEN_LA statements TOKEN_RA { $$ = $2; }
\r
724 onChildren: TOKEN_ONCHILDREN TOKEN_LP TOKEN_RP statementBlock
\r
726 int sizeToJump = getChildrenByteCodeSize ($4.ByteCodeList);
\r
727 sizeToJump ++; // One for the jump offset
\r
728 sizeToJump ++; // One for the EOP instruction
\r
729 NODE5 ($$, CScriptVM::ONCHILDREN, CScriptVM::JUMP, sizeToJump, $4, CScriptVM::EOP);
\r
735 int aierror (const char *s)
\r
737 aiOutputError (aiLine, s);
\r
741 // This is just a compile fix for bison-1.875+gcc-3.2
\r
742 static void dummy()
\r