a8dbee6c2ca19408e3272c2fe5405d837b8db228
[supertux.git] / external / squirrel / squirrel / sqcompiler.cpp
1 /*\r
2         see copyright notice in squirrel.h\r
3 */\r
4 #include "sqpcheader.h"\r
5 #ifndef NO_COMPILER\r
6 #include <stdarg.h>\r
7 #include <setjmp.h>\r
8 #include "sqopcodes.h"\r
9 #include "sqstring.h"\r
10 #include "sqfuncproto.h"\r
11 #include "sqcompiler.h"\r
12 #include "sqfuncstate.h"\r
13 #include "sqlexer.h"\r
14 #include "sqvm.h"\r
15 #include "sqtable.h"\r
16 \r
17 #define EXPR   1\r
18 #define OBJECT 2\r
19 #define BASE   3\r
20 #define LOCAL  4\r
21 #define OUTER  5\r
22 \r
23 struct SQExpState {\r
24   SQInteger  etype;       /* expr. type; one of EXPR, OBJECT, BASE, OUTER or LOCAL */\r
25   SQInteger  epos;        /* expr. location on stack; -1 for OBJECT and BASE */\r
26   bool       donot_get;   /* signal not to deref the next value */\r
27 };\r
28 \r
29 struct SQScope {\r
30         SQInteger outers;\r
31         SQInteger stacksize;\r
32 };\r
33 \r
34 #define BEGIN_SCOPE() SQScope __oldscope__ = _scope; \\r
35                                          _scope.outers = _fs->_outers; \\r
36                                          _scope.stacksize = _fs->GetStackSize();\r
37 \r
38 #define RESOLVE_OUTERS() if(_fs->GetStackSize() != _scope.stacksize) { \\r
39                                                         if(_fs->CountOuters(_scope.stacksize)) { \\r
40                                                                 _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \\r
41                                                         } \\r
42                                                 }\r
43 \r
44 #define END_SCOPE_NO_CLOSE() {  if(_fs->GetStackSize() != _scope.stacksize) { \\r
45                                                         _fs->SetStackSize(_scope.stacksize); \\r
46                                                 } \\r
47                                                 _scope = __oldscope__; \\r
48                                         }\r
49 \r
50 #define END_SCOPE() {   SQInteger oldouters = _fs->_outers;\\r
51                                                 if(_fs->GetStackSize() != _scope.stacksize) { \\r
52                                                         _fs->SetStackSize(_scope.stacksize); \\r
53                                                         if(oldouters != _fs->_outers) { \\r
54                                                                 _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \\r
55                                                         } \\r
56                                                 } \\r
57                                                 _scope = __oldscope__; \\r
58                                         }\r
59 \r
60 #define BEGIN_BREAKBLE_BLOCK()  SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \\r
61                                                         SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \\r
62                                                         _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0);\r
63 \r
64 #define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \\r
65                                         __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \\r
66                                         if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \\r
67                                         if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \\r
68                                         _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();}\r
69 \r
70 class SQCompiler\r
71 {\r
72 public:\r
73         SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo)\r
74         {\r
75                 _vm=v;\r
76                 _lex.Init(_ss(v), rg, up,ThrowError,this);\r
77                 _sourcename = SQString::Create(_ss(v), sourcename);\r
78                 _lineinfo = lineinfo;_raiseerror = raiseerror;\r
79                 _scope.outers = 0;\r
80                 _scope.stacksize = 0;\r
81                 compilererror = NULL;\r
82         }\r
83         static void ThrowError(void *ud, const SQChar *s) {\r
84                 SQCompiler *c = (SQCompiler *)ud;\r
85                 c->Error(s);\r
86         }\r
87         void Error(const SQChar *s, ...)\r
88         {\r
89                 static SQChar temp[256];\r
90                 va_list vl;\r
91                 va_start(vl, s);\r
92                 scvsprintf(temp, s, vl);\r
93                 va_end(vl);\r
94                 compilererror = temp;\r
95                 longjmp(_errorjmp,1);\r
96         }\r
97         void Lex(){     _token = _lex.Lex();}\r
98         SQObject Expect(SQInteger tok)\r
99         {\r
100                 \r
101                 if(_token != tok) {\r
102                         if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) {\r
103                                 //do nothing\r
104                         }\r
105                         else {\r
106                                 const SQChar *etypename;\r
107                                 if(tok > 255) {\r
108                                         switch(tok)\r
109                                         {\r
110                                         case TK_IDENTIFIER:\r
111                                                 etypename = _SC("IDENTIFIER");\r
112                                                 break;\r
113                                         case TK_STRING_LITERAL:\r
114                                                 etypename = _SC("STRING_LITERAL");\r
115                                                 break;\r
116                                         case TK_INTEGER:\r
117                                                 etypename = _SC("INTEGER");\r
118                                                 break;\r
119                                         case TK_FLOAT:\r
120                                                 etypename = _SC("FLOAT");\r
121                                                 break;\r
122                                         default:\r
123                                                 etypename = _lex.Tok2Str(tok);\r
124                                         }\r
125                                         Error(_SC("expected '%s'"), etypename);\r
126                                 }\r
127                                 Error(_SC("expected '%c'"), tok);\r
128                         }\r
129                 }\r
130                 SQObjectPtr ret;\r
131                 switch(tok)\r
132                 {\r
133                 case TK_IDENTIFIER:\r
134                         ret = _fs->CreateString(_lex._svalue);\r
135                         break;\r
136                 case TK_STRING_LITERAL:\r
137                         ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);\r
138                         break;\r
139                 case TK_INTEGER:\r
140                         ret = SQObjectPtr(_lex._nvalue);\r
141                         break;\r
142                 case TK_FLOAT:\r
143                         ret = SQObjectPtr(_lex._fvalue);\r
144                         break;\r
145                 }\r
146                 Lex();\r
147                 return ret;\r
148         }\r
149         bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); }\r
150         void OptionalSemicolon()\r
151         {\r
152                 if(_token == _SC(';')) { Lex(); return; }\r
153                 if(!IsEndOfStatement()) {\r
154                         Error(_SC("end of statement expected (; or lf)"));\r
155                 }\r
156         }\r
157         void MoveIfCurrentTargetIsLocal() {\r
158                 SQInteger trg = _fs->TopTarget();\r
159                 if(_fs->IsLocal(trg)) {\r
160                         trg = _fs->PopTarget(); //no pops the target and move it\r
161                         _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg);\r
162                 }\r
163         }\r
164         bool Compile(SQObjectPtr &o)\r
165         {\r
166                 _debugline = 1;\r
167                 _debugop = 0;\r
168 \r
169                 SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this);\r
170                 funcstate._name = SQString::Create(_ss(_vm), _SC("main"));\r
171                 _fs = &funcstate;\r
172                 _fs->AddParameter(_fs->CreateString(_SC("this")));\r
173                 _fs->AddParameter(_fs->CreateString(_SC("vargv")));\r
174                 _fs->_varparams = true;\r
175                 _fs->_sourcename = _sourcename;\r
176                 SQInteger stacksize = _fs->GetStackSize();\r
177                 if(setjmp(_errorjmp) == 0) {\r
178                         Lex();\r
179                         while(_token > 0){\r
180                                 Statement();\r
181                                 if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();\r
182                         }\r
183                         _fs->SetStackSize(stacksize);\r
184                         _fs->AddLineInfos(_lex._currentline, _lineinfo, true);\r
185                         _fs->AddInstruction(_OP_RETURN, 0xFF);\r
186                         _fs->SetStackSize(0);\r
187                         o =_fs->BuildProto();\r
188 #ifdef _DEBUG_DUMP\r
189                         _fs->Dump(_funcproto(o));\r
190 #endif\r
191                 }\r
192                 else {\r
193                         if(_raiseerror && _ss(_vm)->_compilererrorhandler) {\r
194                                 _ss(_vm)->_compilererrorhandler(_vm, compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"),\r
195                                         _lex._currentline, _lex._currentcolumn);\r
196                         }\r
197                         _vm->_lasterror = SQString::Create(_ss(_vm), compilererror, -1);\r
198                         return false;\r
199                 }\r
200                 return true;\r
201         }\r
202         void Statements()\r
203         {\r
204                 while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) {\r
205                         Statement();\r
206                         if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();\r
207                 }\r
208         }\r
209         void Statement(bool closeframe = true)\r
210         {\r
211                 _fs->AddLineInfos(_lex._currentline, _lineinfo);\r
212                 switch(_token){\r
213                 case _SC(';'):  Lex();                                  break;\r
214                 case TK_IF:             IfStatement();                  break;\r
215                 case TK_WHILE:          WhileStatement();               break;\r
216                 case TK_DO:             DoWhileStatement();             break;\r
217                 case TK_FOR:            ForStatement();                 break;\r
218                 case TK_FOREACH:        ForEachStatement();             break;\r
219                 case TK_SWITCH: SwitchStatement();              break;\r
220                 case TK_LOCAL:          LocalDeclStatement();   break;\r
221                 case TK_RETURN:\r
222                 case TK_YIELD: {\r
223                         SQOpcode op;\r
224                         if(_token == TK_RETURN) {\r
225                                 op = _OP_RETURN;\r
226                         }\r
227                         else {\r
228                                 op = _OP_YIELD;\r
229                                 _fs->_bgenerator = true;\r
230                         }\r
231                         Lex();\r
232                         if(!IsEndOfStatement()) {\r
233                                 SQInteger retexp = _fs->GetCurrentPos()+1;\r
234                                 CommaExpr();\r
235                                 if(op == _OP_RETURN && _fs->_traps > 0)\r
236                                         _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0);\r
237                                 _fs->_returnexp = retexp;\r
238                                 _fs->AddInstruction(op, 1, _fs->PopTarget(),_fs->GetStackSize());\r
239                         }\r
240                         else{ \r
241                                 if(op == _OP_RETURN && _fs->_traps > 0)\r
242                                         _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0);\r
243                                 _fs->_returnexp = -1;\r
244                                 _fs->AddInstruction(op, 0xFF,0,_fs->GetStackSize()); \r
245                         }\r
246                         break;}\r
247                 case TK_BREAK:\r
248                         if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block"));\r
249                         if(_fs->_breaktargets.top() > 0){\r
250                                 _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);\r
251                         }\r
252                         RESOLVE_OUTERS();\r
253                         _fs->AddInstruction(_OP_JMP, 0, -1234);\r
254                         _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos());\r
255                         Lex();\r
256                         break;\r
257                 case TK_CONTINUE:\r
258                         if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block"));\r
259                         if(_fs->_continuetargets.top() > 0) {\r
260                                 _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);\r
261                         }\r
262                         RESOLVE_OUTERS();\r
263                         _fs->AddInstruction(_OP_JMP, 0, -1234);\r
264                         _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos());\r
265                         Lex();\r
266                         break;\r
267                 case TK_FUNCTION:\r
268                         FunctionStatement();\r
269                         break;\r
270                 case TK_CLASS:\r
271                         ClassStatement();\r
272                         break;\r
273                 case TK_ENUM:\r
274                         EnumStatement();\r
275                         break;\r
276                 case _SC('{'):{\r
277                                 BEGIN_SCOPE();\r
278                                 Lex();\r
279                                 Statements();\r
280                                 Expect(_SC('}'));\r
281                                 if(closeframe) {\r
282                                         END_SCOPE();\r
283                                 }\r
284                                 else {\r
285                                         END_SCOPE_NO_CLOSE();\r
286                                 }\r
287                         }\r
288                         break;\r
289                 case TK_TRY:\r
290                         TryCatchStatement();\r
291                         break;\r
292                 case TK_THROW:\r
293                         Lex();\r
294                         CommaExpr();\r
295                         _fs->AddInstruction(_OP_THROW, _fs->PopTarget());\r
296                         break;\r
297                 case TK_CONST:\r
298                         {\r
299                         Lex();\r
300                         SQObject id = Expect(TK_IDENTIFIER);\r
301                         Expect('=');\r
302                         SQObject val = ExpectScalar();\r
303                         OptionalSemicolon();\r
304                         SQTable *enums = _table(_ss(_vm)->_consts);\r
305                         SQObjectPtr strongid = id; \r
306                         enums->NewSlot(strongid,SQObjectPtr(val));\r
307                         strongid.Null();\r
308                         }\r
309                         break;\r
310                 default:\r
311                         CommaExpr();\r
312                         _fs->DiscardTarget();\r
313                         //_fs->PopTarget();\r
314                         break;\r
315                 }\r
316                 _fs->SnoozeOpt();\r
317         }\r
318         void EmitDerefOp(SQOpcode op)\r
319         {\r
320                 SQInteger val = _fs->PopTarget();\r
321                 SQInteger key = _fs->PopTarget();\r
322                 SQInteger src = _fs->PopTarget();\r
323         _fs->AddInstruction(op,_fs->PushTarget(),src,key,val);\r
324         }\r
325         void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0)\r
326         {\r
327                 SQInteger p2 = _fs->PopTarget(); //src in OP_GET\r
328                 SQInteger p1 = _fs->PopTarget(); //key in OP_GET\r
329                 _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3);\r
330         }\r
331         void EmitCompoundArith(SQInteger tok, SQInteger etype, SQInteger pos)\r
332         {\r
333                 /* Generate code depending on the expression type */\r
334                 switch(etype) {\r
335                 case LOCAL:{\r
336                         SQInteger p2 = _fs->PopTarget(); //src in OP_GET\r
337                         SQInteger p1 = _fs->PopTarget(); //key in OP_GET\r
338                         _fs->PushTarget(p1);\r
339                         //EmitCompArithLocal(tok, p1, p1, p2);\r
340                         _fs->AddInstruction(ChooseArithOpByToken(tok),p1, p2, p1, 0);\r
341                                    }\r
342                         break;\r
343                 case OBJECT:\r
344                 case BASE:\r
345                         {\r
346                                 SQInteger val = _fs->PopTarget();\r
347                                 SQInteger key = _fs->PopTarget();\r
348                                 SQInteger src = _fs->PopTarget();\r
349                                 /* _OP_COMPARITH mixes dest obj and source val in the arg1 */\r
350                                 _fs->AddInstruction(_OP_COMPARITH, _fs->PushTarget(), (src<<16)|val, key, ChooseCompArithCharByToken(tok));\r
351                         }\r
352                         break;\r
353                 case OUTER:\r
354                         {\r
355                                 SQInteger val = _fs->TopTarget();\r
356                                 SQInteger tmp = _fs->PushTarget();\r
357                                 _fs->AddInstruction(_OP_GETOUTER,   tmp, pos);\r
358                                 _fs->AddInstruction(ChooseArithOpByToken(tok), tmp, val, tmp, 0);\r
359                                 _fs->AddInstruction(_OP_SETOUTER, tmp, pos, tmp);\r
360                         }\r
361                         break;\r
362                 }\r
363         }\r
364         void CommaExpr()\r
365         {\r
366                 for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr());\r
367         }\r
368         void Expression()\r
369         {\r
370                  SQExpState es = _es;\r
371                 _es.etype     = EXPR;\r
372                 _es.epos      = -1;\r
373                 _es.donot_get = false;\r
374                 LogicalOrExp();\r
375                 switch(_token)  {\r
376                 case _SC('='):\r
377                 case TK_NEWSLOT:\r
378                 case TK_MINUSEQ:\r
379                 case TK_PLUSEQ:\r
380                 case TK_MULEQ:\r
381                 case TK_DIVEQ:\r
382                 case TK_MODEQ:{\r
383                         SQInteger op = _token;\r
384                         SQInteger ds = _es.etype;\r
385                         SQInteger pos = _es.epos;\r
386                         if(ds == EXPR) Error(_SC("can't assign expression"));\r
387                         Lex(); Expression();\r
388 \r
389                         switch(op){\r
390                         case TK_NEWSLOT:\r
391                                 if(ds == OBJECT || ds == BASE)\r
392                                         EmitDerefOp(_OP_NEWSLOT);\r
393                                 else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local\r
394                                         Error(_SC("can't 'create' a local slot"));\r
395                                 break;\r
396                         case _SC('='): //ASSIGN\r
397                                 switch(ds) {\r
398                                 case LOCAL:\r
399                                         {\r
400                                                 SQInteger src = _fs->PopTarget();\r
401                                                 SQInteger dst = _fs->TopTarget();\r
402                                                 _fs->AddInstruction(_OP_MOVE, dst, src);\r
403                                         }\r
404                                         break;\r
405                                 case OBJECT:\r
406                                 case BASE:\r
407                                         EmitDerefOp(_OP_SET);\r
408                                         break;\r
409                                 case OUTER:\r
410                                         {\r
411                                                 SQInteger src = _fs->PopTarget();\r
412                                                 SQInteger dst = _fs->PushTarget();\r
413                                                 _fs->AddInstruction(_OP_SETOUTER, dst, pos, src);\r
414                                         }\r
415                                 }\r
416                                 break;\r
417                         case TK_MINUSEQ:\r
418                         case TK_PLUSEQ:\r
419                         case TK_MULEQ:\r
420                         case TK_DIVEQ:\r
421                         case TK_MODEQ:\r
422                                 EmitCompoundArith(op, ds, pos);\r
423                                 break;\r
424                         }\r
425                         }\r
426                         break;\r
427                 case _SC('?'): {\r
428                         Lex();\r
429                         _fs->AddInstruction(_OP_JZ, _fs->PopTarget());\r
430                         SQInteger jzpos = _fs->GetCurrentPos();\r
431                         SQInteger trg = _fs->PushTarget();\r
432                         Expression();\r
433                         SQInteger first_exp = _fs->PopTarget();\r
434                         if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);\r
435                         SQInteger endfirstexp = _fs->GetCurrentPos();\r
436                         _fs->AddInstruction(_OP_JMP, 0, 0);\r
437                         Expect(_SC(':'));\r
438                         SQInteger jmppos = _fs->GetCurrentPos();\r
439                         Expression();\r
440                         SQInteger second_exp = _fs->PopTarget();\r
441                         if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);\r
442                         _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);\r
443                         _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1);\r
444                         _fs->SnoozeOpt();\r
445                         }\r
446                         break;\r
447                 }\r
448                 _es = es;\r
449         }\r
450         template<typename T> void BIN_EXP(SQOpcode op, T f,SQInteger op3 = 0)\r
451         {\r
452                 Lex(); (this->*f)();\r
453                 SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget();\r
454                 _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3);\r
455         }\r
456         void LogicalOrExp()\r
457         {\r
458                 LogicalAndExp();\r
459                 for(;;) if(_token == TK_OR) {\r
460                         SQInteger first_exp = _fs->PopTarget();\r
461                         SQInteger trg = _fs->PushTarget();\r
462                         _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0);\r
463                         SQInteger jpos = _fs->GetCurrentPos();\r
464                         if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);\r
465                         Lex(); LogicalOrExp();\r
466                         _fs->SnoozeOpt();\r
467                         SQInteger second_exp = _fs->PopTarget();\r
468                         if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);\r
469                         _fs->SnoozeOpt();\r
470                         _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));\r
471                         break;\r
472                 }else return;\r
473         }\r
474         void LogicalAndExp()\r
475         {\r
476                 BitwiseOrExp();\r
477                 for(;;) switch(_token) {\r
478                 case TK_AND: {\r
479                         SQInteger first_exp = _fs->PopTarget();\r
480                         SQInteger trg = _fs->PushTarget();\r
481                         _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0);\r
482                         SQInteger jpos = _fs->GetCurrentPos();\r
483                         if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);\r
484                         Lex(); LogicalAndExp();\r
485                         _fs->SnoozeOpt();\r
486                         SQInteger second_exp = _fs->PopTarget();\r
487                         if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);\r
488                         _fs->SnoozeOpt();\r
489                         _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));\r
490                         break;\r
491                         }\r
492                 case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break;\r
493                 case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break;\r
494                 default:\r
495                         return;\r
496                 }\r
497         }\r
498         void BitwiseOrExp()\r
499         {\r
500                 BitwiseXorExp();\r
501                 for(;;) if(_token == _SC('|'))\r
502                 {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR);\r
503                 }else return;\r
504         }\r
505         void BitwiseXorExp()\r
506         {\r
507                 BitwiseAndExp();\r
508                 for(;;) if(_token == _SC('^'))\r
509                 {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR);\r
510                 }else return;\r
511         }\r
512         void BitwiseAndExp()\r
513         {\r
514                 EqExp();\r
515                 for(;;) if(_token == _SC('&'))\r
516                 {BIN_EXP(_OP_BITW, &SQCompiler::EqExp,BW_AND);\r
517                 }else return;\r
518         }\r
519         void EqExp()\r
520         {\r
521                 CompExp();\r
522                 for(;;) switch(_token) {\r
523                 case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::CompExp); break;\r
524                 case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::CompExp); break;\r
525                 case TK_3WAYSCMP: BIN_EXP(_OP_CMP, &SQCompiler::CompExp,CMP_3W); break;\r
526                 default: return;        \r
527                 }\r
528         }\r
529         void CompExp()\r
530         {\r
531                 ShiftExp();\r
532                 for(;;) switch(_token) {\r
533                 case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break;\r
534                 case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break;\r
535                 case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break;\r
536                 case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break;\r
537                 default: return;        \r
538                 }\r
539         }\r
540         void ShiftExp()\r
541         {\r
542                 PlusExp();\r
543                 for(;;) switch(_token) {\r
544                 case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break;\r
545                 case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break;\r
546                 case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break;\r
547                 default: return;        \r
548                 }\r
549         }\r
550         SQOpcode ChooseArithOpByToken(SQInteger tok)\r
551         {\r
552                 switch(tok) {\r
553                         case TK_PLUSEQ: case '+': return _OP_ADD;\r
554                         case TK_MINUSEQ: case '-': return _OP_SUB;\r
555                         case TK_MULEQ: case '*': return _OP_MUL;\r
556                         case TK_DIVEQ: case '/': return _OP_DIV;\r
557                         case TK_MODEQ: case '%': return _OP_MOD;\r
558                         default: assert(0);\r
559                 }\r
560                 return _OP_ADD;\r
561         }\r
562         SQInteger ChooseCompArithCharByToken(SQInteger tok)\r
563         {\r
564                 SQInteger oper;\r
565                 switch(tok){\r
566                 case TK_MINUSEQ: oper = '-'; break;\r
567                 case TK_PLUSEQ: oper = '+'; break;\r
568                 case TK_MULEQ: oper = '*'; break;\r
569                 case TK_DIVEQ: oper = '/'; break;\r
570                 case TK_MODEQ: oper = '%'; break;\r
571                 default: oper = 0; //shut up compiler\r
572                         assert(0); break;\r
573                 };\r
574                 return oper;\r
575         }\r
576         void PlusExp()\r
577         {\r
578                 MultExp();\r
579                 for(;;) switch(_token) {\r
580                 case _SC('+'): case _SC('-'):\r
581                         BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::MultExp); break;\r
582                 default: return;\r
583                 }\r
584         }\r
585         \r
586         void MultExp()\r
587         {\r
588                 PrefixedExpr();\r
589                 for(;;) switch(_token) {\r
590                 case _SC('*'): case _SC('/'): case _SC('%'):\r
591                         BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::PrefixedExpr); break;\r
592                 default: return;\r
593                 }\r
594         }\r
595         //if 'pos' != -1 the previous variable is a local variable\r
596         void PrefixedExpr()\r
597         {\r
598                 SQInteger pos = Factor();\r
599                 for(;;) {\r
600                         switch(_token) {\r
601                         case _SC('.'):\r
602                                 pos = -1;\r
603                                 Lex(); \r
604 \r
605                                 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));\r
606                                 if(_es.etype==BASE) {\r
607                                         Emit2ArgsOP(_OP_GET);\r
608                                         pos = _fs->TopTarget();\r
609                                         _es.etype = EXPR;\r
610                                         _es.epos   = pos;\r
611                                 }\r
612                                 else {\r
613                                         if(NeedGet()) {\r
614                                                 Emit2ArgsOP(_OP_GET);\r
615                                         }\r
616                                         _es.etype = OBJECT;\r
617                                 }\r
618                                 break;\r
619                         case _SC('['):\r
620                                 if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration"));\r
621                                 Lex(); Expression(); Expect(_SC(']')); \r
622                                 pos = -1;\r
623                                 if(_es.etype==BASE) {\r
624                                         Emit2ArgsOP(_OP_GET);\r
625                                         pos = _fs->TopTarget();\r
626                                         _es.etype = EXPR;\r
627                                         _es.epos   = pos;\r
628                                 }\r
629                                 else {\r
630                                         if(NeedGet()) {\r
631                                                 Emit2ArgsOP(_OP_GET);\r
632                                         }\r
633                                         _es.etype = OBJECT;\r
634                                 }\r
635                                 break;\r
636                         case TK_MINUSMINUS:\r
637                         case TK_PLUSPLUS:\r
638                                 {\r
639                                         if(IsEndOfStatement()) return;\r
640                                         SQInteger diff = (_token==TK_MINUSMINUS) ? -1 : 1;\r
641                                         Lex();\r
642                                         switch(_es.etype)\r
643                                         {\r
644                                                 case EXPR: Error(_SC("can't '++' or '--' an expression")); break;\r
645                                                 case OBJECT:\r
646                                                 case BASE:\r
647                                                         Emit2ArgsOP(_OP_PINC, diff);\r
648                                                         break;\r
649                                                 case LOCAL: {\r
650                                                         SQInteger src = _fs->PopTarget();\r
651                                                         _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, diff);\r
652                                                                         }\r
653                                                         break;\r
654                                                 case OUTER: {\r
655                                                         SQInteger tmp1 = _fs->PushTarget();\r
656                                                         SQInteger tmp2 = _fs->PushTarget();\r
657                                                         _fs->AddInstruction(_OP_GETOUTER, tmp2, _es.epos);\r
658                                                         _fs->AddInstruction(_OP_PINCL,    tmp1, tmp2, 0, diff);\r
659                                                         _fs->AddInstruction(_OP_SETOUTER, tmp2, _es.epos, tmp2);\r
660                                                         _fs->PopTarget();\r
661                                                 }\r
662                                         }\r
663                                 }\r
664                                 return;\r
665                                 break;  \r
666                         case _SC('('): \r
667                                 switch(_es.etype) {\r
668                                         case OBJECT: {\r
669                                                 SQInteger key     = _fs->PopTarget();  /* location of the key */\r
670                                                 SQInteger table   = _fs->PopTarget();  /* location of the object */\r
671                                                 SQInteger closure = _fs->PushTarget(); /* location for the closure */\r
672                                                 SQInteger ttarget = _fs->PushTarget(); /* location for 'this' pointer */\r
673                                                 _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget);\r
674                                                 }\r
675                                                 break;\r
676                                         case BASE:\r
677                                                 //Emit2ArgsOP(_OP_GET);\r
678                                                 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);\r
679                                                 break;\r
680                                         case OUTER:\r
681                                                 _fs->AddInstruction(_OP_GETOUTER, _fs->PushTarget(), _es.epos);\r
682                                                 _fs->AddInstruction(_OP_MOVE,     _fs->PushTarget(), 0);\r
683                                                 break;\r
684                                         default:\r
685                                                 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);\r
686                                 }\r
687                                 _es.etype = EXPR;\r
688                                 Lex();\r
689                                 FunctionCallArgs();\r
690                                 break;\r
691                         default: return;\r
692                         }\r
693                 }\r
694         }\r
695         SQInteger Factor()\r
696         {\r
697                 _es.etype = EXPR;\r
698                 switch(_token)\r
699                 {\r
700                 case TK_STRING_LITERAL:\r
701                         _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1)));\r
702                         Lex(); \r
703                         break;\r
704                 case TK_BASE:\r
705                         Lex();\r
706                         _fs->AddInstruction(_OP_GETBASE, _fs->PushTarget());\r
707                         _es.etype  = BASE;\r
708                         _es.epos   = _fs->TopTarget();\r
709                         return (_es.epos);\r
710                         break;\r
711                 case TK_IDENTIFIER:\r
712                 case TK_CONSTRUCTOR:\r
713                 case TK_THIS:{\r
714                                 SQObject id;\r
715                                 SQObject constant;\r
716 \r
717                                 switch(_token) {\r
718                                         case TK_IDENTIFIER:  id = _fs->CreateString(_lex._svalue);       break;\r
719                                         case TK_THIS:        id = _fs->CreateString(_SC("this"));        break;\r
720                                         case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor")); break;\r
721                                 }\r
722 \r
723                                 SQInteger pos = -1;\r
724                                 Lex();\r
725                                 if((pos = _fs->GetLocalVariable(id)) != -1) {\r
726                                         /* Handle a local variable (includes 'this') */\r
727                                         _fs->PushTarget(pos);\r
728                                         _es.etype  = LOCAL;\r
729                                         _es.epos   = pos;\r
730                                 }\r
731 \r
732                                 else if((pos = _fs->GetOuterVariable(id)) != -1) {\r
733                                         /* Handle a free var */\r
734                                         if(NeedGet()) {\r
735                                                 _es.epos  = _fs->PushTarget();\r
736                                                 _fs->AddInstruction(_OP_GETOUTER, _es.epos, pos);       \r
737                                                 /* _es.etype = EXPR; already default value */\r
738                                         }\r
739                                         else {\r
740                                                 _es.etype = OUTER;\r
741                                                 _es.epos  = pos;\r
742                                         }\r
743                                 }\r
744 \r
745                                 else if(_fs->IsConstant(id, constant)) {\r
746                                         /* Handle named constant */\r
747                                         SQObjectPtr constval;\r
748                                         SQObject    constid;\r
749                                         if(type(constant) == OT_TABLE) {\r
750                                                 Expect('.');\r
751                                                 constid = Expect(TK_IDENTIFIER);\r
752                                                 if(!_table(constant)->Get(constid, constval)) {\r
753                                                         constval.Null();\r
754                                                         Error(_SC("invalid constant [%s.%s]"), _stringval(id), _stringval(constid));\r
755                                                 }\r
756                                         }\r
757                                         else {\r
758                                                 constval = constant;\r
759                                         }\r
760                                         _es.epos = _fs->PushTarget();\r
761 \r
762                                         /* generate direct or literal function depending on size */\r
763                                         SQObjectType ctype = type(constval);\r
764                                         switch(ctype) {\r
765                                                 case OT_INTEGER: EmitLoadConstInt(_integer(constval),_es.epos); break;\r
766                                                 case OT_FLOAT: EmitLoadConstFloat(_float(constval),_es.epos); break;\r
767                                                 default: _fs->AddInstruction(_OP_LOAD,_es.epos,_fs->GetConstant(constval)); break;\r
768                                         }\r
769                                         _es.etype = EXPR;\r
770                                 }\r
771                                 else {\r
772                                         /* Handle a non-local variable, aka a field. Push the 'this' pointer on\r
773                                         * the virtual stack (always found in offset 0, so no instruction needs to\r
774                                         * be generated), and push the key next. Generate an _OP_LOAD instruction\r
775                                         * for the latter. If we are not using the variable as a dref expr, generate\r
776                                         * the _OP_GET instruction.\r
777                                         */\r
778                                         _fs->PushTarget(0);\r
779                                         _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));\r
780                                         if(NeedGet()) {\r
781                                                 Emit2ArgsOP(_OP_GET);\r
782                                         }\r
783                                         _es.etype = OBJECT;\r
784                                 }\r
785                                 return _es.epos;\r
786                         }\r
787                         break;\r
788                 case TK_DOUBLE_COLON:  // "::"\r
789                         _fs->AddInstruction(_OP_LOADROOT, _fs->PushTarget());\r
790                         _es.etype = OBJECT;\r
791                         _token = _SC('.'); /* hack: drop into PrefixExpr, case '.'*/\r
792                         _es.epos = -1;\r
793                         return _es.epos;\r
794                         break;\r
795                 case TK_NULL: \r
796                         _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);\r
797                         Lex();\r
798                         break;\r
799                 case TK_INTEGER: EmitLoadConstInt(_lex._nvalue,-1); Lex();      break;\r
800                 case TK_FLOAT: EmitLoadConstFloat(_lex._fvalue,-1); Lex(); break;\r
801                 case TK_TRUE: case TK_FALSE:\r
802                         _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0);\r
803                         Lex();\r
804                         break;\r
805                 case _SC('['): {\r
806                                 _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,0,NOT_ARRAY);\r
807                                 SQInteger apos = _fs->GetCurrentPos(),key = 0;\r
808                                 Lex();\r
809                                 while(_token != _SC(']')) {\r
810                     Expression(); \r
811                                         if(_token == _SC(',')) Lex();\r
812                                         SQInteger val = _fs->PopTarget();\r
813                                         SQInteger array = _fs->TopTarget();\r
814                                         _fs->AddInstruction(_OP_APPENDARRAY, array, val, AAT_STACK);\r
815                                         key++;\r
816                                 }\r
817                                 _fs->SetIntructionParam(apos, 1, key);\r
818                                 Lex();\r
819                         }\r
820                         break;\r
821                 case _SC('{'):\r
822                         _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE);\r
823                         Lex();ParseTableOrClass(_SC(','),_SC('}'));\r
824                         break;\r
825                 case TK_FUNCTION: FunctionExp(_token);break;\r
826                 case _SC('@'): FunctionExp(_token,true);break;\r
827                 case TK_CLASS: Lex(); ClassExp();break;\r
828                 case _SC('-'): \r
829                         Lex(); \r
830                         switch(_token) {\r
831                         case TK_INTEGER: EmitLoadConstInt(-_lex._nvalue,-1); Lex(); break;\r
832                         case TK_FLOAT: EmitLoadConstFloat(-_lex._fvalue,-1); Lex(); break;\r
833                         default: UnaryOP(_OP_NEG);\r
834                         }\r
835                         break;\r
836                 case _SC('!'): Lex(); UnaryOP(_OP_NOT); break;\r
837                 case _SC('~'): \r
838                         Lex(); \r
839                         if(_token == TK_INTEGER)  { EmitLoadConstInt(~_lex._nvalue,-1); Lex(); break; }\r
840                         UnaryOP(_OP_BWNOT); \r
841                         break;\r
842                 case TK_TYPEOF : Lex() ;UnaryOP(_OP_TYPEOF); break;\r
843                 case TK_RESUME : Lex(); UnaryOP(_OP_RESUME); break;\r
844                 case TK_CLONE : Lex(); UnaryOP(_OP_CLONE); break;\r
845                 case TK_MINUSMINUS : \r
846                 case TK_PLUSPLUS :PrefixIncDec(_token); break;\r
847                 case TK_DELETE : DeleteExpr(); break;\r
848                 case _SC('('): Lex(); CommaExpr(); Expect(_SC(')'));\r
849                         break;\r
850                 default: Error(_SC("expression expected"));\r
851                 }\r
852                 return -1;\r
853         }\r
854         void EmitLoadConstInt(SQInteger value,SQInteger target)\r
855         {\r
856                 if(target < 0) {\r
857                         target = _fs->PushTarget();\r
858                 }\r
859                 if((value & (~((SQInteger)0xFFFFFFFF))) == 0) { //does it fit in 32 bits?\r
860                         _fs->AddInstruction(_OP_LOADINT, target,value);\r
861                 }\r
862                 else {\r
863                         _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value));\r
864                 }\r
865         }\r
866         void EmitLoadConstFloat(SQFloat value,SQInteger target)\r
867         {\r
868                 if(target < 0) {\r
869                         target = _fs->PushTarget();\r
870                 }\r
871                 if(sizeof(SQFloat) == sizeof(SQInt32)) {\r
872                         _fs->AddInstruction(_OP_LOADFLOAT, target,*((SQInt32 *)&value));\r
873                 }\r
874                 else {\r
875                         _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value));\r
876                 }\r
877         }\r
878         void UnaryOP(SQOpcode op)\r
879         {\r
880                 PrefixedExpr();\r
881                 SQInteger src = _fs->PopTarget();\r
882                 _fs->AddInstruction(op, _fs->PushTarget(), src);\r
883         }\r
884         bool NeedGet()\r
885         {\r
886                 switch(_token) {\r
887                 case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_MODEQ: case TK_MULEQ:\r
888             case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS:\r
889                         return false;\r
890                 }\r
891                 return (!_es.donot_get || ( _es.donot_get && (_token == _SC('.') || _token == _SC('['))));\r
892         }\r
893         void FunctionCallArgs()\r
894         {\r
895                 SQInteger nargs = 1;//this\r
896                  while(_token != _SC(')')) {\r
897                          Expression();\r
898                          MoveIfCurrentTargetIsLocal();\r
899                          nargs++; \r
900                          if(_token == _SC(',')){ \r
901                                  Lex(); \r
902                                  if(_token == ')') Error(_SC("expression expected, found ')'"));\r
903                          }\r
904                  }\r
905                  Lex();\r
906                  for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget();\r
907                  SQInteger stackbase = _fs->PopTarget();\r
908                  SQInteger closure = _fs->PopTarget();\r
909          _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs);\r
910         }\r
911         void ParseTableOrClass(SQInteger separator,SQInteger terminator)\r
912         {\r
913                 SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0;\r
914                 while(_token != terminator) {\r
915                         bool hasattrs = false;\r
916                         bool isstatic = false;\r
917                         //check if is an attribute\r
918                         if(separator == ';') {\r
919                                 if(_token == TK_ATTR_OPEN) {\r
920                                         _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex();\r
921                                         ParseTableOrClass(',',TK_ATTR_CLOSE);\r
922                                         hasattrs = true;\r
923                                 }\r
924                                 if(_token == TK_STATIC) {\r
925                                         isstatic = true;\r
926                                         Lex();\r
927                                 }\r
928                         }\r
929                         switch(_token) {\r
930                         case TK_FUNCTION:\r
931                         case TK_CONSTRUCTOR:{\r
932                                 SQInteger tk = _token;\r
933                                 Lex();\r
934                                 SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor"));\r
935                                 Expect(_SC('('));\r
936                                 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));\r
937                                 CreateFunction(id);\r
938                                 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);\r
939                                                                 }\r
940                                                                 break;\r
941                         case _SC('['):\r
942                                 Lex(); CommaExpr(); Expect(_SC(']'));\r
943                                 Expect(_SC('=')); Expression();\r
944                                 break;\r
945                         case TK_STRING_LITERAL: //JSON\r
946                                 if(separator == ',') { //only works for tables\r
947                                         _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_STRING_LITERAL)));\r
948                                         Expect(_SC(':')); Expression();\r
949                                         break;\r
950                                 }\r
951                         default :\r
952                                 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));\r
953                                 Expect(_SC('=')); Expression();\r
954                         }\r
955                         if(_token == separator) Lex();//optional comma/semicolon\r
956                         nkeys++;\r
957                         SQInteger val = _fs->PopTarget();\r
958                         SQInteger key = _fs->PopTarget();\r
959                         SQInteger attrs = hasattrs ? _fs->PopTarget():-1;\r
960                         assert((hasattrs && (attrs == key-1)) || !hasattrs);\r
961                         unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0);\r
962                         SQInteger table = _fs->TopTarget(); //<<BECAUSE OF THIS NO COMMON EMIT FUNC IS POSSIBLE\r
963                         if(separator == _SC(',')) { //hack recognizes a table from the separator\r
964                                 _fs->AddInstruction(_OP_NEWSLOT, 0xFF, table, key, val);\r
965                         }\r
966                         else {\r
967                                 _fs->AddInstruction(_OP_NEWSLOTA, flags, table, key, val); //this for classes only as it invokes _newmember\r
968                         }\r
969                 }\r
970                 if(separator == _SC(',')) //hack recognizes a table from the separator\r
971                         _fs->SetIntructionParam(tpos, 1, nkeys);\r
972                 Lex();\r
973         }\r
974         void LocalDeclStatement()\r
975         {\r
976                 SQObject varname;\r
977                 Lex();\r
978                 if( _token == TK_FUNCTION) {\r
979                         Lex();\r
980                         varname = Expect(TK_IDENTIFIER);\r
981                         Expect(_SC('('));\r
982                         CreateFunction(varname,false);\r
983                         _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);\r
984                         _fs->PopTarget();\r
985                         _fs->PushLocalVariable(varname);\r
986                         return;\r
987                 }\r
988 \r
989                 do {\r
990                         varname = Expect(TK_IDENTIFIER);\r
991                         if(_token == _SC('=')) {\r
992                                 Lex(); Expression();\r
993                                 SQInteger src = _fs->PopTarget();\r
994                                 SQInteger dest = _fs->PushTarget();\r
995                                 if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src);\r
996                         }\r
997                         else{\r
998                                 _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);\r
999                         }\r
1000                         _fs->PopTarget();\r
1001                         _fs->PushLocalVariable(varname);\r
1002                         if(_token == _SC(',')) Lex(); else break;\r
1003                 } while(1);\r
1004         }\r
1005         void IfStatement()\r
1006         {\r
1007                 SQInteger jmppos;\r
1008                 bool haselse = false;\r
1009                 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));\r
1010                 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());\r
1011                 SQInteger jnepos = _fs->GetCurrentPos();\r
1012                 BEGIN_SCOPE();\r
1013                 \r
1014                 Statement();\r
1015                 //\r
1016                 if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon();\r
1017                 \r
1018                 END_SCOPE();\r
1019                 SQInteger endifblock = _fs->GetCurrentPos();\r
1020                 if(_token == TK_ELSE){\r
1021                         haselse = true;\r
1022                         BEGIN_SCOPE();\r
1023                         _fs->AddInstruction(_OP_JMP);\r
1024                         jmppos = _fs->GetCurrentPos();\r
1025                         Lex();\r
1026                         Statement(); OptionalSemicolon();\r
1027                         END_SCOPE();\r
1028                         _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);\r
1029                 }\r
1030                 _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0));\r
1031         }\r
1032         void WhileStatement()\r
1033         {\r
1034                 SQInteger jzpos, jmppos;\r
1035                 jmppos = _fs->GetCurrentPos();\r
1036                 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));\r
1037                 \r
1038                 BEGIN_BREAKBLE_BLOCK();\r
1039                 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());\r
1040                 jzpos = _fs->GetCurrentPos();\r
1041                 BEGIN_SCOPE();\r
1042                 \r
1043                 Statement();\r
1044                 \r
1045                 END_SCOPE();\r
1046                 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);\r
1047                 _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);\r
1048                 \r
1049                 END_BREAKBLE_BLOCK(jmppos);\r
1050         }\r
1051         void DoWhileStatement()\r
1052         {\r
1053                 Lex();\r
1054                 SQInteger jmptrg = _fs->GetCurrentPos();\r
1055                 BEGIN_BREAKBLE_BLOCK()\r
1056                 BEGIN_SCOPE();\r
1057                 Statement();\r
1058                 END_SCOPE();\r
1059                 Expect(TK_WHILE);\r
1060                 SQInteger continuetrg = _fs->GetCurrentPos();\r
1061                 Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));\r
1062                 _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), 1);\r
1063                 _fs->AddInstruction(_OP_JMP, 0, jmptrg - _fs->GetCurrentPos() - 1);\r
1064                 END_BREAKBLE_BLOCK(continuetrg);\r
1065         }\r
1066         void ForStatement()\r
1067         {\r
1068                 Lex();\r
1069                 BEGIN_SCOPE();\r
1070                 Expect(_SC('('));\r
1071                 if(_token == TK_LOCAL) LocalDeclStatement();\r
1072                 else if(_token != _SC(';')){\r
1073                         CommaExpr();\r
1074                         _fs->PopTarget();\r
1075                 }\r
1076                 Expect(_SC(';'));\r
1077                 _fs->SnoozeOpt();\r
1078                 SQInteger jmppos = _fs->GetCurrentPos();\r
1079                 SQInteger jzpos = -1;\r
1080                 if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); }\r
1081                 Expect(_SC(';'));\r
1082                 _fs->SnoozeOpt();\r
1083                 SQInteger expstart = _fs->GetCurrentPos() + 1;\r
1084                 if(_token != _SC(')')) {\r
1085                         CommaExpr();\r
1086                         _fs->PopTarget();\r
1087                 }\r
1088                 Expect(_SC(')'));\r
1089                 _fs->SnoozeOpt();\r
1090                 SQInteger expend = _fs->GetCurrentPos();\r
1091                 SQInteger expsize = (expend - expstart) + 1;\r
1092                 SQInstructionVec exp;\r
1093                 if(expsize > 0) {\r
1094                         for(SQInteger i = 0; i < expsize; i++)\r
1095                                 exp.push_back(_fs->GetInstruction(expstart + i));\r
1096                         _fs->PopInstructions(expsize);\r
1097                 }\r
1098                 BEGIN_BREAKBLE_BLOCK()\r
1099                 Statement();\r
1100                 SQInteger continuetrg = _fs->GetCurrentPos();\r
1101                 if(expsize > 0) {\r
1102                         for(SQInteger i = 0; i < expsize; i++)\r
1103                                 _fs->AddInstruction(exp[i]);\r
1104                 }\r
1105                 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0);\r
1106                 if(jzpos>  0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);\r
1107                 END_SCOPE();\r
1108                 \r
1109                 END_BREAKBLE_BLOCK(continuetrg);\r
1110         }\r
1111         void ForEachStatement()\r
1112         {\r
1113                 SQObject idxname, valname;\r
1114                 Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER);\r
1115                 if(_token == _SC(',')) {\r
1116                         idxname = valname;\r
1117                         Lex(); valname = Expect(TK_IDENTIFIER);\r
1118                 }\r
1119                 else{\r
1120                         idxname = _fs->CreateString(_SC("@INDEX@"));\r
1121                 }\r
1122                 Expect(TK_IN);\r
1123                 \r
1124                 //save the stack size\r
1125                 BEGIN_SCOPE();\r
1126                 //put the table in the stack(evaluate the table expression)\r
1127                 Expression(); Expect(_SC(')'));\r
1128                 SQInteger container = _fs->TopTarget();\r
1129                 //push the index local var\r
1130                 SQInteger indexpos = _fs->PushLocalVariable(idxname);\r
1131                 _fs->AddInstruction(_OP_LOADNULLS, indexpos,1);\r
1132                 //push the value local var\r
1133                 SQInteger valuepos = _fs->PushLocalVariable(valname);\r
1134                 _fs->AddInstruction(_OP_LOADNULLS, valuepos,1);\r
1135                 //push reference index\r
1136                 SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible\r
1137                 _fs->AddInstruction(_OP_LOADNULLS, itrpos,1);\r
1138                 SQInteger jmppos = _fs->GetCurrentPos();\r
1139                 _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos);\r
1140                 SQInteger foreachpos = _fs->GetCurrentPos();\r
1141                 _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos);\r
1142                 //generate the statement code\r
1143                 BEGIN_BREAKBLE_BLOCK()\r
1144                 Statement();\r
1145                 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);\r
1146                 _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos);\r
1147                 _fs->SetIntructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos);\r
1148                 END_BREAKBLE_BLOCK(foreachpos - 1);\r
1149                 //restore the local variable stack(remove index,val and ref idx)\r
1150                 _fs->PopTarget();\r
1151                 END_SCOPE();\r
1152         }\r
1153         void SwitchStatement()\r
1154         {\r
1155                 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));\r
1156                 Expect(_SC('{'));\r
1157                 SQInteger expr = _fs->TopTarget();\r
1158                 bool bfirst = true;\r
1159                 SQInteger tonextcondjmp = -1;\r
1160                 SQInteger skipcondjmp = -1;\r
1161                 SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size();\r
1162                 _fs->_breaktargets.push_back(0);\r
1163                 while(_token == TK_CASE) {\r
1164                         if(!bfirst) {\r
1165                                 _fs->AddInstruction(_OP_JMP, 0, 0);\r
1166                                 skipcondjmp = _fs->GetCurrentPos();\r
1167                                 _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);\r
1168                         }\r
1169                         //condition\r
1170                         Lex(); Expression(); Expect(_SC(':'));\r
1171                         SQInteger trg = _fs->PopTarget();\r
1172                         _fs->AddInstruction(_OP_EQ, trg, trg, expr);\r
1173                         _fs->AddInstruction(_OP_JZ, trg, 0);\r
1174                         //end condition\r
1175                         if(skipcondjmp != -1) {\r
1176                                 _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp));\r
1177                         }\r
1178                         tonextcondjmp = _fs->GetCurrentPos();\r
1179                         BEGIN_SCOPE();\r
1180                         Statements();\r
1181                         END_SCOPE();\r
1182                         bfirst = false;\r
1183                 }\r
1184                 if(tonextcondjmp != -1)\r
1185                         _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);\r
1186                 if(_token == TK_DEFAULT) {\r
1187                         Lex(); Expect(_SC(':'));\r
1188                         BEGIN_SCOPE();\r
1189                         Statements();\r
1190                         END_SCOPE();\r
1191                 }\r
1192                 Expect(_SC('}'));\r
1193                 _fs->PopTarget();\r
1194                 __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__;\r
1195                 if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__);\r
1196                 _fs->_breaktargets.pop_back();\r
1197         }\r
1198         void FunctionStatement()\r
1199         {\r
1200                 SQObject id;\r
1201                 Lex(); id = Expect(TK_IDENTIFIER);\r
1202                 _fs->PushTarget(0);\r
1203                 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));\r
1204                 if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);\r
1205                 \r
1206                 while(_token == TK_DOUBLE_COLON) {\r
1207                         Lex();\r
1208                         id = Expect(TK_IDENTIFIER);\r
1209                         _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));\r
1210                         if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);\r
1211                 }\r
1212                 Expect(_SC('('));\r
1213                 CreateFunction(id);\r
1214                 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);\r
1215                 EmitDerefOp(_OP_NEWSLOT);\r
1216                 _fs->PopTarget();\r
1217         }\r
1218         void ClassStatement()\r
1219         {\r
1220                 SQExpState es;\r
1221                 Lex();\r
1222                 es = _es;\r
1223                 _es.donot_get = true;\r
1224                 PrefixedExpr();\r
1225                 if(_es.etype == EXPR) {\r
1226                         Error(_SC("invalid class name"));\r
1227                 }\r
1228                 else if(_es.etype == OBJECT || _es.etype == BASE) {\r
1229                         ClassExp();\r
1230                         EmitDerefOp(_OP_NEWSLOT);\r
1231                         _fs->PopTarget();\r
1232                 }\r
1233                 else {\r
1234                         Error(_SC("cannot create a class in a local with the syntax(class <local>)"));\r
1235                 }\r
1236                 _es = es;\r
1237         }\r
1238         SQObject ExpectScalar()\r
1239         {\r
1240                 SQObject val;\r
1241                 val._type = OT_NULL; val._unVal.nInteger = 0; //shut up GCC 4.x\r
1242                 switch(_token) {\r
1243                         case TK_INTEGER:\r
1244                                 val._type = OT_INTEGER;\r
1245                                 val._unVal.nInteger = _lex._nvalue;\r
1246                                 break;\r
1247                         case TK_FLOAT:\r
1248                                 val._type = OT_FLOAT;\r
1249                                 val._unVal.fFloat = _lex._fvalue;\r
1250                                 break;\r
1251                         case TK_STRING_LITERAL:\r
1252                                 val = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);\r
1253                                 break;\r
1254                         case '-':\r
1255                                 Lex();\r
1256                                 switch(_token)\r
1257                                 {\r
1258                                 case TK_INTEGER:\r
1259                                         val._type = OT_INTEGER;\r
1260                                         val._unVal.nInteger = -_lex._nvalue;\r
1261                                 break;\r
1262                                 case TK_FLOAT:\r
1263                                         val._type = OT_FLOAT;\r
1264                                         val._unVal.fFloat = -_lex._fvalue;\r
1265                                 break;\r
1266                                 default:\r
1267                                         Error(_SC("scalar expected : integer,float"));\r
1268                                 }\r
1269                                 break;\r
1270                         default:\r
1271                                 Error(_SC("scalar expected : integer,float or string"));\r
1272                 }\r
1273                 Lex();\r
1274                 return val;\r
1275         }\r
1276         void EnumStatement()\r
1277         {\r
1278                 Lex(); \r
1279                 SQObject id = Expect(TK_IDENTIFIER);\r
1280                 Expect(_SC('{'));\r
1281                 \r
1282                 SQObject table = _fs->CreateTable();\r
1283                 SQInteger nval = 0;\r
1284                 while(_token != _SC('}')) {\r
1285                         SQObject key = Expect(TK_IDENTIFIER);\r
1286                         SQObject val;\r
1287                         if(_token == _SC('=')) {\r
1288                                 Lex();\r
1289                                 val = ExpectScalar();\r
1290                         }\r
1291                         else {\r
1292                                 val._type = OT_INTEGER;\r
1293                                 val._unVal.nInteger = nval++;\r
1294                         }\r
1295                         _table(table)->NewSlot(SQObjectPtr(key),SQObjectPtr(val));\r
1296                         if(_token == ',') Lex();\r
1297                 }\r
1298                 SQTable *enums = _table(_ss(_vm)->_consts);\r
1299                 SQObjectPtr strongid = id; \r
1300                 enums->NewSlot(SQObjectPtr(strongid),SQObjectPtr(table));\r
1301                 strongid.Null();\r
1302                 Lex();\r
1303         }\r
1304         void TryCatchStatement()\r
1305         {\r
1306                 SQObject exid;\r
1307                 Lex();\r
1308                 _fs->AddInstruction(_OP_PUSHTRAP,0,0);\r
1309                 _fs->_traps++;\r
1310                 if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++;\r
1311                 if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++;\r
1312                 SQInteger trappos = _fs->GetCurrentPos();\r
1313                 {\r
1314                         BEGIN_SCOPE();\r
1315                         Statement();\r
1316                         END_SCOPE();\r
1317                 }\r
1318                 _fs->_traps--;\r
1319                 _fs->AddInstruction(_OP_POPTRAP, 1, 0);\r
1320                 if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--;\r
1321                 if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--;\r
1322                 _fs->AddInstruction(_OP_JMP, 0, 0);\r
1323                 SQInteger jmppos = _fs->GetCurrentPos();\r
1324                 _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos));\r
1325                 Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')'));\r
1326                 {\r
1327                         BEGIN_SCOPE();\r
1328                         SQInteger ex_target = _fs->PushLocalVariable(exid);\r
1329                         _fs->SetIntructionParam(trappos, 0, ex_target);\r
1330                         Statement();\r
1331                         _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0);\r
1332                         END_SCOPE();\r
1333                 }\r
1334         }\r
1335         void FunctionExp(SQInteger ftype,bool lambda = false)\r
1336         {\r
1337                 Lex(); Expect(_SC('('));\r
1338                 SQObjectPtr dummy;\r
1339                 CreateFunction(dummy,lambda);\r
1340                 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1);\r
1341         }\r
1342         void ClassExp()\r
1343         {\r
1344                 SQInteger base = -1;\r
1345                 SQInteger attrs = -1;\r
1346                 if(_token == TK_EXTENDS) {\r
1347                         Lex(); Expression();\r
1348                         base = _fs->TopTarget();\r
1349                 }\r
1350                 if(_token == TK_ATTR_OPEN) {\r
1351                         Lex();\r
1352                         _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE);\r
1353                         ParseTableOrClass(_SC(','),TK_ATTR_CLOSE);\r
1354                         attrs = _fs->TopTarget();\r
1355                 }\r
1356                 Expect(_SC('{'));\r
1357                 if(attrs != -1) _fs->PopTarget();\r
1358                 if(base != -1) _fs->PopTarget();\r
1359                 _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), base, attrs,NOT_CLASS);\r
1360                 ParseTableOrClass(_SC(';'),_SC('}'));\r
1361         }\r
1362         void DeleteExpr()\r
1363         {\r
1364                 SQExpState es;\r
1365                 Lex();\r
1366                 es = _es;\r
1367                 _es.donot_get = true;\r
1368                 PrefixedExpr();\r
1369                 if(_es.etype==EXPR) Error(_SC("can't delete an expression"));\r
1370                 if(_es.etype==OBJECT || _es.etype==BASE) {\r
1371                         Emit2ArgsOP(_OP_DELETE);\r
1372                 }\r
1373                 else {\r
1374                         Error(_SC("cannot delete an (outer) local"));\r
1375                 }\r
1376                 _es = es;\r
1377         }\r
1378         void PrefixIncDec(SQInteger token)\r
1379         {\r
1380                 SQExpState  es;\r
1381                 SQInteger diff = (token==TK_MINUSMINUS) ? -1 : 1;\r
1382                 Lex();\r
1383                 es = _es;\r
1384                 _es.donot_get = true;\r
1385                 PrefixedExpr();\r
1386                 if(_es.etype==EXPR) {\r
1387                         Error(_SC("can't '++' or '--' an expression"));\r
1388                 }\r
1389                 else if(_es.etype==OBJECT || _es.etype==BASE) {\r
1390                         Emit2ArgsOP(_OP_INC, diff);\r
1391                 }\r
1392                 else if(_es.etype==LOCAL) {\r
1393                         SQInteger src = _fs->TopTarget();\r
1394                         _fs->AddInstruction(_OP_INCL, src, src, 0, diff);\r
1395                         \r
1396                 }\r
1397                 else if(_es.etype==OUTER) {\r
1398                         SQInteger tmp = _fs->PushTarget();\r
1399                         _fs->AddInstruction(_OP_GETOUTER, tmp, _es.epos);\r
1400                         _fs->AddInstruction(_OP_INCL,     tmp, tmp, 0, diff);\r
1401                         _fs->AddInstruction(_OP_SETOUTER, tmp, _es.epos, tmp);\r
1402                 }\r
1403                 _es = es;\r
1404         }\r
1405         void CreateFunction(SQObject &name,bool lambda = false)\r
1406         {\r
1407                 SQFuncState *funcstate = _fs->PushChildState(_ss(_vm));\r
1408                 funcstate->_name = name;\r
1409                 SQObject paramname;\r
1410                 funcstate->AddParameter(_fs->CreateString(_SC("this")));\r
1411                 funcstate->_sourcename = _sourcename;\r
1412                 SQInteger defparams = 0;\r
1413                 while(_token!=_SC(')')) {\r
1414                         if(_token == TK_VARPARAMS) {\r
1415                                 if(defparams > 0) Error(_SC("function with default parameters cannot have variable number of parameters"));\r
1416                                 funcstate->AddParameter(_fs->CreateString(_SC("vargv")));\r
1417                                 funcstate->_varparams = true;\r
1418                                 Lex();\r
1419                                 if(_token != _SC(')')) Error(_SC("expected ')'"));\r
1420                                 break;\r
1421                         }\r
1422                         else {\r
1423                                 paramname = Expect(TK_IDENTIFIER);\r
1424                                 funcstate->AddParameter(paramname);\r
1425                                 if(_token == _SC('=')) { \r
1426                                         Lex();\r
1427                                         Expression();\r
1428                                         funcstate->AddDefaultParam(_fs->TopTarget());\r
1429                                         defparams++;\r
1430                                 }\r
1431                                 else {\r
1432                                         if(defparams > 0) Error(_SC("expected '='"));\r
1433                                 }\r
1434                                 if(_token == _SC(',')) Lex();\r
1435                                 else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));\r
1436                         }\r
1437                 }\r
1438                 Expect(_SC(')'));\r
1439                 for(SQInteger n = 0; n < defparams; n++) {\r
1440                         _fs->PopTarget();\r
1441                 }\r
1442                                 \r
1443                 SQFuncState *currchunk = _fs;\r
1444                 _fs = funcstate;\r
1445                 if(lambda) { \r
1446                         Expression(); \r
1447                         _fs->AddInstruction(_OP_RETURN, 1, _fs->PopTarget());}\r
1448                 else { \r
1449                         Statement(false); \r
1450                 }\r
1451                 funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true);\r
1452         funcstate->AddInstruction(_OP_RETURN, -1);\r
1453                 funcstate->SetStackSize(0);\r
1454 \r
1455                 SQFunctionProto *func = funcstate->BuildProto();\r
1456 #ifdef _DEBUG_DUMP\r
1457                 funcstate->Dump(func);\r
1458 #endif\r
1459                 _fs = currchunk;\r
1460                 _fs->_functions.push_back(func);\r
1461                 _fs->PopChildState();\r
1462         }\r
1463         void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve)\r
1464         {\r
1465                 while(ntoresolve > 0) {\r
1466                         SQInteger pos = funcstate->_unresolvedbreaks.back();\r
1467                         funcstate->_unresolvedbreaks.pop_back();\r
1468                         //set the jmp instruction\r
1469                         funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0);\r
1470                         ntoresolve--;\r
1471                 }\r
1472         }\r
1473         void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos)\r
1474         {\r
1475                 while(ntoresolve > 0) {\r
1476                         SQInteger pos = funcstate->_unresolvedcontinues.back();\r
1477                         funcstate->_unresolvedcontinues.pop_back();\r
1478                         //set the jmp instruction\r
1479                         funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0);\r
1480                         ntoresolve--;\r
1481                 }\r
1482         }\r
1483 private:\r
1484         SQInteger _token;\r
1485         SQFuncState *_fs;\r
1486         SQObjectPtr _sourcename;\r
1487         SQLexer _lex;\r
1488         bool _lineinfo;\r
1489         bool _raiseerror;\r
1490         SQInteger _debugline;\r
1491         SQInteger _debugop;\r
1492         SQExpState   _es;\r
1493         SQScope _scope;\r
1494         SQChar *compilererror;\r
1495         jmp_buf _errorjmp;\r
1496         SQVM *_vm;\r
1497 };\r
1498 \r
1499 bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo)\r
1500 {\r
1501         SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo);\r
1502         return p.Compile(out);\r
1503 }\r
1504 \r
1505 #endif\r