X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fsquirrel%2Fsquirrel%2Fsqcompiler.cpp;h=a6bab092c327b92a88b30fb2fef221bab2b82421;hb=a113d3bd1feddd510e3b2852b0d42522735eee40;hp=464a0c0d061d2cfaba7f1dcf0f387f253880e196;hpb=ff4c6994b952e26b854461d739eb3bcbfc30719f;p=supertux.git diff --git a/src/squirrel/squirrel/sqcompiler.cpp b/src/squirrel/squirrel/sqcompiler.cpp index 464a0c0d0..a6bab092c 100644 --- a/src/squirrel/squirrel/sqcompiler.cpp +++ b/src/squirrel/squirrel/sqcompiler.cpp @@ -1,1214 +1,1214 @@ -/* - see copyright notice in squirrel.h -*/ -#include "sqpcheader.h" -#include -#include -#include "sqopcodes.h" -#include "sqstring.h" -#include "sqfuncproto.h" -#include "sqcompiler.h" -#include "sqfuncstate.h" -#include "sqlexer.h" -#include "sqvm.h" - -#define DEREF_NO_DEREF -1 -#define DEREF_FIELD -2 - -struct ExpState -{ - ExpState() - { - _deref = DEREF_NO_DEREF; - _freevar = false; - _class_or_delete = false; - _funcarg = false; - } - bool _class_or_delete; - bool _funcarg; - bool _freevar; - SQInteger _deref; -}; - -typedef sqvector ExpStateVec; - -#define _exst (_expstates.top()) - -#define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \ - SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \ - _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0); - -#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \ - __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \ - if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \ - if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \ - _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();} - -class SQCompiler -{ -public: - SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo) - { - _vm=v; - _lex.Init(_ss(v), rg, up,ThrowError,this); - _sourcename = SQString::Create(_ss(v), sourcename); - _lineinfo = lineinfo;_raiseerror = raiseerror; - compilererror = NULL; - } - static void ThrowError(void *ud, const SQChar *s) { - SQCompiler *c = (SQCompiler *)ud; - c->Error(s); - } - void Error(const SQChar *s, ...) - { - static SQChar temp[256]; - va_list vl; - va_start(vl, s); - scvsprintf(temp, s, vl); - va_end(vl); - compilererror = temp; - longjmp(_errorjmp,1); - } - void Lex(){ _token = _lex.Lex();} - void PushExpState(){ _expstates.push_back(ExpState()); } - bool IsDerefToken(SQInteger tok) - { - switch(tok){ - case _SC('='): case _SC('('): case TK_NEWSLOT: - case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS: return true; - } - return false; - } - ExpState PopExpState() - { - ExpState ret = _expstates.top(); - _expstates.pop_back(); - return ret; - } - SQObject Expect(SQInteger tok) - { - - if(_token != tok) { - if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) { - //ret = SQString::Create(_ss(_vm),_SC("constructor")); - //do nothing - } - else { - const SQChar *etypename; - if(tok > 255) { - switch(tok) - { - case TK_IDENTIFIER: - etypename = _SC("IDENTIFIER"); - break; - case TK_STRING_LITERAL: - etypename = _SC("STRING_LITERAL"); - break; - case TK_INTEGER: - etypename = _SC("INTEGER"); - break; - case TK_FLOAT: - etypename = _SC("FLOAT"); - break; - default: - etypename = _lex.Tok2Str(tok); - } - Error(_SC("expected '%s'"), etypename); - } - Error(_SC("expected '%c'"), tok); - } - } - SQObjectPtr ret; - switch(tok) - { - case TK_IDENTIFIER: - ret = _fs->CreateString(_lex._svalue); - break; - case TK_STRING_LITERAL: - ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); - break; - case TK_INTEGER: - ret = SQObjectPtr(_lex._nvalue); - break; - case TK_FLOAT: - ret = SQObjectPtr(_lex._fvalue); - break; - } - Lex(); - return ret; - } - bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); } - void OptionalSemicolon() - { - if(_token == _SC(';')) { Lex(); return; } - if(!IsEndOfStatement()) { - Error(_SC("end of statement expected (; or lf)")); - } - } - void MoveIfCurrentTargetIsLocal() { - SQInteger trg = _fs->TopTarget(); - if(_fs->IsLocal(trg)) { - trg = _fs->PopTarget(); //no pops the target and move it - _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); - } - } - bool Compile(SQObjectPtr &o) - { - _debugline = 1; - _debugop = 0; - - SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this); - funcstate._name = SQString::Create(_ss(_vm), _SC("main")); - _fs = &funcstate; - _fs->AddParameter(_fs->CreateString(_SC("this"))); - _fs->_sourcename = _sourcename; - SQInteger stacksize = _fs->GetStackSize(); - if(setjmp(_errorjmp) == 0) { - Lex(); - while(_token > 0){ - Statement(); - if(_lex._prevtoken != _SC('}')) OptionalSemicolon(); - } - CleanStack(stacksize); - _fs->AddLineInfos(_lex._currentline, _lineinfo, true); - _fs->AddInstruction(_OP_RETURN, 0xFF); - _fs->SetStackSize(0); - o =_fs->BuildProto(); -#ifdef _DEBUG_DUMP - _fs->Dump(_funcproto(o)); -#endif - } - else { - if(_raiseerror && _ss(_vm)->_compilererrorhandler) { - _ss(_vm)->_compilererrorhandler(_vm, compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), - _lex._currentline, _lex._currentcolumn); - } - _vm->_lasterror = SQString::Create(_ss(_vm), compilererror, -1); - return false; - } - return true; - } - void Statements() - { - while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) { - Statement(); - if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); - } - } - void Statement() - { - _fs->AddLineInfos(_lex._currentline, _lineinfo); - switch(_token){ - case _SC(';'): Lex(); break; - case TK_IF: IfStatement(); break; - case TK_WHILE: WhileStatement(); break; - case TK_DO: DoWhileStatement(); break; - case TK_FOR: ForStatement(); break; - case TK_FOREACH: ForEachStatement(); break; - case TK_SWITCH: SwitchStatement(); break; - case TK_LOCAL: LocalDeclStatement(); break; - case TK_RETURN: - case TK_YIELD: { - SQOpcode op; - if(_token == TK_RETURN) { - op = _OP_RETURN; - - } - else { - op = _OP_YIELD; - _fs->_bgenerator = true; - } - Lex(); - if(!IsEndOfStatement()) { - SQInteger retexp = _fs->GetCurrentPos()+1; - CommaExpr(); - if(op == _OP_RETURN && _fs->_traps > 0) - _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); - _fs->_returnexp = retexp; - _fs->AddInstruction(op, 1, _fs->PopTarget()); - } - else{ - if(op == _OP_RETURN && _fs->_traps > 0) - _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0); - _fs->_returnexp = -1; - _fs->AddInstruction(op, 0xFF); - } - break;} - case TK_BREAK: - if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block")); - if(_fs->_breaktargets.top() > 0){ - _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); - } - _fs->AddInstruction(_OP_JMP, 0, -1234); - _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); - Lex(); - break; - case TK_CONTINUE: - if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block")); - if(_fs->_continuetargets.top() > 0) { - _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); - } - _fs->AddInstruction(_OP_JMP, 0, -1234); - _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); - Lex(); - break; - case TK_FUNCTION: - FunctionStatement(); - break; - case TK_CLASS: - ClassStatement(); - break; - case _SC('{'):{ - SQInteger stacksize = _fs->GetStackSize(); - Lex(); - Statements(); - Expect(_SC('}')); - _fs->SetStackSize(stacksize); - } - break; - case TK_TRY: - TryCatchStatement(); - break; - case TK_THROW: - Lex(); - CommaExpr(); - _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); - break; - default: - CommaExpr(); - _fs->PopTarget(); - break; - } - _fs->SnoozeOpt(); - } - void EmitDerefOp(SQOpcode op) - { - SQInteger val = _fs->PopTarget(); - SQInteger key = _fs->PopTarget(); - SQInteger src = _fs->PopTarget(); - _fs->AddInstruction(op,_fs->PushTarget(),src,key,val); - } - void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0) - { - SQInteger p2 = _fs->PopTarget(); //src in OP_GET - SQInteger p1 = _fs->PopTarget(); //key in OP_GET - _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3); - } - void EmitCompoundArith(SQInteger tok,bool deref) - { - SQInteger oper; - switch(tok){ - case TK_MINUSEQ: oper = '-'; break; - case TK_PLUSEQ: oper = '+'; break; - case TK_MULEQ: oper = '*'; break; - case TK_DIVEQ: oper = '/'; break; - case TK_MODEQ: oper = '%'; break; - default: oper = 0; //shut up compiler - assert(0); break; - }; - if(deref) { - SQInteger val = _fs->PopTarget(); - SQInteger key = _fs->PopTarget(); - SQInteger src = _fs->PopTarget(); - //mixes dest obj and source val in the arg1(hack?) - _fs->AddInstruction(_OP_COMPARITH,_fs->PushTarget(),(src<<16)|val,key,oper); - } - else { - Emit2ArgsOP(_OP_COMPARITHL, oper); - } - } - void CommaExpr() - { - for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr()); - } - ExpState Expression(bool funcarg = false) - { - PushExpState(); - _exst._class_or_delete = false; - _exst._funcarg = funcarg; - LogicalOrExp(); - switch(_token) { - case _SC('='): - case TK_NEWSLOT: - case TK_MINUSEQ: - case TK_PLUSEQ: - case TK_MULEQ: - case TK_DIVEQ: - case TK_MODEQ: - { - SQInteger op = _token; - SQInteger ds = _exst._deref; - bool freevar = _exst._freevar; - if(ds == DEREF_NO_DEREF) Error(_SC("can't assign expression")); - Lex(); Expression(); - - switch(op){ - case TK_NEWSLOT: - if(freevar) Error(_SC("free variables cannot be modified")); - if(ds == DEREF_FIELD) - EmitDerefOp(_OP_NEWSLOT); - else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local - Error(_SC("can't 'create' a local slot")); - break; - case _SC('='): //ASSIGN - if(freevar) Error(_SC("free variables cannot be modified")); - if(ds == DEREF_FIELD) - EmitDerefOp(_OP_SET); - else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local - SQInteger p2 = _fs->PopTarget(); //src in OP_GET - SQInteger p1 = _fs->TopTarget(); //key in OP_GET - _fs->AddInstruction(_OP_MOVE, p1, p2); - } - break; - case TK_MINUSEQ: - case TK_PLUSEQ: - case TK_MULEQ: - case TK_DIVEQ: - case TK_MODEQ: - EmitCompoundArith(op,ds == DEREF_FIELD); - break; - } - } - break; - case _SC('?'): { - Lex(); - _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); - SQInteger jzpos = _fs->GetCurrentPos(); - SQInteger trg = _fs->PushTarget(); - Expression(); - SQInteger first_exp = _fs->PopTarget(); - if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); - SQInteger endfirstexp = _fs->GetCurrentPos(); - _fs->AddInstruction(_OP_JMP, 0, 0); - Expect(_SC(':')); - SQInteger jmppos = _fs->GetCurrentPos(); - Expression(); - SQInteger second_exp = _fs->PopTarget(); - if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); - _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); - _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1); - _fs->SnoozeOpt(); - } - break; - } - return PopExpState(); - } - void BIN_EXP(SQOpcode op, void (SQCompiler::*f)(void),SQInteger op3 = 0) - { - Lex(); (this->*f)(); - SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget(); - _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); - } - void LogicalOrExp() - { - LogicalAndExp(); - for(;;) if(_token == TK_OR) { - SQInteger first_exp = _fs->PopTarget(); - SQInteger trg = _fs->PushTarget(); - _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0); - SQInteger jpos = _fs->GetCurrentPos(); - if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); - Lex(); LogicalOrExp(); - _fs->SnoozeOpt(); - SQInteger second_exp = _fs->PopTarget(); - if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); - _fs->SnoozeOpt(); - _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); - break; - }else return; - } - void LogicalAndExp() - { - BitwiseOrExp(); - for(;;) switch(_token) { - case TK_AND: { - SQInteger first_exp = _fs->PopTarget(); - SQInteger trg = _fs->PushTarget(); - _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0); - SQInteger jpos = _fs->GetCurrentPos(); - if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); - Lex(); LogicalAndExp(); - _fs->SnoozeOpt(); - SQInteger second_exp = _fs->PopTarget(); - if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); - _fs->SnoozeOpt(); - _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); - break; - } - case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break; - case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break; - default: - return; - } - } - void BitwiseOrExp() - { - BitwiseXorExp(); - for(;;) if(_token == _SC('|')) - {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR); - }else return; - } - void BitwiseXorExp() - { - BitwiseAndExp(); - for(;;) if(_token == _SC('^')) - {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR); - }else return; - } - void BitwiseAndExp() - { - CompExp(); - for(;;) if(_token == _SC('&')) - {BIN_EXP(_OP_BITW, &SQCompiler::CompExp,BW_AND); - }else return; - } - void CompExp() - { - ShiftExp(); - for(;;) switch(_token) { - case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::ShiftExp); break; - case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break; - case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break; - case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break; - case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break; - case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::ShiftExp); break; - default: return; - } - } - void ShiftExp() - { - PlusExp(); - for(;;) switch(_token) { - case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break; - case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break; - case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break; - default: return; - } - } - void PlusExp() - { - MultExp(); - for(;;) switch(_token) { - case _SC('+'): case _SC('-'): - BIN_EXP(_OP_ARITH, &SQCompiler::MultExp,_token); break; - default: return; - } - } - - void MultExp() - { - PrefixedExpr(); - for(;;) switch(_token) { - case _SC('*'): case _SC('/'): case _SC('%'): - BIN_EXP(_OP_ARITH, &SQCompiler::PrefixedExpr,_token); break; - default: return; - } - } - //if 'pos' != -1 the previous variable is a local variable - void PrefixedExpr() - { - SQInteger pos = Factor(); - for(;;) { - switch(_token) { - case _SC('.'): { - pos = -1; - Lex(); - if(_token == TK_PARENT) { - Lex(); - if(!NeedGet()) - Error(_SC("parent cannot be set")); - SQInteger src = _fs->PopTarget(); - _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src); - } - else { - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); - if(NeedGet()) Emit2ArgsOP(_OP_GET); - } - _exst._deref = DEREF_FIELD; - _exst._freevar = false; - } - break; - case _SC('['): - if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration")); - Lex(); Expression(); Expect(_SC(']')); - pos = -1; - if(NeedGet()) Emit2ArgsOP(_OP_GET); - _exst._deref = DEREF_FIELD; - _exst._freevar = false; - break; - case TK_MINUSMINUS: - case TK_PLUSPLUS: - if(_exst._deref != DEREF_NO_DEREF && !IsEndOfStatement()) { - SQInteger tok = _token; Lex(); - if(pos < 0) - Emit2ArgsOP(_OP_PINC,tok == TK_MINUSMINUS?-1:1); - else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local - SQInteger src = _fs->PopTarget(); - _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS?-1:1); - } - - } - return; - break; - case _SC('('): - { - if(_exst._deref != DEREF_NO_DEREF) { - if(pos<0) { - SQInteger key = _fs->PopTarget(); //key - SQInteger table = _fs->PopTarget(); //table etc... - SQInteger closure = _fs->PushTarget(); - SQInteger ttarget = _fs->PushTarget(); - _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); - } - else{ - _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); - } - } - else - _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); - _exst._deref = DEREF_NO_DEREF; - Lex(); - FunctionCallArgs(); - } - break; - default: return; - } - } - } - SQInteger Factor() - { - switch(_token) - { - case TK_STRING_LITERAL: { - //SQObjectPtr id(SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1)); - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); - Lex(); - } - break; - case TK_VARGC: Lex(); _fs->AddInstruction(_OP_VARGC, _fs->PushTarget()); break; - case TK_VARGV: { Lex(); - Expect(_SC('[')); - Expression(); - Expect(_SC(']')); - SQInteger src = _fs->PopTarget(); - _fs->AddInstruction(_OP_GETVARGV, _fs->PushTarget(), src); - } - break; - case TK_IDENTIFIER: - case TK_CONSTRUCTOR: - case TK_THIS:{ - _exst._freevar = false; - SQObject id; - switch(_token) { - case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break; - case TK_THIS: id = _fs->CreateString(_SC("this")); break; - case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor")); break; - } - SQInteger pos = -1; - Lex(); - if((pos = _fs->GetLocalVariable(id)) == -1) { - //checks if is a free variable - if((pos = _fs->GetOuterVariable(id)) != -1) { - _exst._deref = _fs->PushTarget(); - _fs->AddInstruction(_OP_LOADFREEVAR, _exst._deref ,pos); - _exst._freevar = true; - } else { - _fs->PushTarget(0); - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); - if(NeedGet()) Emit2ArgsOP(_OP_GET); - _exst._deref = DEREF_FIELD; - } - } - else{ - _fs->PushTarget(pos); - _exst._deref = pos; - } - return _exst._deref; - } - break; - case TK_PARENT: Lex();_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), 0); break; - case TK_DOUBLE_COLON: // "::" - _fs->AddInstruction(_OP_LOADROOTTABLE, _fs->PushTarget()); - _exst._deref = DEREF_FIELD; - _token = _SC('.'); //hack - return -1; - break; - case TK_NULL: - _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); - Lex(); - break; - case TK_INTEGER: { - if((_lex._nvalue & (~0x7FFFFFFF)) == 0) { //does it fit in 32 bits? - _fs->AddInstruction(_OP_LOADINT, _fs->PushTarget(),_lex._nvalue); - } - else { - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._nvalue)); - } - Lex(); - } - break; - case TK_FLOAT: - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue)); - Lex(); - break; - case TK_TRUE: case TK_FALSE: - _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0); - Lex(); - break; - case _SC('['): { - _fs->AddInstruction(_OP_NEWARRAY, _fs->PushTarget()); - SQInteger apos = _fs->GetCurrentPos(),key = 0; - Lex(); - while(_token != _SC(']')) { - Expression(); - if(_token == _SC(',')) Lex(); - SQInteger val = _fs->PopTarget(); - SQInteger array = _fs->TopTarget(); - _fs->AddInstruction(_OP_APPENDARRAY, array, val); - key++; - } - _fs->SetIntructionParam(apos, 1, key); - Lex(); - } - break; - case _SC('{'):{ - _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); - Lex();ParseTableOrClass(_SC(',')); - } - break; - case TK_FUNCTION: FunctionExp(_token);break; - case TK_CLASS: Lex(); ClassExp();break; - case _SC('-'): UnaryOP(_OP_NEG); break; - case _SC('!'): UnaryOP(_OP_NOT); break; - case _SC('~'): UnaryOP(_OP_BWNOT); break; - case TK_TYPEOF : UnaryOP(_OP_TYPEOF); break; - case TK_RESUME : UnaryOP(_OP_RESUME); break; - case TK_CLONE : UnaryOP(_OP_CLONE); break; - case TK_MINUSMINUS : - case TK_PLUSPLUS :PrefixIncDec(_token); break; - case TK_DELETE : DeleteExpr(); break; - case TK_DELEGATE : DelegateExpr(); break; - case _SC('('): Lex(); CommaExpr(); Expect(_SC(')')); - break; - default: Error(_SC("expression expected")); - } - return -1; - } - void UnaryOP(SQOpcode op) - { - Lex(); PrefixedExpr(); - SQInteger src = _fs->PopTarget(); - _fs->AddInstruction(op, _fs->PushTarget(), src); - } - bool NeedGet() - { - switch(_token) { - case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_PLUSPLUS: case TK_MINUSMINUS: - case TK_PLUSEQ: case TK_MINUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ: - return false; - } - return (!_exst._class_or_delete) || (_exst._class_or_delete && (_token == _SC('.') || _token == _SC('['))); - } - - void FunctionCallArgs() - { - SQInteger nargs = 1;//this - while(_token != _SC(')')) { - Expression(true); - MoveIfCurrentTargetIsLocal(); - nargs++; - if(_token == _SC(',')){ - Lex(); - if(_token == ')') Error(_SC("expression expected, found ')'")); - } - } - Lex(); - for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget(); - SQInteger stackbase = _fs->PopTarget(); - SQInteger closure = _fs->PopTarget(); - _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); - } - void ParseTableOrClass(SQInteger separator,SQInteger terminator = '}') - { - SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0; - - while(_token != terminator) { - bool hasattrs = false; - bool isstatic = false; - //check if is an attribute - if(separator == ';') { - if(_token == TK_ATTR_OPEN) { - _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); Lex(); - ParseTableOrClass(',',TK_ATTR_CLOSE); - hasattrs = true; - } - if(_token == TK_STATIC) { - isstatic = true; - Lex(); - } - } - switch(_token) { - case TK_FUNCTION: - case TK_CONSTRUCTOR:{ - SQInteger tk = _token; - Lex(); - SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor")); - Expect(_SC('(')); - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); - CreateFunction(id); - _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); - } - break; - case _SC('['): - Lex(); CommaExpr(); Expect(_SC(']')); - Expect(_SC('=')); Expression(); - break; - default : - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); - Expect(_SC('=')); Expression(); - } - - if(_token == separator) Lex();//optional comma/semicolon - nkeys++; - SQInteger val = _fs->PopTarget(); - SQInteger key = _fs->PopTarget(); - SQInteger attrs = hasattrs ? _fs->PopTarget():-1; - assert(hasattrs && attrs == key-1 || !hasattrs); - unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0); - SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOTA, flags, table, key, val); - //_fs->PopTarget(); - } - if(separator == _SC(',')) //hack recognizes a table from the separator - _fs->SetIntructionParam(tpos, 1, nkeys); - Lex(); - } - void LocalDeclStatement() - { - SQObject varname; - do { - Lex(); varname = Expect(TK_IDENTIFIER); - if(_token == _SC('=')) { - Lex(); Expression(); - SQInteger src = _fs->PopTarget(); - SQInteger dest = _fs->PushTarget(); - if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src); - } - else{ - _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); - } - _fs->PopTarget(); - _fs->PushLocalVariable(varname); - - } while(_token == _SC(',')); - } - void IfStatement() - { - SQInteger jmppos; - bool haselse = false; - Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); - _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); - SQInteger jnepos = _fs->GetCurrentPos(); - SQInteger stacksize = _fs->GetStackSize(); - - Statement(); - // - if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon(); - - CleanStack(stacksize); - SQInteger endifblock = _fs->GetCurrentPos(); - if(_token == TK_ELSE){ - haselse = true; - stacksize = _fs->GetStackSize(); - _fs->AddInstruction(_OP_JMP); - jmppos = _fs->GetCurrentPos(); - Lex(); - Statement(); OptionalSemicolon(); - CleanStack(stacksize); - _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); - } - _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); - } - void WhileStatement() - { - SQInteger jzpos, jmppos; - SQInteger stacksize = _fs->GetStackSize(); - jmppos = _fs->GetCurrentPos(); - Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); - - BEGIN_BREAKBLE_BLOCK(); - _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); - jzpos = _fs->GetCurrentPos(); - stacksize = _fs->GetStackSize(); - - Statement(); - - CleanStack(stacksize); - _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); - _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); - - END_BREAKBLE_BLOCK(jmppos); - } - void DoWhileStatement() - { - Lex(); - SQInteger jzpos = _fs->GetCurrentPos(); - SQInteger stacksize = _fs->GetStackSize(); - BEGIN_BREAKBLE_BLOCK() - Statement(); - CleanStack(stacksize); - Expect(TK_WHILE); - SQInteger continuetrg = _fs->GetCurrentPos(); - Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); - _fs->AddInstruction(_OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1); - END_BREAKBLE_BLOCK(continuetrg); - } - void ForStatement() - { - Lex(); - SQInteger stacksize = _fs->GetStackSize(); - Expect(_SC('(')); - if(_token == TK_LOCAL) LocalDeclStatement(); - else if(_token != _SC(';')){ - CommaExpr(); - _fs->PopTarget(); - } - Expect(_SC(';')); - _fs->SnoozeOpt(); - SQInteger jmppos = _fs->GetCurrentPos(); - SQInteger jzpos = -1; - if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } - Expect(_SC(';')); - _fs->SnoozeOpt(); - SQInteger expstart = _fs->GetCurrentPos() + 1; - if(_token != _SC(')')) { - CommaExpr(); - _fs->PopTarget(); - } - Expect(_SC(')')); - _fs->SnoozeOpt(); - SQInteger expend = _fs->GetCurrentPos(); - SQInteger expsize = (expend - expstart) + 1; - SQInstructionVec exp; - if(expsize > 0) { - for(SQInteger i = 0; i < expsize; i++) - exp.push_back(_fs->GetInstruction(expstart + i)); - _fs->PopInstructions(expsize); - } - BEGIN_BREAKBLE_BLOCK() - Statement(); - SQInteger continuetrg = _fs->GetCurrentPos(); - if(expsize > 0) { - for(SQInteger i = 0; i < expsize; i++) - _fs->AddInstruction(exp[i]); - } - _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); - if(jzpos> 0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); - CleanStack(stacksize); - - END_BREAKBLE_BLOCK(continuetrg); - } - void ForEachStatement() - { - SQObject idxname, valname; - Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER); - if(_token == _SC(',')) { - idxname = valname; - Lex(); valname = Expect(TK_IDENTIFIER); - } - else{ - idxname = _fs->CreateString(_SC("@INDEX@")); - } - Expect(TK_IN); - - //save the stack size - SQInteger stacksize = _fs->GetStackSize(); - //put the table in the stack(evaluate the table expression) - Expression(); Expect(_SC(')')); - SQInteger container = _fs->TopTarget(); - //push the index local var - SQInteger indexpos = _fs->PushLocalVariable(idxname); - _fs->AddInstruction(_OP_LOADNULLS, indexpos,1); - //push the value local var - SQInteger valuepos = _fs->PushLocalVariable(valname); - _fs->AddInstruction(_OP_LOADNULLS, valuepos,1); - //push reference index - SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible - _fs->AddInstruction(_OP_LOADNULLS, itrpos,1); - SQInteger jmppos = _fs->GetCurrentPos(); - _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos); - SQInteger foreachpos = _fs->GetCurrentPos(); - //generate the statement code - BEGIN_BREAKBLE_BLOCK() - Statement(); - _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); - _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); - //restore the local variable stack(remove index,val and ref idx) - CleanStack(stacksize); - END_BREAKBLE_BLOCK(foreachpos - 1); - } - void SwitchStatement() - { - Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); - Expect(_SC('{')); - SQInteger expr = _fs->TopTarget(); - bool bfirst = true; - SQInteger tonextcondjmp = -1; - SQInteger skipcondjmp = -1; - SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size(); - _fs->_breaktargets.push_back(0); - while(_token == TK_CASE) { - //_fs->AddLineInfos(_lex._currentline, _lineinfo); think about this one - if(!bfirst) { - _fs->AddInstruction(_OP_JMP, 0, 0); - skipcondjmp = _fs->GetCurrentPos(); - _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); - } - //condition - Lex(); Expression(); Expect(_SC(':')); - SQInteger trg = _fs->PopTarget(); - _fs->AddInstruction(_OP_EQ, trg, trg, expr); - _fs->AddInstruction(_OP_JZ, trg, 0); - //end condition - if(skipcondjmp != -1) { - _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); - } - tonextcondjmp = _fs->GetCurrentPos(); - SQInteger stacksize = _fs->GetStackSize(); - Statements(); - _fs->SetStackSize(stacksize); - bfirst = false; - } - if(tonextcondjmp != -1) - _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); - if(_token == TK_DEFAULT) { - // _fs->AddLineInfos(_lex._currentline, _lineinfo); - Lex(); Expect(_SC(':')); - SQInteger stacksize = _fs->GetStackSize(); - Statements(); - _fs->SetStackSize(stacksize); - } - Expect(_SC('}')); - _fs->PopTarget(); - __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; - if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__); - _fs->_breaktargets.pop_back(); - - } - void FunctionStatement() - { - SQObject id; - Lex(); id = Expect(TK_IDENTIFIER); - _fs->PushTarget(0); - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); - if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); - - while(_token == TK_DOUBLE_COLON) { - Lex(); - id = Expect(TK_IDENTIFIER); - _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); - if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); - } - Expect(_SC('(')); - CreateFunction(id); - _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); - EmitDerefOp(_OP_NEWSLOT); - _fs->PopTarget(); - } - void ClassStatement() - { - ExpState es; - Lex(); PushExpState(); - _exst._class_or_delete = true; - _exst._funcarg = false; - PrefixedExpr(); - es = PopExpState(); - if(es._deref == DEREF_NO_DEREF) Error(_SC("invalid class name")); - if(es._deref == DEREF_FIELD) { - ClassExp(); - EmitDerefOp(_OP_NEWSLOT); - _fs->PopTarget(); - } - else Error(_SC("cannot create a class in a local with the syntax(class )")); - } - void TryCatchStatement() - { - SQObject exid; - Lex(); - _fs->AddInstruction(_OP_PUSHTRAP,0,0); - _fs->_traps++; - if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++; - if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++; - SQInteger trappos = _fs->GetCurrentPos(); - Statement(); - _fs->_traps--; - _fs->AddInstruction(_OP_POPTRAP, 1, 0); - if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--; - if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--; - _fs->AddInstruction(_OP_JMP, 0, 0); - SQInteger jmppos = _fs->GetCurrentPos(); - _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); - Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')')); - SQInteger stacksize = _fs->GetStackSize(); - SQInteger ex_target = _fs->PushLocalVariable(exid); - _fs->SetIntructionParam(trappos, 0, ex_target); - Statement(); - _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); - CleanStack(stacksize); - } - void FunctionExp(SQInteger ftype) - { - Lex(); Expect(_SC('(')); - CreateFunction(_null_); - _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1); - } - void ClassExp() - { - SQInteger base = -1; - SQInteger attrs = -1; - if(_token == TK_EXTENDS) { - Lex(); Expression(); - base = _fs->TopTarget(); - } - if(_token == TK_ATTR_OPEN) { - Lex(); - _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); - ParseTableOrClass(_SC(','),TK_ATTR_CLOSE); - attrs = _fs->TopTarget(); - } - Expect(_SC('{')); - if(attrs != -1) _fs->PopTarget(); - if(base != -1) _fs->PopTarget(); - _fs->AddInstruction(_OP_CLASS, _fs->PushTarget(), base, attrs); - ParseTableOrClass(_SC(';')); - } - void DelegateExpr() - { - Lex(); CommaExpr(); - Expect(_SC(':')); - CommaExpr(); - SQInteger table = _fs->PopTarget(), delegate = _fs->PopTarget(); - _fs->AddInstruction(_OP_DELEGATE, _fs->PushTarget(), table, delegate); - } - void DeleteExpr() - { - ExpState es; - Lex(); PushExpState(); - _exst._class_or_delete = true; - _exst._funcarg = false; - PrefixedExpr(); - es = PopExpState(); - if(es._deref == DEREF_NO_DEREF) Error(_SC("can't delete an expression")); - if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE); - else Error(_SC("cannot delete a local")); - } - void PrefixIncDec(SQInteger token) - { - ExpState es; - Lex(); PushExpState(); - _exst._class_or_delete = true; - _exst._funcarg = false; - PrefixedExpr(); - es = PopExpState(); - if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_INC,token == TK_MINUSMINUS?-1:1); - else { - SQInteger src = _fs->PopTarget(); - _fs->AddInstruction(_OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS?-1:1); - } - } - void CreateFunction(SQObject &name) - { - - SQFuncState *funcstate = _fs->PushChildState(_ss(_vm)); - funcstate->_name = name; - SQObject paramname; - funcstate->AddParameter(_fs->CreateString(_SC("this"))); - funcstate->_sourcename = _sourcename; - while(_token!=_SC(')')) { - if(_token == TK_VARPARAMS) { - funcstate->_varparams = true; - Lex(); - if(_token != _SC(')')) Error(_SC("expected ')'")); - break; - } - else { - paramname = Expect(TK_IDENTIFIER); - funcstate->AddParameter(paramname); - if(_token == _SC(',')) Lex(); - else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); - } - } - Expect(_SC(')')); - //outer values - if(_token == _SC(':')) { - Lex(); Expect(_SC('(')); - while(_token != _SC(')')) { - paramname = Expect(TK_IDENTIFIER); - //outers are treated as implicit local variables - funcstate->AddOuterValue(paramname); - if(_token == _SC(',')) Lex(); - else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); - } - Lex(); - } - - SQFuncState *currchunk = _fs; - _fs = funcstate; - Statement(); - funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true); - funcstate->AddInstruction(_OP_RETURN, -1); - funcstate->SetStackSize(0); - //_fs->->_stacksize = _fs->_stacksize; - SQFunctionProto *func = funcstate->BuildProto(); -#ifdef _DEBUG_DUMP - funcstate->Dump(func); -#endif - _fs = currchunk; - _fs->_functions.push_back(func); - _fs->PopChildState(); - } - void CleanStack(SQInteger stacksize) - { - if(_fs->GetStackSize() != stacksize) - _fs->SetStackSize(stacksize); - } - void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve) - { - while(ntoresolve > 0) { - SQInteger pos = funcstate->_unresolvedbreaks.back(); - funcstate->_unresolvedbreaks.pop_back(); - //set the jmp instruction - funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); - ntoresolve--; - } - } - void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos) - { - while(ntoresolve > 0) { - SQInteger pos = funcstate->_unresolvedcontinues.back(); - funcstate->_unresolvedcontinues.pop_back(); - //set the jmp instruction - funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0); - ntoresolve--; - } - } -private: - SQInteger _token; - SQFuncState *_fs; - SQObjectPtr _sourcename; - SQLexer _lex; - bool _lineinfo; - bool _raiseerror; - SQInteger _debugline; - SQInteger _debugop; - ExpStateVec _expstates; - SQChar *compilererror; - jmp_buf _errorjmp; - SQVM *_vm; -}; - -bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo) -{ - SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo); - return p.Compile(out); -} +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include +#include "sqopcodes.h" +#include "sqstring.h" +#include "sqfuncproto.h" +#include "sqcompiler.h" +#include "sqfuncstate.h" +#include "sqlexer.h" +#include "sqvm.h" + +#define DEREF_NO_DEREF -1 +#define DEREF_FIELD -2 + +struct ExpState +{ + ExpState() + { + _deref = DEREF_NO_DEREF; + _freevar = false; + _class_or_delete = false; + _funcarg = false; + } + bool _class_or_delete; + bool _funcarg; + bool _freevar; + SQInteger _deref; +}; + +typedef sqvector ExpStateVec; + +#define _exst (_expstates.top()) + +#define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \ + SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \ + _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0); + +#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \ + __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \ + if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \ + if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \ + _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();} + +class SQCompiler +{ +public: + SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo) + { + _vm=v; + _lex.Init(_ss(v), rg, up,ThrowError,this); + _sourcename = SQString::Create(_ss(v), sourcename); + _lineinfo = lineinfo;_raiseerror = raiseerror; + compilererror = NULL; + } + static void ThrowError(void *ud, const SQChar *s) { + SQCompiler *c = (SQCompiler *)ud; + c->Error(s); + } + void Error(const SQChar *s, ...) + { + static SQChar temp[256]; + va_list vl; + va_start(vl, s); + scvsprintf(temp, s, vl); + va_end(vl); + compilererror = temp; + longjmp(_errorjmp,1); + } + void Lex(){ _token = _lex.Lex();} + void PushExpState(){ _expstates.push_back(ExpState()); } + bool IsDerefToken(SQInteger tok) + { + switch(tok){ + case _SC('='): case _SC('('): case TK_NEWSLOT: + case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS: return true; + } + return false; + } + ExpState PopExpState() + { + ExpState ret = _expstates.top(); + _expstates.pop_back(); + return ret; + } + SQObject Expect(SQInteger tok) + { + + if(_token != tok) { + if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) { + //ret = SQString::Create(_ss(_vm),_SC("constructor")); + //do nothing + } + else { + const SQChar *etypename; + if(tok > 255) { + switch(tok) + { + case TK_IDENTIFIER: + etypename = _SC("IDENTIFIER"); + break; + case TK_STRING_LITERAL: + etypename = _SC("STRING_LITERAL"); + break; + case TK_INTEGER: + etypename = _SC("INTEGER"); + break; + case TK_FLOAT: + etypename = _SC("FLOAT"); + break; + default: + etypename = _lex.Tok2Str(tok); + } + Error(_SC("expected '%s'"), etypename); + } + Error(_SC("expected '%c'"), tok); + } + } + SQObjectPtr ret; + switch(tok) + { + case TK_IDENTIFIER: + ret = _fs->CreateString(_lex._svalue); + break; + case TK_STRING_LITERAL: + ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); + break; + case TK_INTEGER: + ret = SQObjectPtr(_lex._nvalue); + break; + case TK_FLOAT: + ret = SQObjectPtr(_lex._fvalue); + break; + } + Lex(); + return ret; + } + bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); } + void OptionalSemicolon() + { + if(_token == _SC(';')) { Lex(); return; } + if(!IsEndOfStatement()) { + Error(_SC("end of statement expected (; or lf)")); + } + } + void MoveIfCurrentTargetIsLocal() { + SQInteger trg = _fs->TopTarget(); + if(_fs->IsLocal(trg)) { + trg = _fs->PopTarget(); //no pops the target and move it + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); + } + } + bool Compile(SQObjectPtr &o) + { + _debugline = 1; + _debugop = 0; + + SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this); + funcstate._name = SQString::Create(_ss(_vm), _SC("main")); + _fs = &funcstate; + _fs->AddParameter(_fs->CreateString(_SC("this"))); + _fs->_sourcename = _sourcename; + SQInteger stacksize = _fs->GetStackSize(); + if(setjmp(_errorjmp) == 0) { + Lex(); + while(_token > 0){ + Statement(); + if(_lex._prevtoken != _SC('}')) OptionalSemicolon(); + } + CleanStack(stacksize); + _fs->AddLineInfos(_lex._currentline, _lineinfo, true); + _fs->AddInstruction(_OP_RETURN, 0xFF); + _fs->SetStackSize(0); + o =_fs->BuildProto(); +#ifdef _DEBUG_DUMP + _fs->Dump(_funcproto(o)); +#endif + } + else { + if(_raiseerror && _ss(_vm)->_compilererrorhandler) { + _ss(_vm)->_compilererrorhandler(_vm, compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), + _lex._currentline, _lex._currentcolumn); + } + _vm->_lasterror = SQString::Create(_ss(_vm), compilererror, -1); + return false; + } + return true; + } + void Statements() + { + while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) { + Statement(); + if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + } + } + void Statement() + { + _fs->AddLineInfos(_lex._currentline, _lineinfo); + switch(_token){ + case _SC(';'): Lex(); break; + case TK_IF: IfStatement(); break; + case TK_WHILE: WhileStatement(); break; + case TK_DO: DoWhileStatement(); break; + case TK_FOR: ForStatement(); break; + case TK_FOREACH: ForEachStatement(); break; + case TK_SWITCH: SwitchStatement(); break; + case TK_LOCAL: LocalDeclStatement(); break; + case TK_RETURN: + case TK_YIELD: { + SQOpcode op; + if(_token == TK_RETURN) { + op = _OP_RETURN; + + } + else { + op = _OP_YIELD; + _fs->_bgenerator = true; + } + Lex(); + if(!IsEndOfStatement()) { + SQInteger retexp = _fs->GetCurrentPos()+1; + CommaExpr(); + if(op == _OP_RETURN && _fs->_traps > 0) + _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); + _fs->_returnexp = retexp; + _fs->AddInstruction(op, 1, _fs->PopTarget()); + } + else{ + if(op == _OP_RETURN && _fs->_traps > 0) + _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0); + _fs->_returnexp = -1; + _fs->AddInstruction(op, 0xFF); + } + break;} + case TK_BREAK: + if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block")); + if(_fs->_breaktargets.top() > 0){ + _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); + } + _fs->AddInstruction(_OP_JMP, 0, -1234); + _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); + Lex(); + break; + case TK_CONTINUE: + if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block")); + if(_fs->_continuetargets.top() > 0) { + _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); + } + _fs->AddInstruction(_OP_JMP, 0, -1234); + _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); + Lex(); + break; + case TK_FUNCTION: + FunctionStatement(); + break; + case TK_CLASS: + ClassStatement(); + break; + case _SC('{'):{ + SQInteger stacksize = _fs->GetStackSize(); + Lex(); + Statements(); + Expect(_SC('}')); + _fs->SetStackSize(stacksize); + } + break; + case TK_TRY: + TryCatchStatement(); + break; + case TK_THROW: + Lex(); + CommaExpr(); + _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); + break; + default: + CommaExpr(); + _fs->PopTarget(); + break; + } + _fs->SnoozeOpt(); + } + void EmitDerefOp(SQOpcode op) + { + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(op,_fs->PushTarget(),src,key,val); + } + void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0) + { + SQInteger p2 = _fs->PopTarget(); //src in OP_GET + SQInteger p1 = _fs->PopTarget(); //key in OP_GET + _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3); + } + void EmitCompoundArith(SQInteger tok,bool deref) + { + SQInteger oper; + switch(tok){ + case TK_MINUSEQ: oper = '-'; break; + case TK_PLUSEQ: oper = '+'; break; + case TK_MULEQ: oper = '*'; break; + case TK_DIVEQ: oper = '/'; break; + case TK_MODEQ: oper = '%'; break; + default: oper = 0; //shut up compiler + assert(0); break; + }; + if(deref) { + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger src = _fs->PopTarget(); + //mixes dest obj and source val in the arg1(hack?) + _fs->AddInstruction(_OP_COMPARITH,_fs->PushTarget(),(src<<16)|val,key,oper); + } + else { + Emit2ArgsOP(_OP_COMPARITHL, oper); + } + } + void CommaExpr() + { + for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr()); + } + ExpState Expression(bool funcarg = false) + { + PushExpState(); + _exst._class_or_delete = false; + _exst._funcarg = funcarg; + LogicalOrExp(); + switch(_token) { + case _SC('='): + case TK_NEWSLOT: + case TK_MINUSEQ: + case TK_PLUSEQ: + case TK_MULEQ: + case TK_DIVEQ: + case TK_MODEQ: + { + SQInteger op = _token; + SQInteger ds = _exst._deref; + bool freevar = _exst._freevar; + if(ds == DEREF_NO_DEREF) Error(_SC("can't assign expression")); + Lex(); Expression(); + + switch(op){ + case TK_NEWSLOT: + if(freevar) Error(_SC("free variables cannot be modified")); + if(ds == DEREF_FIELD) + EmitDerefOp(_OP_NEWSLOT); + else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local + Error(_SC("can't 'create' a local slot")); + break; + case _SC('='): //ASSIGN + if(freevar) Error(_SC("free variables cannot be modified")); + if(ds == DEREF_FIELD) + EmitDerefOp(_OP_SET); + else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local + SQInteger p2 = _fs->PopTarget(); //src in OP_GET + SQInteger p1 = _fs->TopTarget(); //key in OP_GET + _fs->AddInstruction(_OP_MOVE, p1, p2); + } + break; + case TK_MINUSEQ: + case TK_PLUSEQ: + case TK_MULEQ: + case TK_DIVEQ: + case TK_MODEQ: + EmitCompoundArith(op,ds == DEREF_FIELD); + break; + } + } + break; + case _SC('?'): { + Lex(); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + SQInteger jzpos = _fs->GetCurrentPos(); + SQInteger trg = _fs->PushTarget(); + Expression(); + SQInteger first_exp = _fs->PopTarget(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + SQInteger endfirstexp = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_JMP, 0, 0); + Expect(_SC(':')); + SQInteger jmppos = _fs->GetCurrentPos(); + Expression(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1); + _fs->SnoozeOpt(); + } + break; + } + return PopExpState(); + } + void BIN_EXP(SQOpcode op, void (SQCompiler::*f)(void),SQInteger op3 = 0) + { + Lex(); (this->*f)(); + SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget(); + _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); + } + void LogicalOrExp() + { + LogicalAndExp(); + for(;;) if(_token == TK_OR) { + SQInteger first_exp = _fs->PopTarget(); + SQInteger trg = _fs->PushTarget(); + _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0); + SQInteger jpos = _fs->GetCurrentPos(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + Lex(); LogicalOrExp(); + _fs->SnoozeOpt(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SnoozeOpt(); + _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + break; + }else return; + } + void LogicalAndExp() + { + BitwiseOrExp(); + for(;;) switch(_token) { + case TK_AND: { + SQInteger first_exp = _fs->PopTarget(); + SQInteger trg = _fs->PushTarget(); + _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0); + SQInteger jpos = _fs->GetCurrentPos(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + Lex(); LogicalAndExp(); + _fs->SnoozeOpt(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SnoozeOpt(); + _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + break; + } + case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break; + case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break; + default: + return; + } + } + void BitwiseOrExp() + { + BitwiseXorExp(); + for(;;) if(_token == _SC('|')) + {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR); + }else return; + } + void BitwiseXorExp() + { + BitwiseAndExp(); + for(;;) if(_token == _SC('^')) + {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR); + }else return; + } + void BitwiseAndExp() + { + CompExp(); + for(;;) if(_token == _SC('&')) + {BIN_EXP(_OP_BITW, &SQCompiler::CompExp,BW_AND); + }else return; + } + void CompExp() + { + ShiftExp(); + for(;;) switch(_token) { + case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::ShiftExp); break; + case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break; + case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break; + case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break; + case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break; + case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::ShiftExp); break; + default: return; + } + } + void ShiftExp() + { + PlusExp(); + for(;;) switch(_token) { + case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break; + case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break; + case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break; + default: return; + } + } + void PlusExp() + { + MultExp(); + for(;;) switch(_token) { + case _SC('+'): case _SC('-'): + BIN_EXP(_OP_ARITH, &SQCompiler::MultExp,_token); break; + default: return; + } + } + + void MultExp() + { + PrefixedExpr(); + for(;;) switch(_token) { + case _SC('*'): case _SC('/'): case _SC('%'): + BIN_EXP(_OP_ARITH, &SQCompiler::PrefixedExpr,_token); break; + default: return; + } + } + //if 'pos' != -1 the previous variable is a local variable + void PrefixedExpr() + { + SQInteger pos = Factor(); + for(;;) { + switch(_token) { + case _SC('.'): { + pos = -1; + Lex(); + if(_token == TK_PARENT) { + Lex(); + if(!NeedGet()) + Error(_SC("parent cannot be set")); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src); + } + else { + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + if(NeedGet()) Emit2ArgsOP(_OP_GET); + } + _exst._deref = DEREF_FIELD; + _exst._freevar = false; + } + break; + case _SC('['): + if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration")); + Lex(); Expression(); Expect(_SC(']')); + pos = -1; + if(NeedGet()) Emit2ArgsOP(_OP_GET); + _exst._deref = DEREF_FIELD; + _exst._freevar = false; + break; + case TK_MINUSMINUS: + case TK_PLUSPLUS: + if(_exst._deref != DEREF_NO_DEREF && !IsEndOfStatement()) { + SQInteger tok = _token; Lex(); + if(pos < 0) + Emit2ArgsOP(_OP_PINC,tok == TK_MINUSMINUS?-1:1); + else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS?-1:1); + } + + } + return; + break; + case _SC('('): + { + if(_exst._deref != DEREF_NO_DEREF) { + if(pos<0) { + SQInteger key = _fs->PopTarget(); //key + SQInteger table = _fs->PopTarget(); //table etc... + SQInteger closure = _fs->PushTarget(); + SQInteger ttarget = _fs->PushTarget(); + _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); + } + else{ + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + } + } + else + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + _exst._deref = DEREF_NO_DEREF; + Lex(); + FunctionCallArgs(); + } + break; + default: return; + } + } + } + SQInteger Factor() + { + switch(_token) + { + case TK_STRING_LITERAL: { + //SQObjectPtr id(SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1)); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); + Lex(); + } + break; + case TK_VARGC: Lex(); _fs->AddInstruction(_OP_VARGC, _fs->PushTarget()); break; + case TK_VARGV: { Lex(); + Expect(_SC('[')); + Expression(); + Expect(_SC(']')); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(_OP_GETVARGV, _fs->PushTarget(), src); + } + break; + case TK_IDENTIFIER: + case TK_CONSTRUCTOR: + case TK_THIS:{ + _exst._freevar = false; + SQObject id; + switch(_token) { + case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break; + case TK_THIS: id = _fs->CreateString(_SC("this")); break; + case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor")); break; + } + SQInteger pos = -1; + Lex(); + if((pos = _fs->GetLocalVariable(id)) == -1) { + //checks if is a free variable + if((pos = _fs->GetOuterVariable(id)) != -1) { + _exst._deref = _fs->PushTarget(); + _fs->AddInstruction(_OP_LOADFREEVAR, _exst._deref ,pos); + _exst._freevar = true; + } else { + _fs->PushTarget(0); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(NeedGet()) Emit2ArgsOP(_OP_GET); + _exst._deref = DEREF_FIELD; + } + } + else{ + _fs->PushTarget(pos); + _exst._deref = pos; + } + return _exst._deref; + } + break; + case TK_PARENT: Lex();_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), 0); break; + case TK_DOUBLE_COLON: // "::" + _fs->AddInstruction(_OP_LOADROOTTABLE, _fs->PushTarget()); + _exst._deref = DEREF_FIELD; + _token = _SC('.'); //hack + return -1; + break; + case TK_NULL: + _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); + Lex(); + break; + case TK_INTEGER: { + if((_lex._nvalue & (~0x7FFFFFFF)) == 0) { //does it fit in 32 bits? + _fs->AddInstruction(_OP_LOADINT, _fs->PushTarget(),_lex._nvalue); + } + else { + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._nvalue)); + } + Lex(); + } + break; + case TK_FLOAT: + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue)); + Lex(); + break; + case TK_TRUE: case TK_FALSE: + _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0); + Lex(); + break; + case _SC('['): { + _fs->AddInstruction(_OP_NEWARRAY, _fs->PushTarget()); + SQInteger apos = _fs->GetCurrentPos(),key = 0; + Lex(); + while(_token != _SC(']')) { + Expression(); + if(_token == _SC(',')) Lex(); + SQInteger val = _fs->PopTarget(); + SQInteger array = _fs->TopTarget(); + _fs->AddInstruction(_OP_APPENDARRAY, array, val); + key++; + } + _fs->SetIntructionParam(apos, 1, key); + Lex(); + } + break; + case _SC('{'):{ + _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); + Lex();ParseTableOrClass(_SC(',')); + } + break; + case TK_FUNCTION: FunctionExp(_token);break; + case TK_CLASS: Lex(); ClassExp();break; + case _SC('-'): UnaryOP(_OP_NEG); break; + case _SC('!'): UnaryOP(_OP_NOT); break; + case _SC('~'): UnaryOP(_OP_BWNOT); break; + case TK_TYPEOF : UnaryOP(_OP_TYPEOF); break; + case TK_RESUME : UnaryOP(_OP_RESUME); break; + case TK_CLONE : UnaryOP(_OP_CLONE); break; + case TK_MINUSMINUS : + case TK_PLUSPLUS :PrefixIncDec(_token); break; + case TK_DELETE : DeleteExpr(); break; + case TK_DELEGATE : DelegateExpr(); break; + case _SC('('): Lex(); CommaExpr(); Expect(_SC(')')); + break; + default: Error(_SC("expression expected")); + } + return -1; + } + void UnaryOP(SQOpcode op) + { + Lex(); PrefixedExpr(); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(op, _fs->PushTarget(), src); + } + bool NeedGet() + { + switch(_token) { + case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_PLUSPLUS: case TK_MINUSMINUS: + case TK_PLUSEQ: case TK_MINUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ: + return false; + } + return (!_exst._class_or_delete) || (_exst._class_or_delete && (_token == _SC('.') || _token == _SC('['))); + } + + void FunctionCallArgs() + { + SQInteger nargs = 1;//this + while(_token != _SC(')')) { + Expression(true); + MoveIfCurrentTargetIsLocal(); + nargs++; + if(_token == _SC(',')){ + Lex(); + if(_token == ')') Error(_SC("expression expected, found ')'")); + } + } + Lex(); + for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget(); + SQInteger stackbase = _fs->PopTarget(); + SQInteger closure = _fs->PopTarget(); + _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); + } + void ParseTableOrClass(SQInteger separator,SQInteger terminator = '}') + { + SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0; + + while(_token != terminator) { + bool hasattrs = false; + bool isstatic = false; + //check if is an attribute + if(separator == ';') { + if(_token == TK_ATTR_OPEN) { + _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); Lex(); + ParseTableOrClass(',',TK_ATTR_CLOSE); + hasattrs = true; + } + if(_token == TK_STATIC) { + isstatic = true; + Lex(); + } + } + switch(_token) { + case TK_FUNCTION: + case TK_CONSTRUCTOR:{ + SQInteger tk = _token; + Lex(); + SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor")); + Expect(_SC('(')); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + CreateFunction(id); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + } + break; + case _SC('['): + Lex(); CommaExpr(); Expect(_SC(']')); + Expect(_SC('=')); Expression(); + break; + default : + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + Expect(_SC('=')); Expression(); + } + + if(_token == separator) Lex();//optional comma/semicolon + nkeys++; + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger attrs = hasattrs ? _fs->PopTarget():-1; + assert(hasattrs && attrs == key-1 || !hasattrs); + unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0); + SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOTA, flags, table, key, val); + //_fs->PopTarget(); + } + if(separator == _SC(',')) //hack recognizes a table from the separator + _fs->SetIntructionParam(tpos, 1, nkeys); + Lex(); + } + void LocalDeclStatement() + { + SQObject varname; + do { + Lex(); varname = Expect(TK_IDENTIFIER); + if(_token == _SC('=')) { + Lex(); Expression(); + SQInteger src = _fs->PopTarget(); + SQInteger dest = _fs->PushTarget(); + if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src); + } + else{ + _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); + } + _fs->PopTarget(); + _fs->PushLocalVariable(varname); + + } while(_token == _SC(',')); + } + void IfStatement() + { + SQInteger jmppos; + bool haselse = false; + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + SQInteger jnepos = _fs->GetCurrentPos(); + SQInteger stacksize = _fs->GetStackSize(); + + Statement(); + // + if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon(); + + CleanStack(stacksize); + SQInteger endifblock = _fs->GetCurrentPos(); + if(_token == TK_ELSE){ + haselse = true; + stacksize = _fs->GetStackSize(); + _fs->AddInstruction(_OP_JMP); + jmppos = _fs->GetCurrentPos(); + Lex(); + Statement(); OptionalSemicolon(); + CleanStack(stacksize); + _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + } + _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); + } + void WhileStatement() + { + SQInteger jzpos, jmppos; + SQInteger stacksize = _fs->GetStackSize(); + jmppos = _fs->GetCurrentPos(); + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + + BEGIN_BREAKBLE_BLOCK(); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + jzpos = _fs->GetCurrentPos(); + stacksize = _fs->GetStackSize(); + + Statement(); + + CleanStack(stacksize); + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); + _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + + END_BREAKBLE_BLOCK(jmppos); + } + void DoWhileStatement() + { + Lex(); + SQInteger jzpos = _fs->GetCurrentPos(); + SQInteger stacksize = _fs->GetStackSize(); + BEGIN_BREAKBLE_BLOCK() + Statement(); + CleanStack(stacksize); + Expect(TK_WHILE); + SQInteger continuetrg = _fs->GetCurrentPos(); + Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + _fs->AddInstruction(_OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1); + END_BREAKBLE_BLOCK(continuetrg); + } + void ForStatement() + { + Lex(); + SQInteger stacksize = _fs->GetStackSize(); + Expect(_SC('(')); + if(_token == TK_LOCAL) LocalDeclStatement(); + else if(_token != _SC(';')){ + CommaExpr(); + _fs->PopTarget(); + } + Expect(_SC(';')); + _fs->SnoozeOpt(); + SQInteger jmppos = _fs->GetCurrentPos(); + SQInteger jzpos = -1; + if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } + Expect(_SC(';')); + _fs->SnoozeOpt(); + SQInteger expstart = _fs->GetCurrentPos() + 1; + if(_token != _SC(')')) { + CommaExpr(); + _fs->PopTarget(); + } + Expect(_SC(')')); + _fs->SnoozeOpt(); + SQInteger expend = _fs->GetCurrentPos(); + SQInteger expsize = (expend - expstart) + 1; + SQInstructionVec exp; + if(expsize > 0) { + for(SQInteger i = 0; i < expsize; i++) + exp.push_back(_fs->GetInstruction(expstart + i)); + _fs->PopInstructions(expsize); + } + BEGIN_BREAKBLE_BLOCK() + Statement(); + SQInteger continuetrg = _fs->GetCurrentPos(); + if(expsize > 0) { + for(SQInteger i = 0; i < expsize; i++) + _fs->AddInstruction(exp[i]); + } + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); + if(jzpos> 0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + CleanStack(stacksize); + + END_BREAKBLE_BLOCK(continuetrg); + } + void ForEachStatement() + { + SQObject idxname, valname; + Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER); + if(_token == _SC(',')) { + idxname = valname; + Lex(); valname = Expect(TK_IDENTIFIER); + } + else{ + idxname = _fs->CreateString(_SC("@INDEX@")); + } + Expect(TK_IN); + + //save the stack size + SQInteger stacksize = _fs->GetStackSize(); + //put the table in the stack(evaluate the table expression) + Expression(); Expect(_SC(')')); + SQInteger container = _fs->TopTarget(); + //push the index local var + SQInteger indexpos = _fs->PushLocalVariable(idxname); + _fs->AddInstruction(_OP_LOADNULLS, indexpos,1); + //push the value local var + SQInteger valuepos = _fs->PushLocalVariable(valname); + _fs->AddInstruction(_OP_LOADNULLS, valuepos,1); + //push reference index + SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible + _fs->AddInstruction(_OP_LOADNULLS, itrpos,1); + SQInteger jmppos = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos); + SQInteger foreachpos = _fs->GetCurrentPos(); + //generate the statement code + BEGIN_BREAKBLE_BLOCK() + Statement(); + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); + _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); + //restore the local variable stack(remove index,val and ref idx) + CleanStack(stacksize); + END_BREAKBLE_BLOCK(foreachpos - 1); + } + void SwitchStatement() + { + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + Expect(_SC('{')); + SQInteger expr = _fs->TopTarget(); + bool bfirst = true; + SQInteger tonextcondjmp = -1; + SQInteger skipcondjmp = -1; + SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size(); + _fs->_breaktargets.push_back(0); + while(_token == TK_CASE) { + //_fs->AddLineInfos(_lex._currentline, _lineinfo); think about this one + if(!bfirst) { + _fs->AddInstruction(_OP_JMP, 0, 0); + skipcondjmp = _fs->GetCurrentPos(); + _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + } + //condition + Lex(); Expression(); Expect(_SC(':')); + SQInteger trg = _fs->PopTarget(); + _fs->AddInstruction(_OP_EQ, trg, trg, expr); + _fs->AddInstruction(_OP_JZ, trg, 0); + //end condition + if(skipcondjmp != -1) { + _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); + } + tonextcondjmp = _fs->GetCurrentPos(); + SQInteger stacksize = _fs->GetStackSize(); + Statements(); + _fs->SetStackSize(stacksize); + bfirst = false; + } + if(tonextcondjmp != -1) + _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + if(_token == TK_DEFAULT) { + // _fs->AddLineInfos(_lex._currentline, _lineinfo); + Lex(); Expect(_SC(':')); + SQInteger stacksize = _fs->GetStackSize(); + Statements(); + _fs->SetStackSize(stacksize); + } + Expect(_SC('}')); + _fs->PopTarget(); + __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; + if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__); + _fs->_breaktargets.pop_back(); + + } + void FunctionStatement() + { + SQObject id; + Lex(); id = Expect(TK_IDENTIFIER); + _fs->PushTarget(0); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); + + while(_token == TK_DOUBLE_COLON) { + Lex(); + id = Expect(TK_IDENTIFIER); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); + } + Expect(_SC('(')); + CreateFunction(id); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + EmitDerefOp(_OP_NEWSLOT); + _fs->PopTarget(); + } + void ClassStatement() + { + ExpState es; + Lex(); PushExpState(); + _exst._class_or_delete = true; + _exst._funcarg = false; + PrefixedExpr(); + es = PopExpState(); + if(es._deref == DEREF_NO_DEREF) Error(_SC("invalid class name")); + if(es._deref == DEREF_FIELD) { + ClassExp(); + EmitDerefOp(_OP_NEWSLOT); + _fs->PopTarget(); + } + else Error(_SC("cannot create a class in a local with the syntax(class )")); + } + void TryCatchStatement() + { + SQObject exid; + Lex(); + _fs->AddInstruction(_OP_PUSHTRAP,0,0); + _fs->_traps++; + if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++; + if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++; + SQInteger trappos = _fs->GetCurrentPos(); + Statement(); + _fs->_traps--; + _fs->AddInstruction(_OP_POPTRAP, 1, 0); + if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--; + if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--; + _fs->AddInstruction(_OP_JMP, 0, 0); + SQInteger jmppos = _fs->GetCurrentPos(); + _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); + Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')')); + SQInteger stacksize = _fs->GetStackSize(); + SQInteger ex_target = _fs->PushLocalVariable(exid); + _fs->SetIntructionParam(trappos, 0, ex_target); + Statement(); + _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); + CleanStack(stacksize); + } + void FunctionExp(SQInteger ftype) + { + Lex(); Expect(_SC('(')); + CreateFunction(_null_); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1); + } + void ClassExp() + { + SQInteger base = -1; + SQInteger attrs = -1; + if(_token == TK_EXTENDS) { + Lex(); Expression(); + base = _fs->TopTarget(); + } + if(_token == TK_ATTR_OPEN) { + Lex(); + _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); + ParseTableOrClass(_SC(','),TK_ATTR_CLOSE); + attrs = _fs->TopTarget(); + } + Expect(_SC('{')); + if(attrs != -1) _fs->PopTarget(); + if(base != -1) _fs->PopTarget(); + _fs->AddInstruction(_OP_CLASS, _fs->PushTarget(), base, attrs); + ParseTableOrClass(_SC(';')); + } + void DelegateExpr() + { + Lex(); CommaExpr(); + Expect(_SC(':')); + CommaExpr(); + SQInteger table = _fs->PopTarget(), delegate = _fs->PopTarget(); + _fs->AddInstruction(_OP_DELEGATE, _fs->PushTarget(), table, delegate); + } + void DeleteExpr() + { + ExpState es; + Lex(); PushExpState(); + _exst._class_or_delete = true; + _exst._funcarg = false; + PrefixedExpr(); + es = PopExpState(); + if(es._deref == DEREF_NO_DEREF) Error(_SC("can't delete an expression")); + if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE); + else Error(_SC("cannot delete a local")); + } + void PrefixIncDec(SQInteger token) + { + ExpState es; + Lex(); PushExpState(); + _exst._class_or_delete = true; + _exst._funcarg = false; + PrefixedExpr(); + es = PopExpState(); + if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_INC,token == TK_MINUSMINUS?-1:1); + else { + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(_OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS?-1:1); + } + } + void CreateFunction(SQObject &name) + { + + SQFuncState *funcstate = _fs->PushChildState(_ss(_vm)); + funcstate->_name = name; + SQObject paramname; + funcstate->AddParameter(_fs->CreateString(_SC("this"))); + funcstate->_sourcename = _sourcename; + while(_token!=_SC(')')) { + if(_token == TK_VARPARAMS) { + funcstate->_varparams = true; + Lex(); + if(_token != _SC(')')) Error(_SC("expected ')'")); + break; + } + else { + paramname = Expect(TK_IDENTIFIER); + funcstate->AddParameter(paramname); + if(_token == _SC(',')) Lex(); + else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); + } + } + Expect(_SC(')')); + //outer values + if(_token == _SC(':')) { + Lex(); Expect(_SC('(')); + while(_token != _SC(')')) { + paramname = Expect(TK_IDENTIFIER); + //outers are treated as implicit local variables + funcstate->AddOuterValue(paramname); + if(_token == _SC(',')) Lex(); + else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); + } + Lex(); + } + + SQFuncState *currchunk = _fs; + _fs = funcstate; + Statement(); + funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true); + funcstate->AddInstruction(_OP_RETURN, -1); + funcstate->SetStackSize(0); + //_fs->->_stacksize = _fs->_stacksize; + SQFunctionProto *func = funcstate->BuildProto(); +#ifdef _DEBUG_DUMP + funcstate->Dump(func); +#endif + _fs = currchunk; + _fs->_functions.push_back(func); + _fs->PopChildState(); + } + void CleanStack(SQInteger stacksize) + { + if(_fs->GetStackSize() != stacksize) + _fs->SetStackSize(stacksize); + } + void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve) + { + while(ntoresolve > 0) { + SQInteger pos = funcstate->_unresolvedbreaks.back(); + funcstate->_unresolvedbreaks.pop_back(); + //set the jmp instruction + funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); + ntoresolve--; + } + } + void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos) + { + while(ntoresolve > 0) { + SQInteger pos = funcstate->_unresolvedcontinues.back(); + funcstate->_unresolvedcontinues.pop_back(); + //set the jmp instruction + funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0); + ntoresolve--; + } + } +private: + SQInteger _token; + SQFuncState *_fs; + SQObjectPtr _sourcename; + SQLexer _lex; + bool _lineinfo; + bool _raiseerror; + SQInteger _debugline; + SQInteger _debugop; + ExpStateVec _expstates; + SQChar *compilererror; + jmp_buf _errorjmp; + SQVM *_vm; +}; + +bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo) +{ + SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo); + return p.Compile(out); +}