2 see copyright notice in squirrel.h
4 #include "sqpcheader.h"
8 #include "sqfuncproto.h"
9 #include "sqfuncstate.h"
10 #include "sqcompiler.h"
14 #define DEREF_NO_DEREF -1
15 #define DEREF_FIELD -2
21 _deref = DEREF_NO_DEREF;
23 _class_or_delete = false;
26 bool _class_or_delete;
32 typedef sqvector<ExpState> ExpStateVec;
34 #define _exst (_expstates.top())
36 #define BEGIN_BREAKBLE_BLOCK() int __nbreaks__=_fs->_unresolvedbreaks.size(); \
37 int __ncontinues__=_fs->_unresolvedcontinues.size(); \
38 _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0);
40 #define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \
41 __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \
42 if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \
43 if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \
44 _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();}
49 SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo)
52 _lex.Init(_ss(v), rg, up);
53 _sourcename = SQString::Create(_ss(v), sourcename);
54 _lineinfo = lineinfo;_raiseerror = raiseerror;
56 void Error(const SQChar *s, ...)
58 static SQChar temp[256];
61 scvsprintf(temp, s, vl);
63 throw ParserException(temp);
65 void Lex(){ _token = _lex.Lex();}
66 void PushExpState(){ _expstates.push_back(ExpState()); }
67 bool IsDerefToken(int tok)
70 case _SC('='): case _SC('('): case TK_NEWSLOT:
71 case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS: return true;
75 ExpState PopExpState()
77 ExpState ret = _expstates.top();
78 _expstates.pop_back();
81 SQObjectPtr Expect(int tok)
85 if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) {
86 //ret = SQString::Create(_ss(_vm),_SC("constructor"));
94 ret = SQString::Create(_ss(_vm), _SC("IDENTIFIER"));
96 case TK_STRING_LITERAL:
97 ret = SQString::Create(_ss(_vm), _SC("STRING_LITERAL"));
100 ret = SQString::Create(_ss(_vm), _SC("INTEGER"));
103 ret = SQString::Create(_ss(_vm), _SC("FLOAT"));
106 ret = _lex.Tok2Str(tok);
108 Error(_SC("expected '%s'"), _stringval(ret));
110 Error(_SC("expected '%c'"), tok);
116 ret = SQString::Create(_ss(_vm), _lex._svalue);
118 case TK_STRING_LITERAL:
119 ret = SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1);
131 bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); }
132 void OptionalSemicolon()
134 if(_token == _SC(';')) { Lex(); return; }
135 if(!IsEndOfStatement()) {
136 Error(_SC("end of statement expected (; or lf)"));
139 void MoveIfCurrentTargetIsLocal() {
140 int trg = _fs->TopTarget();
141 if(_fs->IsLocal(trg)) {
142 trg = _fs->PopTarget(); //no pops the target and move it
143 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg);
146 bool Compile(SQObjectPtr &o)
152 SQFuncState funcstate(_ss(_vm), SQFunctionProto::Create(), NULL);
153 _funcproto(funcstate._func)->_name = SQString::Create(_ss(_vm), _SC("main"));
155 _fs->AddParameter(SQString::Create(_ss(_vm), _SC("this")));
156 _funcproto(_fs->_func)->_sourcename = _sourcename;
157 int stacksize = _fs->GetStackSize();
160 if(_lex._prevtoken != _SC('}')) OptionalSemicolon();
162 CleanStack(stacksize);
163 _fs->AddLineInfos(_lex._currentline, _lineinfo, true);
164 _fs->AddInstruction(_OP_RETURN, 0xFF);
165 _funcproto(_fs->_func)->_stacksize = _fs->_stacksize;
166 _fs->SetStackSize(0);
173 SQ_CATCH(ParserException,ex){
174 if(_raiseerror && _ss(_vm)->_compilererrorhandler){
176 _ss(_vm)->_compilererrorhandler(_vm, ex.desc, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"),
177 _lex._currentline, _lex._currentcolumn);
179 _vm->_lasterror = SQString::Create(_ss(_vm), ex.desc, -1);
186 while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) {
188 if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();
193 _fs->AddLineInfos(_lex._currentline, _lineinfo);
195 case _SC(';'): Lex(); break;
196 case TK_IF: IfStatement(); break;
197 case TK_WHILE: WhileStatement(); break;
198 case TK_DO: DoWhileStatement(); break;
199 case TK_FOR: ForStatement(); break;
200 case TK_FOREACH: ForEachStatement(); break;
201 case TK_SWITCH: SwitchStatement(); break;
202 case TK_LOCAL: LocalDeclStatement(); break;
206 if(_token == TK_RETURN) {
212 _funcproto(_fs->_func)->_bgenerator = true;
215 if(!IsEndOfStatement()) {
216 int retexp = _fs->GetCurrentPos()+1;
218 if(op == _OP_RETURN && _fs->_traps > 0)
219 _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0);
220 _fs->_returnexp = retexp;
221 _fs->AddInstruction(op, 1, _fs->PopTarget());
224 if(op == _OP_RETURN && _fs->_traps > 0)
225 _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0);
226 _fs->_returnexp = -1;
227 _fs->AddInstruction(op, 0xFF);
231 if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block"));
232 if(_fs->_breaktargets.top() > 0){
233 _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);
235 _fs->AddInstruction(_OP_JMP, 0, -1234);
236 _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos());
240 if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block"));
241 if(_fs->_continuetargets.top() > 0) {
242 _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);
244 _fs->AddInstruction(_OP_JMP, 0, -1234);
245 _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos());
255 int stacksize = _fs->GetStackSize();
259 _fs->SetStackSize(stacksize);
268 _fs->AddInstruction(_OP_THROW, _fs->PopTarget());
277 void EmitDerefOp(SQOpcode op)
279 int val = _fs->PopTarget();
280 int key = _fs->PopTarget();
281 int src = _fs->PopTarget();
282 _fs->AddInstruction(op,_fs->PushTarget(),src,key,val);
284 void Emit2ArgsOP(SQOpcode op, int p3 = 0)
286 int p2 = _fs->PopTarget(); //src in OP_GET
287 int p1 = _fs->PopTarget(); //key in OP_GET
288 _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3);
290 void EmitCompoundArith(int tok,bool deref)
294 case TK_MINUSEQ: oper = '-'; break;
295 case TK_PLUSEQ: oper = '+'; break;
296 case TK_MULEQ: oper = '*'; break;
297 case TK_DIVEQ: oper = '/'; break;
298 case TK_MODEQ: oper = '%'; break;
299 default: assert(0); break;
302 int val = _fs->PopTarget();
303 int key = _fs->PopTarget();
304 int src = _fs->PopTarget();
305 //mixes dest obj and source val in the arg1(hack?)
306 _fs->AddInstruction(_OP_COMPARITH,_fs->PushTarget(),(src<<16)|val,key,oper);
309 Emit2ArgsOP(_OP_COMPARITHL, oper);
314 for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr());
316 ExpState Expression(bool funcarg = false)
319 _exst._class_or_delete = false;
320 _exst._funcarg = funcarg;
332 int ds = _exst._deref;
333 bool freevar = _exst._freevar;
334 if(ds == DEREF_NO_DEREF) Error(_SC("can't assign expression"));
339 if(freevar) Error(_SC("free variables cannot be modified"));
340 if(ds == DEREF_FIELD)
341 EmitDerefOp(_OP_NEWSLOT);
342 else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
343 Error(_SC("can't 'create' a local slot"));
345 case _SC('='): //ASSIGN
346 if(freevar) Error(_SC("free variables cannot be modified"));
347 if(ds == DEREF_FIELD)
348 EmitDerefOp(_OP_SET);
349 else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
350 int p2 = _fs->PopTarget(); //src in OP_GET
351 int p1 = _fs->TopTarget(); //key in OP_GET
352 _fs->AddInstruction(_OP_MOVE, p1, p2);
360 EmitCompoundArith(op,ds == DEREF_FIELD);
367 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
368 int jzpos = _fs->GetCurrentPos();
369 int trg = _fs->PushTarget();
371 int first_exp = _fs->PopTarget();
372 if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
373 int endfirstexp = _fs->GetCurrentPos();
374 _fs->AddInstruction(_OP_JMP, 0, 0);
376 int jmppos = _fs->GetCurrentPos();
378 int second_exp = _fs->PopTarget();
379 if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
380 _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
381 _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1);
386 return PopExpState();
388 void BIN_EXP(SQOpcode op, void (SQCompiler::*f)(void),int op3 = 0)
391 int op1 = _fs->PopTarget();int op2 = _fs->PopTarget();
392 _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3);
397 for(;;) if(_token == TK_OR) {
398 int first_exp = _fs->PopTarget();
399 int trg = _fs->PushTarget();
400 _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0);
401 int jpos = _fs->GetCurrentPos();
402 if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
403 Lex(); LogicalOrExp();
405 int second_exp = _fs->PopTarget();
406 if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
408 _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
415 for(;;) switch(_token) {
417 int first_exp = _fs->PopTarget();
418 int trg = _fs->PushTarget();
419 _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0);
420 int jpos = _fs->GetCurrentPos();
421 if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
422 Lex(); LogicalAndExp();
424 int second_exp = _fs->PopTarget();
425 if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
427 _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
430 case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break;
431 case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break;
439 for(;;) if(_token == _SC('|'))
440 {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR);
446 for(;;) if(_token == _SC('^'))
447 {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR);
453 for(;;) if(_token == _SC('&'))
454 {BIN_EXP(_OP_BITW, &SQCompiler::CompExp,BW_AND);
460 for(;;) switch(_token) {
461 case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::ShiftExp); break;
462 case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break;
463 case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break;
464 case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break;
465 case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break;
466 case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::ShiftExp); break;
473 for(;;) switch(_token) {
474 case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break;
475 case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break;
476 case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break;
483 for(;;) switch(_token) {
484 case _SC('+'): case _SC('-'):
485 BIN_EXP(_OP_ARITH, &SQCompiler::MultExp,_token); break;
493 for(;;) switch(_token) {
494 case _SC('*'): case _SC('/'): case _SC('%'):
495 BIN_EXP(_OP_ARITH, &SQCompiler::PrefixedExpr,_token); break;
499 //if 'pos' != -1 the previous variable is a local variable
509 if(_token == TK_PARENT) {
512 Error(_SC("parent cannot be set"));
513 int src = _fs->PopTarget();
514 _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src);
517 idx = Expect(TK_IDENTIFIER);
518 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(idx)));
519 if(NeedGet()) Emit2ArgsOP(_OP_GET);
521 _exst._deref = DEREF_FIELD;
522 _exst._freevar = false;
526 if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration"));
527 Lex(); Expression(); Expect(_SC(']'));
529 if(NeedGet()) Emit2ArgsOP(_OP_GET);
530 _exst._deref = DEREF_FIELD;
531 _exst._freevar = false;
535 if(_exst._deref != DEREF_NO_DEREF && !IsEndOfStatement()) {
536 int tok = _token; Lex();
538 Emit2ArgsOP(_OP_PINC,tok == TK_MINUSMINUS?-1:1);
539 else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
540 int src = _fs->PopTarget();
541 _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS?-1:1);
549 if(_exst._deref != DEREF_NO_DEREF) {
551 int key = _fs->PopTarget(); //key
552 int table = _fs->PopTarget(); //table etc...
553 int closure = _fs->PushTarget();
554 int ttarget = _fs->PushTarget();
555 _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget);
558 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
562 _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
563 _exst._deref = DEREF_NO_DEREF;
576 case TK_STRING_LITERAL: {
577 SQObjectPtr id(SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1));
578 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(id)));
582 case TK_VARGC: Lex(); _fs->AddInstruction(_OP_VARGC, _fs->PushTarget()); break;
583 case TK_VARGV: { Lex();
587 int src = _fs->PopTarget();
588 _fs->AddInstruction(_OP_GETVARGV, _fs->PushTarget(), src);
594 _exst._freevar = false;
597 case TK_IDENTIFIER: id = SQString::Create(_ss(_vm), _lex._svalue); break;
598 case TK_THIS: id = SQString::Create(_ss(_vm), _SC("this")); break;
599 case TK_CONSTRUCTOR: id = SQString::Create(_ss(_vm), _SC("constructor")); break;
603 if((pos = _fs->GetLocalVariable(id)) == -1) {
604 //checks if is a free variable
605 if((pos = _fs->GetOuterVariable(id)) != -1) {
606 _exst._deref = _fs->PushTarget();
607 _fs->AddInstruction(_OP_LOADFREEVAR, _exst._deref ,pos);
608 _exst._freevar = true;
611 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(id)));
612 if(NeedGet()) Emit2ArgsOP(_OP_GET);
613 _exst._deref = DEREF_FIELD;
617 _fs->PushTarget(pos);
623 case TK_PARENT: Lex();_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), 0); break;
624 case TK_DOUBLE_COLON: // "::"
625 _fs->AddInstruction(_OP_LOADROOTTABLE, _fs->PushTarget());
626 _exst._deref = DEREF_FIELD;
627 _token = _SC('.'); //hack
631 _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
635 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._nvalue));
639 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue));
642 case TK_TRUE: case TK_FALSE:
643 _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0);
647 _fs->AddInstruction(_OP_NEWARRAY, _fs->PushTarget());
648 int apos = _fs->GetCurrentPos(),key = 0;
650 while(_token != _SC(']')) {
652 if(_token == _SC(',')) Lex();
653 int val = _fs->PopTarget();
654 int array = _fs->TopTarget();
655 _fs->AddInstruction(_OP_APPENDARRAY, array, val);
658 _fs->SetIntructionParam(apos, 1, key);
663 _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
664 Lex();ParseTableOrClass(_SC(','));
667 case TK_FUNCTION: FunctionExp(_token);break;
668 case TK_CLASS: Lex(); ClassExp();break;
669 case _SC('-'): UnaryOP(_OP_NEG); break;
670 case _SC('!'): UnaryOP(_OP_NOT); break;
671 case _SC('~'): UnaryOP(_OP_BWNOT); break;
672 case TK_TYPEOF : UnaryOP(_OP_TYPEOF); break;
673 case TK_RESUME : UnaryOP(_OP_RESUME); break;
674 case TK_CLONE : UnaryOP(_OP_CLONE); break;
676 case TK_PLUSPLUS :PrefixIncDec(_token); break;
677 case TK_DELETE : DeleteExpr(); break;
678 case TK_DELEGATE : DelegateExpr(); break;
679 case _SC('('): Lex(); CommaExpr(); Expect(_SC(')'));
681 default: Error(_SC("expression expected"));
685 void UnaryOP(SQOpcode op)
687 Lex(); PrefixedExpr();
688 int src = _fs->PopTarget();
689 _fs->AddInstruction(op, _fs->PushTarget(), src);
694 case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_PLUSPLUS: case TK_MINUSMINUS:
695 case TK_PLUSEQ: case TK_MINUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ:
698 return (!_exst._class_or_delete) || (_exst._class_or_delete && (_token == _SC('.') || _token == _SC('[')));
701 void FunctionCallArgs()
704 while(_token != _SC(')')) {
706 MoveIfCurrentTargetIsLocal();
708 if(_token == _SC(',')){
710 if(_token == ')') Error(_SC("expression expected, found ')'"));
714 for(int i = 0; i < (nargs - 1); i++) _fs->PopTarget();
715 int stackbase = _fs->PopTarget();
716 int closure = _fs->PopTarget();
717 _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs);
719 void ParseTableOrClass(int separator,int terminator = '}')
721 int tpos = _fs->GetCurrentPos(),nkeys = 0;
723 while(_token != terminator) {
724 bool hasattrs = false;
725 //check if is an attribute
726 if(separator == ';' && _token == TK_ATTR_OPEN) {
727 _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); Lex();
728 ParseTableOrClass(',',TK_ATTR_CLOSE);
733 case TK_CONSTRUCTOR:{
736 SQObjectPtr id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : SQString::Create(_ss(_vm),_SC("constructor"));
738 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(id)));
740 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
744 Lex(); CommaExpr(); Expect(_SC(']'));
745 Expect(_SC('=')); Expression();
748 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(Expect(TK_IDENTIFIER))));
749 Expect(_SC('=')); Expression();
752 if(_token == separator) Lex();//optional comma/semicolon
754 int val = _fs->PopTarget();
755 int key = _fs->PopTarget();
756 int attrs = hasattrs ? _fs->PopTarget():-1;
757 assert(hasattrs && attrs == key-1 || !hasattrs);
758 int table = _fs->TopTarget(); //<<BECAUSE OF THIS NO COMMON EMIT FUNC IS POSSIBLE
759 _fs->AddInstruction(hasattrs?_OP_NEWSLOTA:_OP_NEWSLOT, _fs->PushTarget(), table, key, val);
762 if(separator == _SC(',')) //hack recognizes a table from the separator
763 _fs->SetIntructionParam(tpos, 1, nkeys);
766 void LocalDeclStatement()
770 Lex(); varname = Expect(TK_IDENTIFIER);
771 if(_token == _SC('=')) {
773 int src = _fs->PopTarget();
774 int dest = _fs->PushTarget();
775 if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src);
778 _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
781 _fs->PushLocalVariable(varname);
783 } while(_token == _SC(','));
788 bool haselse = false;
789 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
790 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
791 int jnepos = _fs->GetCurrentPos();
792 int stacksize = _fs->GetStackSize();
796 if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon();
798 CleanStack(stacksize);
799 int endifblock = _fs->GetCurrentPos();
800 if(_token == TK_ELSE){
802 stacksize = _fs->GetStackSize();
803 _fs->AddInstruction(_OP_JMP);
804 jmppos = _fs->GetCurrentPos();
806 Statement(); OptionalSemicolon();
807 CleanStack(stacksize);
808 _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
810 _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0));
812 void WhileStatement()
815 int stacksize = _fs->GetStackSize();
816 jmppos = _fs->GetCurrentPos();
817 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
819 BEGIN_BREAKBLE_BLOCK();
820 _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
821 jzpos = _fs->GetCurrentPos();
822 stacksize = _fs->GetStackSize();
826 CleanStack(stacksize);
827 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
828 _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
830 END_BREAKBLE_BLOCK(jmppos);
832 void DoWhileStatement()
835 int jzpos = _fs->GetCurrentPos();
836 int stacksize = _fs->GetStackSize();
837 BEGIN_BREAKBLE_BLOCK()
839 CleanStack(stacksize);
841 int continuetrg = _fs->GetCurrentPos();
842 Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
843 _fs->AddInstruction(_OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1);
844 END_BREAKBLE_BLOCK(continuetrg);
849 int stacksize = _fs->GetStackSize();
851 if(_token == TK_LOCAL) LocalDeclStatement();
852 else if(_token != _SC(';')){
858 int jmppos = _fs->GetCurrentPos();
860 if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); }
863 int expstart = _fs->GetCurrentPos() + 1;
864 if(_token != _SC(')')) {
870 int expend = _fs->GetCurrentPos();
871 int expsize = (expend - expstart) + 1;
872 SQInstructionVec exp;
874 for(int i = 0; i < expsize; i++)
875 exp.push_back(_fs->GetInstruction(expstart + i));
876 _fs->PopInstructions(expsize);
878 BEGIN_BREAKBLE_BLOCK()
880 int continuetrg = _fs->GetCurrentPos();
882 for(int i = 0; i < expsize; i++)
883 _fs->AddInstruction(exp[i]);
885 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0);
886 if(jzpos> 0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
887 CleanStack(stacksize);
889 END_BREAKBLE_BLOCK(continuetrg);
891 void ForEachStatement()
893 SQObjectPtr idxname, valname;
894 Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER);
895 if(_token == _SC(',')) {
897 Lex(); valname = Expect(TK_IDENTIFIER);
900 idxname = SQString::Create(_ss(_vm), _SC("@INDEX@"));
904 //save the stack size
905 int stacksize = _fs->GetStackSize();
906 //put the table in the stack(evaluate the table expression)
907 Expression(); Expect(_SC(')'));
908 int container = _fs->TopTarget();
909 //push the index local var
910 int indexpos = _fs->PushLocalVariable(idxname);
911 _fs->AddInstruction(_OP_LOADNULLS, indexpos,1);
912 //push the value local var
913 int valuepos = _fs->PushLocalVariable(valname);
914 _fs->AddInstruction(_OP_LOADNULLS, valuepos,1);
915 //push reference index
916 int itrpos = _fs->PushLocalVariable(SQString::Create(_ss(_vm), _SC("@ITERATOR@"))); //use invalid id to make it inaccessible
917 _fs->AddInstruction(_OP_LOADNULLS, itrpos,1);
918 int jmppos = _fs->GetCurrentPos();
919 _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos);
920 int foreachpos = _fs->GetCurrentPos();
921 //generate the statement code
922 BEGIN_BREAKBLE_BLOCK()
924 _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
925 _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos);
926 //restore the local variable stack(remove index,val and ref idx)
927 CleanStack(stacksize);
928 END_BREAKBLE_BLOCK(foreachpos - 1);
930 void SwitchStatement()
932 Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
934 int expr = _fs->TopTarget();
936 int tonextcondjmp = -1;
937 int skipcondjmp = -1;
938 int __nbreaks__ = _fs->_unresolvedbreaks.size();
939 _fs->_breaktargets.push_back(0);
940 while(_token == TK_CASE) {
942 _fs->AddInstruction(_OP_JMP, 0, 0);
943 skipcondjmp = _fs->GetCurrentPos();
944 _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
947 Lex(); Expression(); Expect(_SC(':'));
948 int trg = _fs->PopTarget();
949 _fs->AddInstruction(_OP_EQ, trg, trg, expr);
950 _fs->AddInstruction(_OP_JZ, trg, 0);
952 if(skipcondjmp != -1) {
953 _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp));
955 tonextcondjmp = _fs->GetCurrentPos();
956 int stacksize = _fs->GetStackSize();
958 _fs->SetStackSize(stacksize);
961 if(tonextcondjmp != -1)
962 _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
963 if(_token == TK_DEFAULT) {
964 Lex(); Expect(_SC(':'));
965 int stacksize = _fs->GetStackSize();
967 _fs->SetStackSize(stacksize);
971 __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__;
972 if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__);
973 _fs->_breaktargets.pop_back();
976 void FunctionStatement()
979 Lex(); id = Expect(TK_IDENTIFIER);
981 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(id)));
982 if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
984 while(_token == TK_DOUBLE_COLON) {
986 id = Expect(TK_IDENTIFIER);
987 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetStringConstant(_stringval(id)));
988 if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
992 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
993 EmitDerefOp(_OP_NEWSLOT);
996 void ClassStatement()
999 Lex(); PushExpState();
1000 _exst._class_or_delete = true;
1001 _exst._funcarg = false;
1004 if(es._deref == DEREF_NO_DEREF) Error(_SC("invalid class name"));
1005 if(es._deref == DEREF_FIELD) {
1007 EmitDerefOp(_OP_NEWSLOT);
1010 else Error(_SC("cannot create a class in a local with the syntax(class <local>)"));
1012 void TryCatchStatement()
1016 _fs->AddInstruction(_OP_PUSHTRAP,0,0);
1018 if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++;
1019 if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++;
1020 int trappos = _fs->GetCurrentPos();
1023 _fs->AddInstruction(_OP_POPTRAP, 1, 0);
1024 if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--;
1025 if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--;
1026 _fs->AddInstruction(_OP_JMP, 0, 0);
1027 int jmppos = _fs->GetCurrentPos();
1028 _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos));
1029 Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')'));
1030 int stacksize = _fs->GetStackSize();
1031 int ex_target = _fs->PushLocalVariable(exid);
1032 _fs->SetIntructionParam(trappos, 0, ex_target);
1034 _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0);
1035 CleanStack(stacksize);
1037 void FunctionExp(int ftype)
1039 Lex(); Expect(_SC('('));
1040 CreateFunction(_null_);
1041 _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1);
1047 if(_token == TK_EXTENDS) {
1048 Lex(); Expression();
1049 base = _fs->TopTarget();
1051 if(_token == TK_ATTR_OPEN) {
1053 _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
1054 ParseTableOrClass(_SC(','),TK_ATTR_CLOSE);
1055 attrs = _fs->TopTarget();
1058 if(attrs != -1) _fs->PopTarget();
1059 if(base != -1) _fs->PopTarget();
1060 _fs->AddInstruction(_OP_CLASS, _fs->PushTarget(), base, attrs);
1061 ParseTableOrClass(_SC(';'));
1068 int table = _fs->PopTarget(), delegate = _fs->PopTarget();
1069 _fs->AddInstruction(_OP_DELEGATE, _fs->PushTarget(), table, delegate);
1074 Lex(); PushExpState();
1075 _exst._class_or_delete = true;
1076 _exst._funcarg = false;
1079 if(es._deref == DEREF_NO_DEREF) Error(_SC("can't delete an expression"));
1080 if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE);
1081 else Error(_SC("cannot delete a local"));
1083 void PrefixIncDec(int token)
1086 Lex(); PushExpState();
1087 _exst._class_or_delete = true;
1088 _exst._funcarg = false;
1091 if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_INC,token == TK_MINUSMINUS?-1:1);
1093 int src = _fs->PopTarget();
1094 _fs->AddInstruction(_OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS?-1:1);
1097 void CreateFunction(SQObjectPtr name)
1099 SQFuncState funcstate(_ss(_vm), SQFunctionProto::Create(), _fs);
1100 _funcproto(funcstate._func)->_name = name;
1101 SQObjectPtr paramname;
1102 funcstate.AddParameter(SQString::Create(_ss(_vm), _SC("this")));
1103 _funcproto(funcstate._func)->_sourcename = _sourcename;
1104 while(_token!=_SC(')')) {
1105 if(_token == TK_VARPARAMS) {
1106 funcstate._varparams = true;
1108 if(_token != _SC(')')) Error(_SC("expected ')'"));
1112 paramname = Expect(TK_IDENTIFIER);
1113 funcstate.AddParameter(paramname);
1114 if(_token == _SC(',')) Lex();
1115 else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
1120 if(_token == _SC(':')) {
1121 Lex(); Expect(_SC('('));
1122 while(_token != _SC(')')) {
1123 paramname = Expect(TK_IDENTIFIER);
1124 //outers are treated as implicit local variables
1125 funcstate.AddOuterValue(paramname);
1126 if(_token == _SC(',')) Lex();
1127 else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
1132 SQFuncState *currchunk = _fs;
1135 funcstate.AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true);
1136 funcstate.AddInstruction(_OP_RETURN, -1);
1137 funcstate.SetStackSize(0);
1138 _funcproto(_fs->_func)->_stacksize = _fs->_stacksize;
1139 funcstate.Finalize();
1144 _fs->_functions.push_back(funcstate._func);
1146 void CleanStack(int stacksize)
1148 if(_fs->GetStackSize() != stacksize)
1149 _fs->SetStackSize(stacksize);
1151 void ResolveBreaks(SQFuncState *funcstate, int ntoresolve)
1153 while(ntoresolve > 0) {
1154 int pos = funcstate->_unresolvedbreaks.back();
1155 funcstate->_unresolvedbreaks.pop_back();
1156 //set the jmp instruction
1157 funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0);
1161 void ResolveContinues(SQFuncState *funcstate, int ntoresolve, int targetpos)
1163 while(ntoresolve > 0) {
1164 int pos = funcstate->_unresolvedcontinues.back();
1165 funcstate->_unresolvedcontinues.pop_back();
1166 //set the jmp instruction
1167 funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0);
1174 SQObjectPtr _sourcename;
1180 ExpStateVec _expstates;
1184 bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo)
1186 SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo);
1187 return p.Compile(out);