1220 lines
35 KiB
C++
1220 lines
35 KiB
C++
|
/*
|
||
|
see copyright notice in squirrel.h
|
||
|
*/
|
||
|
#include "sqpcheader.h"
|
||
|
#include <stdarg.h>
|
||
|
#include <setjmp.h>
|
||
|
#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<ExpState> 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:
|
||
|
if(sizeof(SQFloat) == sizeof(SQInt32)) {
|
||
|
_fs->AddInstruction(_OP_LOADFLOAT, _fs->PushTarget(),*((SQInt32 *)&_lex._fvalue));
|
||
|
}
|
||
|
else {
|
||
|
_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(); //<<BECAUSE OF THIS NO COMMON EMIT FUNC IS POSSIBLE
|
||
|
_fs->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 <local>)"));
|
||
|
}
|
||
|
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);
|
||
|
}
|