Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / script_parser.yacc
blob3d40bc592f47b0c41a288bab49ee05fe27ef56b4
1 %{\r
2 \r
3 #include "stdpch.h"\r
4 #include "script_compiler.h"\r
5 #include <list>\r
6 \r
7 using namespace std;\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
21 extern int      aiLine;\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
29 {\r
30         int result;\r
31         va_list args;\r
32         va_start( args, format );\r
33         if (file == stderr)\r
34         {\r
35                 char buffer[1024];\r
36                 result = vsnprintf(buffer, 1024, format, args);\r
37                 nldebug (buffer);\r
38         }\r
39         else\r
40         {\r
41                 result = vfprintf (file, format, args);\r
42         }\r
43         va_end( args );\r
44         return result;\r
45 }\r
47 std::vector<size_t>     *aiRoot;\r
48 string aiErrorMessage;\r
50 // Main method\r
51 bool aiCompile (std::vector<size_t> &dest, const char *script, const char *scriptName, bool win32Report = false)\r
52 {\r
53 #ifdef AI_COMP_DEBUG\r
54         aidebug = 1;\r
55 #else // AI_COMP_DEBUG\r
56         aidebug = 0;\r
57 #endif // AI_COMP_DEBUG\r
58         aiLine = 1;\r
59         aiErrorCount = 0;\r
60         aiInputScript = script;\r
61         aiInputScriptLength = (uint)strlen (script);\r
62         strcpy (aiFile, scriptName);\r
63         aiRoot = NULL;\r
64         \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
69         if (error)\r
70         {\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
73         }\r
74         else\r
75         {\r
76                 nlassert (aiRoot);\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
79                 dest = *aiRoot;\r
80         }\r
82 #ifdef NL_OS_WINDOWS\r
83         if (win32Report)\r
84                 MessageBox (NULL, aiErrorMessage.c_str (), "AI Script Compiler", MB_OK|(error?MB_ICONEXCLAMATION:MB_ICONINFORMATION));\r
85 #endif // NL_OS_WINDOWS\r
87         // Clean all\r
88         aiClean ();\r
89         return !error;\r
90 }\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
100  */\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
108 void aiClean ()\r
110         uint i;\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
127         aiErrorCount++;\r
128         va_list args;\r
129         va_start (args, format);\r
130         char buffer[1024];\r
131         vsnprintf (buffer, sizeof (buffer), format, args);\r
132         va_end (args);\r
133         nlwarning ("%s(%d):%s", aiFile, line, buffer);\r
134         aiErrorMessage += toString ("%s(%d):%s\n", aiFile, line, buffer);\r
137 // *** Node\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
165         {\r
166                 dest.ByteCode->insert (dest.ByteCode->end(), (*ite)->begin (), (*ite)->end ());\r
167                 ite++;\r
168         }       \r
171 // *** List\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
188         uint size = 0;\r
189         list<vector<size_t> * >::const_iterator ite = l->begin();\r
190         while (ite != l->end())\r
191         {\r
192                 size += (uint)(*ite)->size ();\r
193                 ite++;\r
194         }       \r
195         return size;\r
198 // *** Case\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
211 // *** Switch\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
228         uint size = 0;\r
229         map<size_t, CCase *>::const_iterator ite = l->begin ();\r
230         while (ite != l->end ())\r
231         {\r
232                 size += (uint)ite->second->ByteCode->size();\r
233                 ite ++;\r
234         }       \r
235         return 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
244         \r
245         // Get the native function\r
246         CScriptNativeFuncParams *funcParam=CCompiler::getNativeFunc(funcName, inParamsSig, outParamsSig);\r
247         if (!funcParam)\r
248         {\r
249                 string signature = funcName + "_" + inParamsSig + "_" + outParamsSig;\r
250                 aiOutputError (aiLine, "Unknown function name or bad parameters %s", signature.c_str ());\r
251         }\r
252         else\r
253         {\r
254                 size_t mode = 0;\r
255                 if (funcParam->_va)\r
256                         mode |= 1; // :KLUDGE: Hardcoded 1 :TODO: replace with a named constant\r
257                 \r
258                 TStringId inStrId;\r
259                 TStringId outStrId;\r
260                 inStrId = CStringMapper::map(inParamsSig);\r
261                 outStrId = CStringMapper::map(outParamsSig);\r
262                 \r
263                 // Add the node\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
269         }\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
291 %}\r
293 %start script\r
295 %union  {\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
302         struct {}                                       Nothing;\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
349 %type   <Case>          case\r
351 %type   <Switch>        cases\r
353 %left TOKEN_LOGIC \r
354 %left TOKEN_COMP\r
355 %left TOKEN_ADD TOKEN_SUB\r
356 %left TOKEN_FACTOR\r
358 %%\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
365                                 { \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
369                                 }\r
371 expression:             expression TOKEN_ADD expression \r
372                                 { \r
373                                         NODE3 ($$, $1, $3, $2); TYPEF ($$); \r
374                                 }\r
375                                 | expression TOKEN_SUB expression \r
376                                 { \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
380                                 }\r
381                                 | expression TOKEN_FACTOR expression\r
382                                 { \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
386                                 }\r
387                                 | TOKEN_LP expression TOKEN_RP { $$ = $2; }\r
388                                 | readVar { $$ = $1; }\r
390 setFunction:    TOKEN_NAME TOKEN_LP TOKEN_RP statementBlock\r
391                                 { \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
397                                 }\r
398                                 | TOKEN_NAME TOKEN_LP TOKEN_RP TOKEN_LA TOKEN_RA \r
399                                 { \r
400                                         int sizeToJump = + 2;           // 1 jump instruction and EOP to escape\r
401                                         NODE5 ($$, CScriptVM::FUNCTION, $1, CScriptVM::JUMP, sizeToJump, CScriptVM::EOP); \r
402                                 }\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
419                                 { \r
420                                         NODE2 ($$, $3, CScriptVM::PUSH_THIS);\r
421                                         nativeFunc ($$, $2.Opcode, $3.Signature, $1.Signature);\r
422                                         addNode ($$, $1);\r
423                                         TYPE1 ($$, $1);\r
424                                 }\r
426 nativeOtherFunc:        tuple context TOKEN_NAME params \r
427                                 { \r
428                                         NODE2 ($$, $4, $2);\r
429                                         nativeFunc ($$, $3.Opcode, $4.Signature, $1.Signature);\r
430                                         addNode ($$, $1);\r
431                                         TYPE1 ($$, $1);\r
432                                 }\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
452                                 { \r
453                                         NODE2 ($$, CScriptVM::PUSH_VAR_VAL, $1);\r
454                                         TYPEF ($$);\r
455                                 }\r
456                                 | context TOKEN_NAME \r
457                                 {\r
458                                         NODE3 ($$, $1, CScriptVM::PUSH_CONTEXT_VAR_VAL, $2);\r
459                                         TYPEF ($$); \r
460                                 }\r
461                                 | TOKEN_STRNAME \r
462                                 { \r
463                                         NODE2 ($$, CScriptVM::PUSH_STR_VAR_VAL, $1);\r
464                                         TYPES ($$);\r
465                                 }\r
466                                 | context TOKEN_STRNAME\r
467                                 { \r
468                                         NODE3 ($$, $1, CScriptVM::PUSH_CONTEXT_STR_VAR_VAL, $2);\r
469                                         TYPES ($$);\r
470                                 }\r
471                                 | TOKEN_CTXNAME \r
472                                 { \r
473                                         NODE2 ($$, CScriptVM::PUSH_CTX_VAR_VAL, $1);\r
474                                         TYPEC ($$);\r
475                                 }\r
476                                 | context TOKEN_CTXNAME\r
477                                 { \r
478                                         NODE3 ($$, $1, CScriptVM::PUSH_CONTEXT_CTX_VAR_VAL, $2);\r
479                                         TYPEC ($$);\r
480                                 }\r
481                                 | TOKEN_CHAIN \r
482                                 { \r
483                                         NODE2 ($$, CScriptVM::PUSH_STRING, $1);\r
484                                         TYPES ($$);\r
485                                 }\r
486                                 | TOKEN_NUMBER \r
487                                 { \r
488                                         NODE2 ($$, CScriptVM::PUSH_ON_STACK, $1);\r
489                                         TYPEF ($$);\r
490                                 }\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
498                                 \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
508                                 { \r
509                                         // No need to check the types. All assignations are possibles.\r
510                                         NODE2 ($$, $3, $1); \r
511                                 }\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
530                                 {\r
531                                         int sizeToJump = (int)$5.ByteCode->size() + 1;  // 1 jump instruction to escape\r
532                                         NODE4 ($$, $3, CScriptVM::JE, sizeToJump, $5);\r
533                                 }\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
537                                 { \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
541                                 }\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
545                                 { \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
549                                 }\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
554                                 { \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
558                                 }\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
562                                 { \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
566                                 }\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
597                                 { \r
598                                         createNode ($$);\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
606                                         \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
614                                         sizeToJump -= 2;\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
619                                         {\r
620                                                 sizeToJump -= (*rite)->size ();\r
621                                                 \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
628                                                 rite++;\r
629                                         }       \r
631                                         // Write the code\r
632                                         list<vector<size_t> * >::iterator ite = $2.ByteCodeList->begin();\r
633                                         while (ite != $2.ByteCodeList->end())\r
634                                         {\r
635                                                 $$.ByteCode->insert ($$.ByteCode->end(), (*ite)->begin(), (*ite)->end());\r
636                                                 addNode ($$, CScriptVM::RET);\r
637                                                 ite++;\r
638                                         }\r
640                                         // End\r
641                                         addNode ($$, CScriptVM::RANDEND);\r
642                                 }\r
644 caseIndex:              TOKEN_CHAIN { $$ = $1; TYPES ($$); }\r
645                                 | TOKEN_NUMBER { $$ = $1; TYPEF ($$); }\r
647 case:                   TOKEN_CASE caseIndex TOKEN_PP statement \r
648                                 { \r
649                                         createCase ($$, $2, $4);        // Create a two entry list to keep track of the case index and the case code\r
650                                 }\r
652 cases:                  case \r
653                                 { \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
657                                 }\r
658                                 | cases case\r
659                                 { \r
660                                         $$ = $1; \r
661                                         addNode ($$, $2.Case);  // Add a case to the list\r
662                                 }\r
664 switch:                 TOKEN_SWITCH TOKEN_LP expression TOKEN_RP TOKEN_LA cases TOKEN_RA \r
665                                 {\r
666                                         // Expression\r
667                                         createNode ($$);\r
668                                         addNode ($$, $3);\r
669                                         \r
670                                         int childCount = $6.Cases->size ();\r
671                                         \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
679                                         \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
685                                         sizeToJump -= 2;\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
690                                         {\r
691                                                 const CCase *_case = ite->second;\r
693                                                 // Type checking\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
704                                                 ite++;\r
705                                         }       \r
707                                         // Write the code\r
708                                         ite = $6.Cases->begin ();\r
709                                         while (ite != $6.Cases->end ())\r
710                                         {\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
714                                                 ite++;\r
715                                         }\r
716                                 }\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
725                                 { \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
730                                 }\r
733 %%\r
735 int aierror (const char *s)\r
737         aiOutputError (aiLine, s);\r
738         return 1;\r
741 // This is just a compile fix for bison-1.875+gcc-3.2\r
742 static void dummy()\r
744 //      if (false)\r
745 //              YYERROR;\r