2 see copyright notice in squirrel.h
4 #include "sqpcheader.h"
9 #include "sqfuncproto.h"
10 #include "sqcompiler.h"
11 #include "sqfuncstate.h"
15 #define DEREF_NO_DEREF -1
16 #define DEREF_FIELD -2
22 _deref = DEREF_NO_DEREF;
24 _class_or_delete = false;
27 bool _class_or_delete;
33 typedef sqvector<ExpState> ExpStateVec;
35 #define _exst (_expstates.top())
37 #define BEGIN_BREAKBLE_BLOCK() int __nbreaks__=_fs->_unresolvedbreaks.size(); \
38 int __ncontinues__=_fs->_unresolvedcontinues.size(); \
39 _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0);
41 #define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \
42 __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \
43 if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \
44 if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \
45 _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();}
50 SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo)
53 _lex.Init(_ss(v), rg, up,ThrowError,this);
54 _sourcename = SQString::Create(_ss(v), sourcename);
55 _lineinfo = lineinfo;_raiseerror = raiseerror;
58 static void ThrowError(void *ud, const SQChar *s) {
59 SQCompiler *c = (SQCompiler *)ud;
62 void Error(const SQChar *s, ...)
64 static SQChar temp[256];
67 scvsprintf(temp, s, vl);
72 void Lex(){ _token = _lex.Lex();}
73 void PushExpState(){ _expstates.push_back(ExpState()); }
74 bool IsDerefToken(int tok)
77 case _SC('='): case _SC('('): case TK_NEWSLOT:
78 case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS: return true;
82 ExpState PopExpState()
84 ExpState ret = _expstates.top();
85 _expstates.pop_back();
88 SQObject Expect(int tok)
92 if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) {
93 //ret = SQString::Create(_ss(_vm),_SC("constructor"));
97 const SQChar *etypename;
102 etypename = _SC("IDENTIFIER");
104 case TK_STRING_LITERAL:
105 etypename = _SC("STRING_LITERAL");
108 etypename = _SC("INTEGER");
111 etypename = _SC("FLOAT");
114 etypename = _lex.Tok2Str(tok);
116 Error(_SC("expected '%s'"), etypename);
118 Error(_SC("expected '%c'"), tok);
125 ret = _fs->CreateString(_lex._svalue);
127 case TK_STRING_LITERAL:
128 ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);
131 ret = SQObjectPtr(_lex._nvalue);
134 ret = SQObjectPtr(_lex._fvalue);
140 bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); }
141 void OptionalSemicolon()
143 if(_token == _SC(';')) { Lex(); return; }
144 if(!IsEndOfStatement()) {
145 Error(_SC("end of statement expected (; or lf)"));
148 void MoveIfCurrentTargetIsLocal() {
149 int trg = _fs->TopTarget();
150 if(_fs->IsLocal(trg)) {
151 trg = _fs->PopTarget(); //no pops the target and move it
152 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg);
155 bool Compile(SQObjectPtr &o)
160 SQFuncState funcstate(_ss(_vm), SQFunctionProto::Create(), NULL,ThrowError,this);
161 _funcproto(funcstate._func)->_name = SQString::Create(_ss(_vm), _SC("main"));
163 _fs->AddParameter(_fs->CreateString(_SC("this")));
164 _funcproto(_fs->_func)->_sourcename = _sourcename;
165 int stacksize = _fs->GetStackSize();
166 if(setjmp(_errorjmp) == 0) {
170 if(_lex._prevtoken != _SC('}')) OptionalSemicolon();
172 CleanStack(stacksize);
173 _fs->AddLineInfos(_lex._currentline, _lineinfo, true);
174 _fs->AddInstruction(_OP_RETURN, 0xFF);
175 _funcproto(_fs->_func)->_stacksize = _fs->_stacksize;
176 _fs->SetStackSize(0);
184 if(_raiseerror && _ss(_vm)->_compilererrorhandler) {
185 _ss(_vm)->_compilererrorhandler(_vm, compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"),
186 _lex._currentline, _lex._currentcolumn);
188 _vm->_lasterror = SQString::Create(_ss(_vm), compilererror, -1);
195 while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) {
197 if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();
202 _fs->AddLineInfos(_lex._currentline, _lineinfo);
204 case _SC(';'): Lex(); break;
205 case TK_IF: IfStatement(); break;
206 case TK_WHILE: WhileStatement(); break;
207 case TK_DO: DoWhileStatement(); break;
208 case TK_FOR: ForStatement(); break;
209 case TK_FOREACH: ForEachStatement(); break;
210 case TK_SWITCH: SwitchStatement(); break;
211 case TK_LOCAL: LocalDeclStatement(); break;
215 if(_token == TK_RETURN) {
221 _funcproto(_fs->_func)->_bgenerator = true;
224 if(!IsEndOfStatement()) {
225 int retexp = _fs->GetCurrentPos()+1;
227 if(op == _OP_RETURN && _fs->_traps > 0)
228 _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0);
229 _fs->_returnexp = retexp;
230 _fs->AddInstruction(op, 1, _fs->PopTarget());
233 if(op == _OP_RETURN && _fs->_traps > 0)
234 _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0);
235 _fs->_returnexp = -1;
236 _fs->AddInstruction(op, 0xFF);
240 if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block"));
241 if(_fs->_breaktargets.top() > 0){
242 _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);
244 _fs->AddInstruction(_OP_JMP, 0, -1234);
245 _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos());
249 if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block"));
250 if(_fs->_continuetargets.top() > 0) {
251 _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);
253 _fs->AddInstruction(_OP_JMP, 0, -1234);
254 _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos());
264 int stacksize = _fs->GetStackSize();
268 _fs->SetStackSize(stacksize);
277 _fs->AddInstruction(_OP_THROW, _fs->PopTarget());
286 void EmitDerefOp(SQOpcode op)
288 int val = _fs->PopTarget();
289 int key = _fs->PopTarget();
290 int src = _fs->PopTarget();
291 _fs->AddInstruction(op,_fs->PushTarget(),src,key,val);
293 void Emit2ArgsOP(SQOpcode op, int p3 = 0)
295 int p2 = _fs->PopTarget(); //src in OP_GET
296 int p1 = _fs->PopTarget(); //key in OP_GET
297 _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3);
299 void EmitCompoundArith(int tok,bool deref)
303 case TK_MINUSEQ: oper = '-'; break;
304 case TK_PLUSEQ: oper = '+'; break;
305 case TK_MULEQ: oper = '*'; break;
306 case TK_DIVEQ: oper = '/'; break;
307 case TK_MODEQ: oper = '%'; break;
308 default: assert(0); break;
311 int val = _fs->PopTarget();
312 int key = _fs->PopTarget();
313 int src = _fs->PopTarget();
314 //mixes dest obj and source val in the arg1(hack?)
315 _fs->AddInstruction(_OP_COMPARITH,_fs->PushTarget(),(src<<16)|val,key,oper);
318 Emit2ArgsOP(_OP_COMPARITHL, oper);
323 for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr());
325 ExpState Expression(bool funcarg = false)
328 _exst._class_or_delete = false;
329 _exst._funcarg = funcarg;
341 int ds = _exst._deref;
342 bool freevar = _exst._freevar;
343 if(ds == DEREF_NO_DEREF) Error(_SC("can't assign expression"));
348 if(freevar) Error(_SC("free variables cannot be modified"));
349 if(ds == DEREF_FIELD)
350 EmitDerefOp(_OP_NEWSLOT);
351 else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
352 Error(_SC("can't 'create' a local slot"));
354 case _SC('='): //ASSIGN
355 if(freevar) Error(_SC("free variables cannot be modified"));
356 if(ds == DEREF_FIELD)
357 EmitDerefOp(_OP_SET);
358 else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
359 int p2 = _fs->PopTarget(); //src in OP_GET
360 int p1 = _fs->TopTarget(); //key in OP_GET
361 _fs->AddInstruction(_OP_MOVE, p1, p2);
369 EmitCompoundArith(op,ds == DEREF_FIELD);
376 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
377 int jzpos = _fs->GetCurrentPos();
378 int trg = _fs->PushTarget();
380 int first_exp = _fs->PopTarget();
381 if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
382 int endfirstexp = _fs->GetCurrentPos();
383 _fs->AddInstruction(_OP_JMP, 0, 0);
385 int jmppos = _fs->GetCurrentPos();
387 int second_exp = _fs->PopTarget();
388 if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
389 _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
390 _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1);
395 return PopExpState();
397 void BIN_EXP(SQOpcode op, void (SQCompiler::*f)(void),int op3 = 0)
400 int op1 = _fs->PopTarget();int op2 = _fs->PopTarget();
401 _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3);
406 for(;;) if(_token == TK_OR) {
407 int first_exp = _fs->PopTarget();
408 int trg = _fs->PushTarget();
409 _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0);
410 int jpos = _fs->GetCurrentPos();
411 if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
412 Lex(); LogicalOrExp();
414 int second_exp = _fs->PopTarget();
415 if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
417 _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
424 for(;;) switch(_token) {
426 int first_exp = _fs->PopTarget();
427 int trg = _fs->PushTarget();
428 _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0);
429 int jpos = _fs->GetCurrentPos();
430 if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
431 Lex(); LogicalAndExp();
433 int second_exp = _fs->PopTarget();
434 if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
436 _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
439 case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break;
440 case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break;
448 for(;;) if(_token == _SC('|'))
449 {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR);
455 for(;;) if(_token == _SC('^'))
456 {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR);
462 for(;;) if(_token == _SC('&'))
463 {BIN_EXP(_OP_BITW, &SQCompiler::CompExp,BW_AND);
469 for(;;) switch(_token) {
470 case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::ShiftExp); break;
471 case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break;
472 case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break;
473 case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break;
474 case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break;
475 case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::ShiftExp); break;
482 for(;;) switch(_token) {
483 case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break;
484 case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break;
485 case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break;
492 for(;;) switch(_token) {
493 case _SC('+'): case _SC('-'):
494 BIN_EXP(_OP_ARITH, &SQCompiler::MultExp,_token); break;
502 for(;;) switch(_token) {
503 case _SC('*'): case _SC('/'): case _SC('%'):
504 BIN_EXP(_OP_ARITH, &SQCompiler::PrefixedExpr,_token); break;
508 //if 'pos' != -1 the previous variable is a local variable
517 if(_token == TK_PARENT) {
520 Error(_SC("parent cannot be set"));
521 int src = _fs->PopTarget();
522 _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src);
525 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
526 if(NeedGet()) Emit2ArgsOP(_OP_GET);
528 _exst._deref = DEREF_FIELD;
529 _exst._freevar = false;
533 if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration"));
534 Lex(); Expression(); Expect(_SC(']'));
536 if(NeedGet()) Emit2ArgsOP(_OP_GET);
537 _exst._deref = DEREF_FIELD;
538 _exst._freevar = false;
542 if(_exst._deref != DEREF_NO_DEREF && !IsEndOfStatement()) {
543 int tok = _token; Lex();
545 Emit2ArgsOP(_OP_PINC,tok == TK_MINUSMINUS?-1:1);
546 else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
547 int src = _fs->PopTarget();
548 _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS?-1:1);
556 if(_exst._deref != DEREF_NO_DEREF) {
558 int key = _fs->PopTarget(); //key
559 int table = _fs->PopTarget(); //table etc...
560 int closure = _fs->PushTarget();
561 int ttarget = _fs->PushTarget();
562 _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget);
565 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
569 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
570 _exst._deref = DEREF_NO_DEREF;
583 case TK_STRING_LITERAL: {
584 //SQObjectPtr id(SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1));
585 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1)));
589 case TK_VARGC: Lex(); _fs->AddInstruction(_OP_VARGC, _fs->PushTarget()); break;
590 case TK_VARGV: { Lex();
594 int src = _fs->PopTarget();
595 _fs->AddInstruction(_OP_GETVARGV, _fs->PushTarget(), src);
601 _exst._freevar = false;
604 case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break;
605 case TK_THIS: id = _fs->CreateString(_SC("this")); break;
606 case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor")); break;
610 if((pos = _fs->GetLocalVariable(id)) == -1) {
611 //checks if is a free variable
612 if((pos = _fs->GetOuterVariable(id)) != -1) {
613 _exst._deref = _fs->PushTarget();
614 _fs->AddInstruction(_OP_LOADFREEVAR, _exst._deref ,pos);
615 _exst._freevar = true;
618 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
619 if(NeedGet()) Emit2ArgsOP(_OP_GET);
620 _exst._deref = DEREF_FIELD;
624 _fs->PushTarget(pos);
630 case TK_PARENT: Lex();_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), 0); break;
631 case TK_DOUBLE_COLON: // "::"
632 _fs->AddInstruction(_OP_LOADROOTTABLE, _fs->PushTarget());
633 _exst._deref = DEREF_FIELD;
634 _token = _SC('.'); //hack
638 _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
642 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._nvalue));
646 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue));
649 case TK_TRUE: case TK_FALSE:
650 _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0);
654 _fs->AddInstruction(_OP_NEWARRAY, _fs->PushTarget());
655 int apos = _fs->GetCurrentPos(),key = 0;
657 while(_token != _SC(']')) {
659 if(_token == _SC(',')) Lex();
660 int val = _fs->PopTarget();
661 int array = _fs->TopTarget();
662 _fs->AddInstruction(_OP_APPENDARRAY, array, val);
665 _fs->SetIntructionParam(apos, 1, key);
670 _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
671 Lex();ParseTableOrClass(_SC(','));
674 case TK_FUNCTION: FunctionExp(_token);break;
675 case TK_CLASS: Lex(); ClassExp();break;
676 case _SC('-'): UnaryOP(_OP_NEG); break;
677 case _SC('!'): UnaryOP(_OP_NOT); break;
678 case _SC('~'): UnaryOP(_OP_BWNOT); break;
679 case TK_TYPEOF : UnaryOP(_OP_TYPEOF); break;
680 case TK_RESUME : UnaryOP(_OP_RESUME); break;
681 case TK_CLONE : UnaryOP(_OP_CLONE); break;
683 case TK_PLUSPLUS :PrefixIncDec(_token); break;
684 case TK_DELETE : DeleteExpr(); break;
685 case TK_DELEGATE : DelegateExpr(); break;
686 case _SC('('): Lex(); CommaExpr(); Expect(_SC(')'));
688 default: Error(_SC("expression expected"));
692 void UnaryOP(SQOpcode op)
694 Lex(); PrefixedExpr();
695 int src = _fs->PopTarget();
696 _fs->AddInstruction(op, _fs->PushTarget(), src);
701 case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_PLUSPLUS: case TK_MINUSMINUS:
702 case TK_PLUSEQ: case TK_MINUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ:
705 return (!_exst._class_or_delete) || (_exst._class_or_delete && (_token == _SC('.') || _token == _SC('[')));
708 void FunctionCallArgs()
711 while(_token != _SC(')')) {
713 MoveIfCurrentTargetIsLocal();
715 if(_token == _SC(',')){
717 if(_token == ')') Error(_SC("expression expected, found ')'"));
721 for(int i = 0; i < (nargs - 1); i++) _fs->PopTarget();
722 int stackbase = _fs->PopTarget();
723 int closure = _fs->PopTarget();
724 _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs);
726 void ParseTableOrClass(int separator,int terminator = '}')
728 int tpos = _fs->GetCurrentPos(),nkeys = 0;
730 while(_token != terminator) {
731 bool hasattrs = false;
732 //check if is an attribute
733 if(separator == ';' && _token == TK_ATTR_OPEN) {
734 _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); Lex();
735 ParseTableOrClass(',',TK_ATTR_CLOSE);
740 case TK_CONSTRUCTOR:{
743 SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor"));
745 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
747 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
751 Lex(); CommaExpr(); Expect(_SC(']'));
752 Expect(_SC('=')); Expression();
755 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
756 Expect(_SC('=')); Expression();
759 if(_token == separator) Lex();//optional comma/semicolon
761 int val = _fs->PopTarget();
762 int key = _fs->PopTarget();
763 int attrs = hasattrs ? _fs->PopTarget():-1;
764 assert(hasattrs && attrs == key-1 || !hasattrs);
765 int table = _fs->TopTarget(); //<<BECAUSE OF THIS NO COMMON EMIT FUNC IS POSSIBLE
766 _fs->AddInstruction(hasattrs?_OP_NEWSLOTA:_OP_NEWSLOT, _fs->PushTarget(), table, key, val);
769 if(separator == _SC(',')) //hack recognizes a table from the separator
770 _fs->SetIntructionParam(tpos, 1, nkeys);
773 void LocalDeclStatement()
777 Lex(); varname = Expect(TK_IDENTIFIER);
778 if(_token == _SC('=')) {
780 int src = _fs->PopTarget();
781 int dest = _fs->PushTarget();
782 if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src);
785 _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
788 _fs->PushLocalVariable(varname);
790 } while(_token == _SC(','));
795 bool haselse = false;
796 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
797 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
798 int jnepos = _fs->GetCurrentPos();
799 int stacksize = _fs->GetStackSize();
803 if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon();
805 CleanStack(stacksize);
806 int endifblock = _fs->GetCurrentPos();
807 if(_token == TK_ELSE){
809 stacksize = _fs->GetStackSize();
810 _fs->AddInstruction(_OP_JMP);
811 jmppos = _fs->GetCurrentPos();
813 Statement(); OptionalSemicolon();
814 CleanStack(stacksize);
815 _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
817 _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0));
819 void WhileStatement()
822 int stacksize = _fs->GetStackSize();
823 jmppos = _fs->GetCurrentPos();
824 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
826 BEGIN_BREAKBLE_BLOCK();
827 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
828 jzpos = _fs->GetCurrentPos();
829 stacksize = _fs->GetStackSize();
833 CleanStack(stacksize);
834 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
835 _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
837 END_BREAKBLE_BLOCK(jmppos);
839 void DoWhileStatement()
842 int jzpos = _fs->GetCurrentPos();
843 int stacksize = _fs->GetStackSize();
844 BEGIN_BREAKBLE_BLOCK()
846 CleanStack(stacksize);
848 int continuetrg = _fs->GetCurrentPos();
849 Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
850 _fs->AddInstruction(_OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1);
851 END_BREAKBLE_BLOCK(continuetrg);
856 int stacksize = _fs->GetStackSize();
858 if(_token == TK_LOCAL) LocalDeclStatement();
859 else if(_token != _SC(';')){
865 int jmppos = _fs->GetCurrentPos();
867 if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); }
870 int expstart = _fs->GetCurrentPos() + 1;
871 if(_token != _SC(')')) {
877 int expend = _fs->GetCurrentPos();
878 int expsize = (expend - expstart) + 1;
879 SQInstructionVec exp;
881 for(int i = 0; i < expsize; i++)
882 exp.push_back(_fs->GetInstruction(expstart + i));
883 _fs->PopInstructions(expsize);
885 BEGIN_BREAKBLE_BLOCK()
887 int continuetrg = _fs->GetCurrentPos();
889 for(int i = 0; i < expsize; i++)
890 _fs->AddInstruction(exp[i]);
892 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0);
893 if(jzpos> 0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
894 CleanStack(stacksize);
896 END_BREAKBLE_BLOCK(continuetrg);
898 void ForEachStatement()
900 SQObject idxname, valname;
901 Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER);
902 if(_token == _SC(',')) {
904 Lex(); valname = Expect(TK_IDENTIFIER);
907 idxname = _fs->CreateString(_SC("@INDEX@"));
911 //save the stack size
912 int stacksize = _fs->GetStackSize();
913 //put the table in the stack(evaluate the table expression)
914 Expression(); Expect(_SC(')'));
915 int container = _fs->TopTarget();
916 //push the index local var
917 int indexpos = _fs->PushLocalVariable(idxname);
918 _fs->AddInstruction(_OP_LOADNULLS, indexpos,1);
919 //push the value local var
920 int valuepos = _fs->PushLocalVariable(valname);
921 _fs->AddInstruction(_OP_LOADNULLS, valuepos,1);
922 //push reference index
923 int itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible
924 _fs->AddInstruction(_OP_LOADNULLS, itrpos,1);
925 int jmppos = _fs->GetCurrentPos();
926 _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos);
927 int foreachpos = _fs->GetCurrentPos();
928 //generate the statement code
929 BEGIN_BREAKBLE_BLOCK()
931 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
932 _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos);
933 //restore the local variable stack(remove index,val and ref idx)
934 CleanStack(stacksize);
935 END_BREAKBLE_BLOCK(foreachpos - 1);
937 void SwitchStatement()
939 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
941 int expr = _fs->TopTarget();
943 int tonextcondjmp = -1;
944 int skipcondjmp = -1;
945 int __nbreaks__ = _fs->_unresolvedbreaks.size();
946 _fs->_breaktargets.push_back(0);
947 while(_token == TK_CASE) {
949 _fs->AddInstruction(_OP_JMP, 0, 0);
950 skipcondjmp = _fs->GetCurrentPos();
951 _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
954 Lex(); Expression(); Expect(_SC(':'));
955 int trg = _fs->PopTarget();
956 _fs->AddInstruction(_OP_EQ, trg, trg, expr);
957 _fs->AddInstruction(_OP_JZ, trg, 0);
959 if(skipcondjmp != -1) {
960 _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp));
962 tonextcondjmp = _fs->GetCurrentPos();
963 int stacksize = _fs->GetStackSize();
965 _fs->SetStackSize(stacksize);
968 if(tonextcondjmp != -1)
969 _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
970 if(_token == TK_DEFAULT) {
971 Lex(); Expect(_SC(':'));
972 int stacksize = _fs->GetStackSize();
974 _fs->SetStackSize(stacksize);
978 __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__;
979 if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__);
980 _fs->_breaktargets.pop_back();
983 void FunctionStatement()
986 Lex(); id = Expect(TK_IDENTIFIER);
988 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
989 if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
991 while(_token == TK_DOUBLE_COLON) {
993 id = Expect(TK_IDENTIFIER);
994 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
995 if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
999 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
1000 EmitDerefOp(_OP_NEWSLOT);
1003 void ClassStatement()
1006 Lex(); PushExpState();
1007 _exst._class_or_delete = true;
1008 _exst._funcarg = false;
1011 if(es._deref == DEREF_NO_DEREF) Error(_SC("invalid class name"));
1012 if(es._deref == DEREF_FIELD) {
1014 EmitDerefOp(_OP_NEWSLOT);
1017 else Error(_SC("cannot create a class in a local with the syntax(class <local>)"));
1019 void TryCatchStatement()
1023 _fs->AddInstruction(_OP_PUSHTRAP,0,0);
1025 if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++;
1026 if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++;
1027 int trappos = _fs->GetCurrentPos();
1030 _fs->AddInstruction(_OP_POPTRAP, 1, 0);
1031 if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--;
1032 if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--;
1033 _fs->AddInstruction(_OP_JMP, 0, 0);
1034 int jmppos = _fs->GetCurrentPos();
1035 _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos));
1036 Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')'));
1037 int stacksize = _fs->GetStackSize();
1038 int ex_target = _fs->PushLocalVariable(exid);
1039 _fs->SetIntructionParam(trappos, 0, ex_target);
1041 _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0);
1042 CleanStack(stacksize);
1044 void FunctionExp(int ftype)
1046 Lex(); Expect(_SC('('));
1047 CreateFunction(_null_);
1048 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1);
1054 if(_token == TK_EXTENDS) {
1055 Lex(); Expression();
1056 base = _fs->TopTarget();
1058 if(_token == TK_ATTR_OPEN) {
1060 _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
1061 ParseTableOrClass(_SC(','),TK_ATTR_CLOSE);
1062 attrs = _fs->TopTarget();
1065 if(attrs != -1) _fs->PopTarget();
1066 if(base != -1) _fs->PopTarget();
1067 _fs->AddInstruction(_OP_CLASS, _fs->PushTarget(), base, attrs);
1068 ParseTableOrClass(_SC(';'));
1075 int table = _fs->PopTarget(), delegate = _fs->PopTarget();
1076 _fs->AddInstruction(_OP_DELEGATE, _fs->PushTarget(), table, delegate);
1081 Lex(); PushExpState();
1082 _exst._class_or_delete = true;
1083 _exst._funcarg = false;
1086 if(es._deref == DEREF_NO_DEREF) Error(_SC("can't delete an expression"));
1087 if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE);
1088 else Error(_SC("cannot delete a local"));
1090 void PrefixIncDec(int token)
1093 Lex(); PushExpState();
1094 _exst._class_or_delete = true;
1095 _exst._funcarg = false;
1098 if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_INC,token == TK_MINUSMINUS?-1:1);
1100 int src = _fs->PopTarget();
1101 _fs->AddInstruction(_OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS?-1:1);
1104 void CreateFunction(SQObject &name)
1107 SQFuncState *funcstate = _fs->PushChildState(_ss(_vm), SQFunctionProto::Create());
1108 _funcproto(funcstate->_func)->_name = name;
1110 funcstate->AddParameter(_fs->CreateString(_SC("this")));
1111 _funcproto(funcstate->_func)->_sourcename = _sourcename;
1112 while(_token!=_SC(')')) {
1113 if(_token == TK_VARPARAMS) {
1114 funcstate->_varparams = true;
1116 if(_token != _SC(')')) Error(_SC("expected ')'"));
1120 paramname = Expect(TK_IDENTIFIER);
1121 funcstate->AddParameter(paramname);
1122 if(_token == _SC(',')) Lex();
1123 else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
1128 if(_token == _SC(':')) {
1129 Lex(); Expect(_SC('('));
1130 while(_token != _SC(')')) {
1131 paramname = Expect(TK_IDENTIFIER);
1132 //outers are treated as implicit local variables
1133 funcstate->AddOuterValue(paramname);
1134 if(_token == _SC(',')) Lex();
1135 else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
1140 SQFuncState *currchunk = _fs;
1143 funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true);
1144 funcstate->AddInstruction(_OP_RETURN, -1);
1145 funcstate->SetStackSize(0);
1146 _funcproto(_fs->_func)->_stacksize = _fs->_stacksize;
1147 funcstate->Finalize();
1152 _fs->_functions.push_back(funcstate->_func);
1153 _fs->PopChildState();
1155 void CleanStack(int stacksize)
1157 if(_fs->GetStackSize() != stacksize)
1158 _fs->SetStackSize(stacksize);
1160 void ResolveBreaks(SQFuncState *funcstate, int ntoresolve)
1162 while(ntoresolve > 0) {
1163 int pos = funcstate->_unresolvedbreaks.back();
1164 funcstate->_unresolvedbreaks.pop_back();
1165 //set the jmp instruction
1166 funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0);
1170 void ResolveContinues(SQFuncState *funcstate, int ntoresolve, int targetpos)
1172 while(ntoresolve > 0) {
1173 int pos = funcstate->_unresolvedcontinues.back();
1174 funcstate->_unresolvedcontinues.pop_back();
1175 //set the jmp instruction
1176 funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0);
1183 SQObjectPtr _sourcename;
1189 ExpStateVec _expstates;
1190 SQChar *compilererror;
1195 bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo)
1197 SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo);
1198 return p.Compile(out);