/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef NO_COMPILER #include #include #include "sqopcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqcompiler.h" #include "sqfuncstate.h" #include "sqlexer.h" #include "sqvm.h" #include "sqtable.h" enum { eFunctionType_global = 1, eFunctionType_local, eFunctionType_lambda, eFunctionType_member, eFunctionType_anonymous }; #define EXPR 1 #define OBJECT 2 #define BASE 3 #define LOCAL 4 #define OUTER 5 #define EXPR_STATEMENT 6 //we start from statement state struct SQExpState { SQInteger etype; /* expr. type; one of EXPR, OBJECT, BASE, OUTER, LOCAL or EXPR_STATEMENT */ SQInteger epos; /* expr. location on stack; -1 for OBJECT and BASE */ bool donot_get; /* signal not to deref the next value */ }; #define MAX_COMPILER_ERROR_LEN 256 struct SQScope { SQInt32 stacksize; SQInt16 outers; SQInt16 nested; }; #define BEGIN_SCOPE() SQScope __oldscope__ = _scope; \ ++_scope.nested; \ _scope.outers = _fs->_outers; \ _scope.stacksize = _fs->GetStackSize();\ _scope_consts.push_back(SQTable::Create(_ss(_vm),0)); #define RESOLVE_OUTERS() if(_fs->GetStackSize() != _scope.stacksize) { \ if(_fs->CountOuters(_scope.stacksize)) { \ _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ } \ } #define END_SCOPE_NO_CLOSE() { if(_fs->GetStackSize() != _scope.stacksize) { \ _fs->SetStackSize(_scope.stacksize); \ } \ _scope = __oldscope__; \ _scope_consts.pop_back();\ } #define END_SCOPE() { SQInteger oldouters = _fs->_outers;\ if(_fs->GetStackSize() != _scope.stacksize) { \ _fs->SetStackSize(_scope.stacksize); \ if(oldouters != _fs->_outers) { \ _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ } \ } \ _scope = __oldscope__; \ } #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();} #define CASE_TK_LOCAL_CHAR_TYPES \ case TK_LOCAL_STRING_T: \ case TK_LOCAL_CHAR_T: \ case TK_LOCAL_WCHAR_T #define CASE_TK_LOCAL_INT_TYPES \ case TK_LOCAL_INT8_T: \ case TK_LOCAL_INT16_T: \ case TK_LOCAL_INT32_T: \ case TK_LOCAL_INT64_T: \ case TK_LOCAL_INT_T: \ case TK_LOCAL_UINT8_T: \ case TK_LOCAL_UINT16_T: \ case TK_LOCAL_UINT32_T: \ case TK_LOCAL_UINT64_T: \ case TK_LOCAL_UINT_T: \ case TK_LOCAL_SIZE_T: \ case TK_LOCAL_SSIZE_T #define CASE_TK_LOCAL_FLOAT_TYPES \ case TK_LOCAL_FLOAT_T: \ case TK_LOCAL_DOUBLE_T: \ case TK_LOCAL_LONG_DOUBLE_T #define CASE_TK_NUMBER_TYPES \ CASE_TK_LOCAL_FLOAT_TYPES: \ CASE_TK_LOCAL_INT_TYPES: \ case TK_LOCAL_NUMBER_T #define CASE_TK_LOCAL_TYPES \ CASE_TK_LOCAL_CHAR_TYPES: \ CASE_TK_NUMBER_TYPES: \ case TK_LOCAL_BOOL_T: \ case TK_LOCAL_TABLE_T: \ case TK_LOCAL_ANY_T: \ case TK_LOCAL_ARRAY_T: \ case TK_VOID: \ case TK_LOCAL_VOIDPTR_T: \ case TK_LOCAL_WEAKREF_T static SQInteger compilerReadFunc(SQUserPointer fp) { SQInteger c = fgetc((FILE *)fp); if(c == EOF) { return 0; } return c; } class SQCompiler { public: SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo, bool show_warnings, SQInteger max_nested_includes) { _vm=v; _lex.Init(_ss(v), rg, up,ThrowError,this); _sourcename = SQString::Create(_ss(v), sourcename); _lineinfo = lineinfo;_raiseerror = raiseerror; _show_warnings = show_warnings; _scope.outers = 0; _scope.stacksize = 0; _scope.nested = 0; _compilererror = NULL; _globals = SQTable::Create(_ss(_vm),0); _type_names = SQTable::Create(_ss(_vm),0); _extern_names = SQTable::Create(_ss(_vm),0); _max_nested_includes = max_nested_includes; _nested_includes_count = 0; _is_parsing_extern = false; } ~SQCompiler(){ _table(_globals)->Finalize(); _globals.Null(); } static void ThrowError(void *ud, const SQChar *s) { SQCompiler *c = (SQCompiler *)ud; c->Error(s); } void Error(const SQChar *s, ...) //__attribute__((format(printf, 2, 3))) { va_list vl; va_start(vl, s); scvsprintf(error_buf, sizeof(error_buf), s, vl); va_end(vl); _compilererror = error_buf; longjmp(_errorjmp,1); } void Warning(const SQChar *s, ...) //__attribute__((format(printf, 2, 3))) { if(!_show_warnings) return; va_list vl; va_start(vl, s); scvfprintf(stderr, s, vl); va_end(vl); } bool CheckNameIsType(const SQObject &name){ return _table(_type_names)->Exists(name); } bool CheckTypeName(const SQObject &name, bool addIfNotExists=false){ bool found = _table(_type_names)->Exists(name); if(addIfNotExists && !found) { SQObjectPtr oname = name, otrue = true; _table(_type_names)->NewSlot(oname, otrue); } return found; } bool TypesGet(const SQObjectPtr &key,SQObjectPtr &val){ return _table(_type_names)->Get(key,val); } bool TypesNewSlot(const SQObjectPtr &key, const SQObjectPtr &val){ return _table(_type_names)->NewSlot(key,val); } int CheckExternName(const SQObject &name, bool addIfNotExists=false){ SQObjectPtr orefcount; int found = _table(_extern_names)->Get(name, orefcount); if(addIfNotExists && !found) { SQObjectPtr oname = name; _integer(orefcount) = 1; //1 means only declared, 2 declared and assigned _table(_extern_names)->NewSlot(oname, orefcount); _table(_globals)->NewSlot(oname, orefcount); } return found ? _integer(orefcount) : 0; } int ExternNameSetRefCount(const SQObject &name) { int ref_count = CheckExternName(name); if(ref_count == 1) { SQObjectPtr orefcount; _integer(orefcount) = ref_count = 2; _table(_extern_names)->Set(name, orefcount); } return ref_count; } void AddGlobalName(const SQObject &name) { SQObjectPtr oname = name, otrue = true; _table(_globals)->NewSlot(oname, otrue); } void CheckGlobalName(const SQObject &name, bool addIfNotExists=false, bool checkLocals=true){ const SQChar *found = NULL; if(CheckExternName(name)){ found = _SC("extern"); } else if(_table(_globals)->Exists(name)){ found = _SC("global"); } if(found) { if(checkLocals) Error(_SC("%s '%s' already declared"), found, _stringval(name)); else Warning(_SC("%s:%d:%d warning %s '%s' already declared will be shadowed\n"), _stringval(_sourcename), _lex.data->currentline, _lex.data->currentcolumn, found, _stringval(name)); } else if(checkLocals) CheckLocalNameScope(name, -1, false); if(addIfNotExists) { AddGlobalName(name); } } void CheckLocalNameScope(const SQObject &name, SQInteger scope, bool checkGlobals=true){ SQInteger found = _fs->GetLocalVariable(name); if(found >= 0){ SQLocalVarInfo &lvi = _fs->_vlocals[found]; if(lvi._scope == scope) Error(_SC("local '%s' already declared"), _stringval(name)); else Warning(_SC("%s:%d:%d warning local '%s' already declared will be shadowed\n"), _stringval(_sourcename), _lex.data->currentline, _lex.data->currentcolumn, _stringval(name)); } else { found = _fs->FindOuterVariable(name); if(found >= 0) Warning(_SC("%s:%d:%d warning outer variable '%s' already declared will be shadowed\n"), _stringval(_sourcename), _lex.data->currentline, _lex.data->currentcolumn, _stringval(name)); } if(checkGlobals) CheckGlobalName(name, false, false); SQObjectPtr strongid = name; CheckConstsExists(strongid); } bool IsConstant(const SQObject &name,SQObject &e){ SQObjectPtr val; for(int i=_scope.nested-1; i >= 0; --i){ if(_table(_scope_consts[i])->Get(name,val)) { e = val; return true; } } if(_table(_ss(_vm)->_consts)->Get(name,val)) { e = val; return true; } return false; } SQInteger ExpressionConstant(SQObject id) { SQObject constant; SQInteger epos; if(IsConstant(id, constant)) { /* Handle named constant */ SQObjectPtr constval; SQObject constid; if(type(constant) == OT_TABLE) { Expect('.'); constid = Expect(TK_IDENTIFIER); if(!_table(constant)->Get(constid, constval)) { constval.Null(); Error(_SC("invalid constant [%s.%s]"), _stringval(id), _stringval(constid)); } } else { constval = constant; } epos = _fs->PushTarget(); /* generate direct or literal function depending on size */ SQObjectType ctype = type(constval); switch(ctype) { case OT_INTEGER: EmitLoadConstInt(_integer(constval),epos); break; case OT_FLOAT: EmitLoadConstFloat(_float(constval),epos); break; default: _fs->AddInstruction(_OP_LOAD,epos,_fs->GetConstant(constval)); break; } } else { Error(_SC("invalid constant [%s]"), _stringval(id)); } return epos; } void CheckConstsExists(const SQObjectPtr &key){ int found = -1; for(int i=_scope.nested-1; i >= 0; --i){ if(_table(_scope_consts[i])->Exists(key)) { found = i+1; break; } } if(found < 0 && _table(_ss(_vm)->_consts)->Exists(key)) found = 0; if(found == _scope.nested) { Error(_SC("constant '%s' already exists\n"), _stringval(key)); } if(found >= 0) Warning(_SC("%s:%d:%d warning an already defined constant '%s' will be shadowed\n"), _stringval(_sourcename), _lex.data->currentline, _lex.data->currentcolumn, _stringval(key)); } bool ConstsGet(const SQObjectPtr &key,SQObjectPtr &val){ for(int i=_scope.nested-1; i >= 0; --i){ if(_table(_scope_consts[i])->Get(key,val)) return true; } return _table(_ss(_vm)->_consts)->Get(key,val); } bool ConstsNewSlot(const SQObjectPtr &key, const SQObjectPtr &val){ if(_scope.nested) return _table(_scope_consts[_scope.nested-1])->NewSlot(key,val); return _table(_ss(_vm)->_consts)->NewSlot(key,val); } void Lex() { _token = _lex.Lex(); } SQObjectPtr GetTokenObject(SQInteger tok, bool doLex=true) { SQObjectPtr ret; switch(tok) { case TK_IDENTIFIER: ret = _fs->CreateString(_lex.data->svalue); break; case TK_STRING_LITERAL: ret = _fs->CreateString(_lex.data->svalue,_lex.data->longstr.size()-1); break; case TK_INTEGER: ret = SQObjectPtr(_lex.data->nvalue); break; case TK_FLOAT: ret = SQObjectPtr(_lex.data->fvalue); break; default: ret = _fs->CreateString(_lex.GetTokenName(_token)); } if(doLex) Lex(); return ret; } void ErrorIfNotToken(SQInteger tok){ if(_token != tok) { if(((_token == TK_CONSTRUCTOR) || (_token == TK_DESTRUCTOR)) && tok == TK_IDENTIFIER) { //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); } } } SQObject Expect(SQInteger tok, bool doLex=true) { ErrorIfNotToken(tok); return GetTokenObject(tok, doLex); } SQObject ExpectTypeToken() { switch(_token) { CASE_TK_LOCAL_TYPES: return GetTokenObject(_token); break; case TK_IDENTIFIER: break; default: ErrorIfNotToken(TK_IDENTIFIER); } return GetTokenObject(TK_IDENTIFIER); } bool IsEndOfStatement() { return ((_lex.data->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) found (%d)"), _token); } } void MoveIfCurrentTargetIsLocal() { SQInteger trg = _fs->TopTarget(); if(_fs->IsLocal(trg)) { trg = _fs->PopTarget(); //pops the target and moves it _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); } } void Pragma() { int line = _lex.data->currentline; //int column = _lex.data->currentcolumn; Lex(); SQObject id = Expect(TK_IDENTIFIER); if(scstrcmp(_stringval(id), _SC("include")) == 0) { #if SQ_ENABLE_INCLUDES SQInteger nested_count = _nested_includes_count + 1; if((_max_nested_includes <= 0) || (nested_count > _max_nested_includes)) { Error(_SC("Error: too many nested includes %d %s\n"), nested_count, _stringval(id)); } //do not call Lex() since next token can try search for id defined in the include //and then go global instead of local, we'll call it after including id = Expect(TK_STRING_LITERAL, false); //Warning(_SC("%s:%d:%d warning pragma include %s\n"), // _stringval(_sourcename), line, column, _stringval(id)); FILE *fp = scfopen(_stringval(id), "r"); if(fp != NULL) { //increment nested count ++_nested_includes_count; //save current source file and lex state SQUserPointer saved_up = _lex._up; //current userpointer SQLEXREADFUNC saved_readf = _lex._readf; //current readfunction SQInteger saved_line = _lex.data->currentline; SQInteger saved_column = _lex.data->currentcolumn; SQInteger saved_curdata = _lex.data->currdata; SQInteger saved_prevtoken = _lex.data->prevtoken; SQInteger saved_token = _token; SQObjectPtr saved_source_name = _sourcename; //set new source file _fs->_sourcename = id; _sourcename = id; _lex.ResetReader(compilerReadFunc, fp, 1); //compile the include file Lex(); while(_token > 0){ Statement(); if(_lex.data->prevtoken != _SC('}') && _lex.data->prevtoken != _SC(';')) OptionalSemicolon(); } //close file fclose(fp); //restore saved source file and lex state _fs->_sourcename = saved_source_name; _sourcename = saved_source_name; _token = saved_token; _lex.data->currdata = saved_curdata; _lex.data->prevtoken = saved_prevtoken; _lex.data->currentcolumn = saved_column; _lex.data->currentline = saved_line; _lex._readf = saved_readf; _lex._up = saved_up; --_nested_includes_count; //done let's continue working //Now we do the Lex() call skiped before compile the include file Lex(); } else { Error(_SC("Error: opening include file %s\n"), _stringval(id)); } #else Error(_SC("Error: includes are not enabled\n")); #endif } else { _lex.data->currentline = line; Error(_SC("Error: unknown pragma %s\n"), _stringval(id)); } } 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")), _scope.nested+1); _fs->AddParameter(_fs->CreateString(_SC("vargv")), _scope.nested+1); _fs->_varparams = true; _fs->_sourcename = _sourcename; SQInteger stacksize = _fs->GetStackSize(); if(setjmp(_errorjmp) == 0) { Lex(); while(_token > 0){ Statement(); if(_lex.data->prevtoken != _SC('}') && _lex.data->prevtoken != _SC(';')) OptionalSemicolon(); } _fs->SetStackSize(stacksize); _fs->AddLineInfos(_lex.data->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.data->currentline, _lex.data->currentcolumn); } _vm->_lasterror = SQString::Create(_ss(_vm), _compilererror, -1); _vm->_lasterror_line = _lex.data->currentline; _vm->_lasterror_column = _lex.data->currentcolumn; return false; } return true; } void Statements() { while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) { Statement(); if(_lex.data->prevtoken != _SC('}') && _lex.data->prevtoken != _SC(';')) OptionalSemicolon(); } } void Statement(bool closeframe = true) { _es.etype = EXPR_STATEMENT; SQObject id; _fs->AddLineInfos(_lex.data->currentline, _lineinfo); start_again: 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: //case TK_CONST: CASE_TK_LOCAL_TYPES: 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(true); if(op == _OP_RETURN && _fs->_traps > 0) _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); _fs->_returnexp = retexp; _fs->AddInstruction(op, 1, _fs->PopTarget(),_fs->GetStackSize()); } else{ if(op == _OP_RETURN && _fs->_traps > 0) _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0); _fs->_returnexp = -1; _fs->AddInstruction(op, 0xFF,0,_fs->GetStackSize()); } break;} case TK_GOTO: { //error if outside any function if(!_fs->_parent) Error(_SC("'goto' has to be in a function block")); Lex(); //ignore for now id = Expect(TK_IDENTIFIER); SQGotoLabelsInfo info; info.name = id; info.line = _lex.data->currentline; //need to get line number here info.traps = _fs->_traps; info.nested = _scope.nested; //Expect(_SC(';')); if(info.traps) _fs->AddInstruction(_OP_NOP, 0, 0); //for _OP_POPTRAP RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); //instruction pos info.pos = _fs->GetCurrentPos(); _fs->_unresolvedgotos.push_back(info); } 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); } RESOLVE_OUTERS(); _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); } RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); Lex(); break; case TK_FUNCTION: FunctionStatement(); break; case TK_CLASS: case TK_STRUCT: ClassStatement(); break; case TK_ENUM: EnumStatement(); break; case _SC('{'):{ BEGIN_SCOPE(); Lex(); Statements(); Expect(_SC('}')); if(closeframe) { END_SCOPE(); } else { END_SCOPE_NO_CLOSE(); } } break; case TK_TRY: TryCatchStatement(); break; case TK_THROW: Lex(); CommaExpr(); _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); break; case TK_CONST: { Lex(); if(_token == TK_IDENTIFIER) { id = _fs->CreateString(_lex.data->svalue); if(CheckTypeName(id)) //C/C++ type declaration; { goto start_again; } Lex(); } else id = Expect(TK_IDENTIFIER); if(_token == _SC(':')) { //type specifier like typescript Lex(); ExpectTypeToken(); //ignore for now } Expect('='); SQObjectPtr strongid = id; CheckLocalNameScope(id, _scope.nested); //CheckConstsExists(strongid); SQObject val = ExpectScalar(); OptionalSemicolon(); ConstsNewSlot(strongid,SQObjectPtr(val)); } break; case TK_INLINE: //accept and ignore case TK_CONSTEXPR: //accept and ignore case TK_FRIEND: case TK_VOLATILE: Lex(); goto start_again; case TK_STATIC: if(_scope.nested) { Warning(_SC("%s:%d:%d warning static cualifier is ignored\n"), _stringval(_sourcename), _lex.data->currentline, _lex.data->currentcolumn); } Lex(); //ignore it only to allow run some C/C++ code goto start_again; case TK_DECLARE: //parse as extern case TK_EXTERN: ExternDeclStatement(); break; case TK_TYPEDEF: { Lex(); SQObject type_val = ExpectTypeToken(); id = ExpectTypeToken(); SQObjectPtr strongid = id; CheckLocalNameScope(id, _scope.nested); //Expect(_SC(';')); TypesNewSlot(strongid,SQObjectPtr(type_val)); } break; case TK_USING: { Lex(); id = Expect(TK_IDENTIFIER); Expect('='); SQObjectPtr strongid = id; CheckLocalNameScope(id, _scope.nested); SQObject type_val = ExpectTypeToken(); //Expect(_SC(';')); TypesNewSlot(strongid,SQObjectPtr(type_val)); } break; case TK_PRAGMA: Pragma(); break; case TK_UNSAFE: Lex(); //ignore for now goto start_again; break; case TK_TEMPLATE: TemplateStatement(); break; case TK_IDENTIFIER:{ id = _fs->CreateString(_lex.data->svalue); SQInteger lhtk = _lex.LookaheadLex(); if(lhtk == _SC(':')) { if(!_fs->_parent) Error(_SC("'label' has to be inside a function block")); if(!_fs->AddGotoTarget(id, _lex.data->currentline, _fs->_traps, _scope.nested)) { Error(_SC("Label already declared")); } Lex(); //eat ':' Lex(); break; } if(CheckTypeName(id)) //C/C++ type declaration; { if(lhtk != _SC('.')) { LocalDeclStatement(); break; } } } default: CommaExpr(); _fs->DiscardTarget(); /* //Fagiano says that this is not a bug //and with this modification stack grow by one lement all the time if(_token == TK_IDENTIFIER){ CommaExpr(); if(_token == TK_IDENTIFIER){ Error(_SC(" '=' expected near '%s'"), _lex.data->svalue); } } */ //_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, SQInteger etype, SQInteger pos) { /* Generate code depending on the expression type */ switch(etype) { case LOCAL:{ SQInteger p2 = _fs->PopTarget(); //src in OP_GET SQInteger p1 = _fs->PopTarget(); //key in OP_GET _fs->PushTarget(p1); //EmitCompArithLocal(tok, p1, p1, p2); _fs->AddInstruction(ChooseArithOpByToken(tok),p1, p2, p1, 0); _fs->SnoozeOpt(); //FIX: stop optimizer in retargeting opcode } break; case OBJECT: case BASE: { SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); /* _OP_COMPARITH mixes dest obj and source val in the arg1 */ _fs->AddInstruction(_OP_COMPARITH, _fs->PushTarget(), (src<<16)|val, key, ChooseCompArithCharByToken(tok)); } break; case OUTER: { SQInteger val = _fs->TopTarget(); SQInteger tmp = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp, pos); _fs->AddInstruction(ChooseArithOpByToken(tok), tmp, val, tmp, 0); _fs->PopTarget(); _fs->PopTarget(); _fs->AddInstruction(_OP_SETOUTER, _fs->PushTarget(), pos, tmp); } break; } } void CommaExpr(bool warningAssign=false) { for(Expression(warningAssign);_token == ',';_fs->PopTarget(), Lex(), CommaExpr(warningAssign)); } void ErrorIfConst(){ SQLocalVarInfo &vsrc = _fs->_vlocals[_fs->TopTarget()]; //printf("%d %d %d %d %s\n", __LINE__, vsrc._scope, vsrc._type, vsrc._pos, vsrc._name._unVal.pString ? _stringval(vsrc._name) : "?"); if(vsrc._type & _VAR_CONST) Error(_SC("can't assign to a const variable")); } void Expression(bool warningAssign=false) { SQExpState es = _es; _es.etype = EXPR; _es.epos = -1; _es.donot_get = false; SQObject id; if((_token == TK_IDENTIFIER) && (es.etype == EXPR_STATEMENT)) { id = _fs->CreateString(_lex.data->svalue); } //else id = {}; //old compilers do not allow this else id._type = OT_NULL; //segfault without it with gcc -O3 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 = _es.etype; SQInteger pos = _es.epos; if(ds == EXPR) Error(_SC("can't assign expression")); else if(ds == BASE) Error(_SC("'base' cannot be modified")); Lex(); Expression(); switch(op){ case TK_NEWSLOT: if(ds == OBJECT || ds == BASE) { EmitDerefOp(_OP_NEWSLOT); if((_es.epos == -1) && (es.etype == EXPR_STATEMENT) && (type(id) == OT_STRING) ) AddGlobalName(id); } 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(warningAssign) Warning(_SC("%s:%d:%d warning making assignment, maybe it's not what you want\n"), _stringval(_sourcename), _lex.data->currentline, _lex.data->currentcolumn); switch(ds) { case LOCAL: { SQInteger src = _fs->PopTarget(); SQInteger dst = _fs->TopTarget(); _fs->AddInstruction(_OP_MOVE, dst, src); } break; case OBJECT: case BASE: EmitDerefOp(_OP_SET); break; case OUTER: { SQInteger src = _fs->PopTarget(); SQInteger dst = _fs->PushTarget(); _fs->AddInstruction(_OP_SETOUTER, dst, pos, src); } } break; case TK_MINUSEQ: case TK_PLUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ: EmitCompoundArith(op, ds, pos); 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; } _es = es; } template void INVOKE_EXP(T f) { SQExpState es = _es; _es.etype = EXPR; _es.epos = -1; _es.donot_get = false; (this->*f)(); _es = es; } template void BIN_EXP(SQOpcode op, T f,SQInteger op3 = 0) { Lex(); INVOKE_EXP(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(); INVOKE_EXP(&SQCompiler::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(); INVOKE_EXP(&SQCompiler::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; } 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() { EqExp(); for(;;) if(_token == _SC('&')) {BIN_EXP(_OP_BITW, &SQCompiler::EqExp,BW_AND); }else return; } void EqExp() { CompExp(); for(;;) switch(_token) { case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::CompExp); break; case TK_EQ_IDENTITY :BIN_EXP(_OP_EQI, &SQCompiler::CompExp); break; case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::CompExp); break; case TK_NE_IDENTITY: BIN_EXP(_OP_NEI, &SQCompiler::CompExp); break; case TK_3WAYSCMP: BIN_EXP(_OP_CMP, &SQCompiler::CompExp,CMP_3W); break; default: return; } } void CompExp() { ShiftExp(); for(;;) switch(_token) { 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_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::ShiftExp); break; case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &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; } } SQOpcode ChooseArithOpByToken(SQInteger tok) { switch(tok) { case TK_PLUSEQ: case '+': return _OP_ADD; case TK_MINUSEQ: case '-': return _OP_SUB; case TK_MULEQ: case '*': return _OP_MUL; case TK_DIVEQ: case '/': return _OP_DIV; case TK_MODEQ: case '%': return _OP_MOD; default: assert(0); } return _OP_ADD; } SQInteger ChooseCompArithCharByToken(SQInteger tok) { 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; }; return oper; } void PlusExp() { MultExp(); for(;;) switch(_token) { case _SC('+'): case _SC('-'): BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::MultExp); break; default: return; } } void MultExp() { PrefixedExpr(); for(;;) switch(_token) { case _SC('*'): case _SC('/'): case _SC('%'): BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::PrefixedExpr); 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(); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); if(_es.etype==BASE) { Emit2ArgsOP(_OP_GET); pos = _fs->TopTarget(); _es.etype = EXPR; _es.epos = pos; } else { if(NeedGet()) { Emit2ArgsOP(_OP_GET); } _es.etype = OBJECT; } break; case _SC('['): if(_lex.data->prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration")); Lex(); Expression(); Expect(_SC(']')); pos = -1; if(_es.etype==BASE) { Emit2ArgsOP(_OP_GET); pos = _fs->TopTarget(); _es.etype = EXPR; _es.epos = pos; } else { if(NeedGet()) { Emit2ArgsOP(_OP_GET); } _es.etype = OBJECT; } break; case TK_MINUSMINUS: case TK_PLUSPLUS: { if(IsEndOfStatement()) return; SQInteger diff = (_token==TK_MINUSMINUS) ? -1 : 1; Lex(); switch(_es.etype) { case EXPR: Error(_SC("can't '++' or '--' an expression")); break; case OBJECT: case BASE: if(_es.donot_get == true) { Error(_SC("can't '++' or '--' an expression")); break; } //mmh dor this make sense? Emit2ArgsOP(_OP_PINC, diff); break; case LOCAL: { SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, diff); } break; case OUTER: { SQInteger tmp1 = _fs->PushTarget(); SQInteger tmp2 = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp2, _es.epos); _fs->AddInstruction(_OP_PINCL, tmp1, tmp2, 0, diff); _fs->AddInstruction(_OP_SETOUTER, tmp2, _es.epos, tmp2); _fs->PopTarget(); } } } return; break; case _SC('('): switch(_es.etype) { case OBJECT: { SQInteger key = _fs->PopTarget(); /* location of the key */ SQInteger table = _fs->PopTarget(); /* location of the object */ SQInteger closure = _fs->PushTarget(); /* location for the closure */ SQInteger ttarget = _fs->PushTarget(); /* location for 'this' pointer */ _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); } break; case BASE: //Emit2ArgsOP(_OP_GET); _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); break; case OUTER: _fs->AddInstruction(_OP_GETOUTER, _fs->PushTarget(), _es.epos); _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); break; default: _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); } _es.etype = EXPR; Lex(); FunctionCallArgs(); break; case TK_AS: { Lex(); ExpectTypeToken(); //ignore for now } default: return; } } } SQInteger Factor() { _es.etype = EXPR; switch(_token) { case TK_STRING_LITERAL: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex.data->svalue,_lex.data->longstr.size()-1))); Lex(); break; case TK_BASE: Lex(); _fs->AddInstruction(_OP_GETBASE, _fs->PushTarget()); _es.etype = BASE; _es.epos = _fs->TopTarget(); return (_es.epos); break; case TK_IDENTIFIER: case TK_CONSTRUCTOR: case TK_DESTRUCTOR: case TK_THIS:{ SQObject id; SQObject constant; switch(_token) { case TK_IDENTIFIER: id = _fs->CreateString(_lex.data->svalue); break; case TK_THIS: id = _fs->CreateString(_SC("this"), 4); break; case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor"), 11); break; case TK_DESTRUCTOR: id = _fs->CreateString(_SC("destructor"), 10); break; } SQInteger pos = -1; Lex(); if((pos = _fs->GetLocalVariable(id)) != -1) { /* Handle a local variable (includes 'this') */ _fs->PushTarget(pos); _es.etype = LOCAL; _es.epos = pos; } else if((pos = _fs->GetOuterVariable(id)) != -1) { /* Handle a free var */ if(NeedGet()) { _es.epos = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, _es.epos, pos); /* _es.etype = EXPR; already default value */ } else { _es.etype = OUTER; _es.epos = pos; } } else if(IsConstant(id, constant)) { /* Handle named constant */ SQObjectPtr constval; SQObject constid; if(type(constant) == OT_TABLE) { Expect('.'); constid = Expect(TK_IDENTIFIER); if(!_table(constant)->Get(constid, constval)) { constval.Null(); Error(_SC("invalid constant [%s.%s]"), _stringval(id), _stringval(constid)); } } else { constval = constant; } _es.epos = _fs->PushTarget(); /* generate direct or literal function depending on size */ SQObjectType ctype = type(constval); switch(ctype) { case OT_INTEGER: EmitLoadConstInt(_integer(constval),_es.epos); break; case OT_FLOAT: EmitLoadConstFloat(_float(constval),_es.epos); break; case OT_BOOL: _fs->AddInstruction(_OP_LOADBOOL, _es.epos, _integer(constval)); break; default: _fs->AddInstruction(_OP_LOAD,_es.epos,_fs->GetConstant(constval)); break; } _es.etype = EXPR; } else { /* Handle a non-local variable, aka a field. Push the 'this' pointer on * the virtual stack (always found in offset 0, so no instruction needs to * be generated), and push the key next. Generate an _OP_LOAD instruction * for the latter. If we are not using the variable as a dref expr, generate * the _OP_GET instruction. */ _fs->PushTarget(0); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); if(NeedGet()) { Emit2ArgsOP(_OP_GET); } _es.etype = OBJECT; } return _es.epos; } break; case TK_DOUBLE_COLON: // "::" _fs->AddInstruction(_OP_LOADROOT, _fs->PushTarget()); _es.etype = OBJECT; _token = _SC('.'); /* hack: drop into PrefixExpr, case '.'*/ _es.epos = -1; return _es.epos; break; case TK_NULL: _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); Lex(); break; case TK_INTEGER: EmitLoadConstInt(_lex.data->nvalue,-1); Lex(); break; case TK_FLOAT: EmitLoadConstFloat(_lex.data->fvalue,-1); 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_NEWOBJ, _fs->PushTarget(),0,0,NOT_ARRAY); 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, AAT_STACK); key++; } _fs->SetIntructionParam(apos, 1, key); Lex(); } break; case _SC('{'): _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex();ParseTableOrClass(_SC(','),_SC('}')); break; case TK_FUNCTION: FunctionExp(_token);break; case _SC('@'): FunctionExp(_token,true);break; case TK_STRUCT: case TK_CLASS: Lex(); ClassExp(NULL);break; case _SC('-'): Lex(); switch(_token) { case TK_INTEGER: EmitLoadConstInt(-_lex.data->nvalue,-1); Lex(); break; case TK_FLOAT: EmitLoadConstFloat(-_lex.data->fvalue,-1); Lex(); break; default: UnaryOP(_OP_NEG); } break; case _SC('!'): Lex(); UnaryOP(_OP_NOT); break; case _SC('~'): Lex(); if(_token == TK_INTEGER) { EmitLoadConstInt(~_lex.data->nvalue,-1); Lex(); break; } UnaryOP(_OP_BWNOT); break; case TK_TYPEOF : Lex() ;UnaryOP(_OP_TYPEOF); break; case TK_RESUME : Lex(); UnaryOP(_OP_RESUME); break; case TK_CLONE : Lex(); UnaryOP(_OP_CLONE); break; case TK_RAWCALL: Lex(); Expect('('); FunctionCallArgs(true); break; case TK_MINUSMINUS : case TK_PLUSPLUS :PrefixIncDec(_token); break; case TK_DELETE : DeleteExpr(); break; case _SC('('): Lex(); if(_token == TK_IDENTIFIER) { //check C/C++ cast SQObject id = _fs->CreateString(_lex.data->svalue); if(CheckTypeName(id)) //C/C++ type declaration; { SQInteger lhtk = _lex.LookaheadLex(); if(lhtk == _SC(')')) { Lex(); //eat TK_IDENTIFIER Lex(); //eat ')' CommaExpr(); break; } } } CommaExpr(); Expect(_SC(')')); break; case TK___LINE__: EmitLoadConstInt(_lex.data->currentline,-1); Lex(); break; case TK___FILE__: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_sourcename)); Lex(); break; case TK___FUNCTION__: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->_name)); Lex(); break; case TK_IGNORE: //Warning("Keyword ignored \"%s\" at line %d:%d\n", _lex.Tok2Str(_token), // _lex.data->currentline, _lex.data->currentcolumn); Lex(); Factor(); break; default: Error(_SC("expression expected")); } return -1; } void EmitLoadConstInt(SQInteger value,SQInteger target) { if(target < 0) { target = _fs->PushTarget(); } //with the line bellow we get wrong result for -1 //if(value <= INT_MAX && value > INT_MIN) { //does it fit in 32 bits? if((value & (~((SQInteger)0xFFFFFFFF))) == 0) { //does it fit in 32 bits? _fs->AddInstruction(_OP_LOADINT, target,value); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); } } void EmitLoadConstFloat(SQFloat value,SQInteger target) { if(target < 0) { target = _fs->PushTarget(); } if(sizeof(SQFloat) == sizeof(SQInt32)) { _fs->AddInstruction(_OP_LOADFLOAT, target,*((SQInt32 *)&value)); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); } } void UnaryOP(SQOpcode op) { PrefixedExpr(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), src); } bool NeedGet() { switch(_token) { case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: return false; case TK_PLUSPLUS: case TK_MINUSMINUS: if (!IsEndOfStatement()) { return false; } break; } return (!_es.donot_get || ( _es.donot_get && (_token == _SC('.') || _token == _SC('[')))); } void FunctionCallArgs(bool rawcall = false) { SQInteger nargs = 1;//this while(_token != _SC(')')) { Expression(); MoveIfCurrentTargetIsLocal(); nargs++; if(_token == _SC(',')){ Lex(); if(_token == ')') Error(_SC("expression expected, found ')'")); } } Lex(); if (rawcall) { if (nargs < 3) Error(_SC("rawcall requires at least 2 parameters (callee and this)")); nargs -= 2; //removes callee and this from count } 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 AddClassMemberExists(SQObjectPtr &member_names, SQObject &name) { SQObjectPtr oname = name, otrue = true; _table(member_names)->NewSlot(oname, otrue); } void CheckClassMemberExists(SQObjectPtr &member_names, SQObject &name, bool addIfNotExists=true) { if(_table(member_names)->Exists(name)) { Error(_SC("class already has a member named: %s"), _stringval(name)); } if(addIfNotExists) AddClassMemberExists(member_names, name); } void CheckClassMemberExists(SQObjectPtr &member_names, const SQChar *name) { SQObject oname = _fs->CreateString(name); CheckClassMemberExists(member_names, oname); } void ParseTableOrClass(SQInteger separator,SQInteger terminator, SQObjectPtr *class_name=NULL) { SQObjectPtr member_names; SQInteger saved_tok, tpos = _fs->GetCurrentPos(),nkeys = 0; SQObject type_name, obj_id; bool isClass = separator == ';'; //hack recognizes a table/class from the separator if(isClass) { member_names = SQTable::Create(_ss(_vm),0); } int addClassMember = 0; while(_token != terminator) { bool hasattrs = false; bool isstatic = false; bool cppDestructor = false; //bool isvirtual = false; //bool isprivate = false; const SQChar *membertypename = 0; //check if is an attribute if(isClass) { if(_token == TK_ATTR_OPEN) { _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex(); ParseTableOrClass(',',TK_ATTR_CLOSE); hasattrs = true; } if(_token == TK_STATIC) { isstatic = true; Lex(); } if(_token == TK_VIRTUAL) { //isvirtual = true; Lex(); } else if(_token == TK_PUBLIC) { Lex(); } else if(_token == TK_PRIVATE) { //isprivate = true; Lex(); } else if(_token == TK_INLINE) { Lex(); } else if(_token == TK_CONST) { Lex(); } else if(_token == TK_TEMPLATE) { TemplateStatement(); } } member_has_type: switch(_token) { case TK_FUNCTION: case TK_CONSTRUCTOR: case TK_DESTRUCTOR:{ saved_tok = _token; Lex(); obj_id = saved_tok == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(saved_tok == TK_CONSTRUCTOR ? _SC("constructor") : _SC("destructor")); CheckClassMemberExists(member_names, obj_id); Expect(_SC('(')); function_params_decl: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(obj_id)); CreateFunction(obj_id, eFunctionType_member); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); } break; case _SC('['): Lex(); CommaExpr(); Expect(_SC(']')); Expect(_SC('=')); Expression(); break; case TK_STRING_LITERAL: //JSON if(isClass) { Error(_SC("unexpected string literal in class declaration")); } case TK_IDENTIFIER: {//JSON obj_id = GetTokenObject(_token); SQInteger next_token = _SC('='); if(isClass) { CheckClassMemberExists(member_names, obj_id, false); addClassMember = 0; switch(_token) { case _SC('('): //C/C++ style function declaration Lex(); if(class_name) { //printf("ClassMember %d : %s : %s\n", (int)cppDestructor, _stringval(*class_name), _stringval(obj_id)); if(memcmp(_stringval(*class_name), _stringval(obj_id), _string(*class_name)->_len) == 0) { //C++ style constructor/destructor declaration obj_id = _fs->CreateString(cppDestructor ? _SC("destructor") : _SC("constructor")); cppDestructor = false; } } AddClassMemberExists(member_names, obj_id); goto function_params_decl; break; case _SC(':'): //typescript field with type annotation if(membertypename) { Error(_SC("member type already declared before %s"), _stringval(obj_id)); } Lex(); type_name = ExpectTypeToken(); addClassMember = 1; break; case _SC(','): ++addClassMember; case _SC(';'): //member variable declaration without explicit initialization Lex(); ++addClassMember; break; case TK_IDENTIFIER: //if 2 identifier found the first should be a type if(CheckNameIsType(obj_id)) //Struct/Class/Typedef names { membertypename = _stringval(obj_id); goto member_has_type; } } if(addClassMember) { AddClassMemberExists(member_names, obj_id); if(_token != _SC('=')) { _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(obj_id)); _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(), 1); break; } } } if(_token == _SC(':')){ next_token = _token; } Expect(next_token); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(obj_id)); Expression(); break; } CASE_TK_LOCAL_TYPES: //class member variables; case TK_LOCAL: if(isClass) { membertypename = _lex.GetTokenName(_token); Lex(); goto member_has_type; } case _SC('~'): //C++ style destructor declaration if(isClass) { cppDestructor = true; Lex(); goto member_has_type; } case TK_CONSTEXPR: if(isClass) { Lex(); continue; } //else fallthrough default : ErrorIfNotToken(TK_IDENTIFIER); } 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); (void)attrs; // UNUSED 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); //this for classes only as it invokes _newmember if(addClassMember > 1) { addClassMember = 0; goto member_has_type; } } else { _fs->AddInstruction(_OP_NEWSLOT, 0xFF, table, key, val); } } if(separator == _SC(',')) //hack recognizes a table from the separator _fs->SetIntructionParam(tpos, 1, nkeys); Lex(); } void ExternDeclStatement() { _is_parsing_extern = true; Lex(); LocalDeclStatement(); _is_parsing_extern = false; } #define CHECK_REF_DECLARATION(tk) if(tk == _SC('&')){is_reference_declaration = true;Lex();} void LocalDeclStatement() { SQObject varname; //SQChar *param_type_name; bool is_void_declaration = _token == TK_VOID; bool is_const_declaration = _token == TK_CONST; bool is_reference_declaration = false; //bool is_instance_declaration = _token == TK_IDENTIFIER; SQInteger declType = _token; Lex(); if( _token == TK_FUNCTION) { Lex(); varname = Expect(TK_IDENTIFIER); CheckLocalNameScope(varname, _scope.nested); Expect(_SC('(')); function_params_decl: #if 1 //doing this way works but prevents garbage collection when doing multiple reloads on the same vm //the following is an attempt to allow local declared functions be called recursivelly SQInteger old_pos = _fs->GetCurrentPos(); //save current instructions position _fs->PushLocalVariable(varname, _scope.nested, _VAR_CLOSURE); //add function name to find it as outer var if needed //-1 to compensate default parameters when relocating CreateFunction(varname, eFunctionType_local, -1); if(_is_parsing_extern) { if(_token == _SC(';')) //to parse thinscript { //Expect(_SC(';')); CheckExternName(varname, true); return; } else _is_parsing_extern = false; } _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); //rellocate any stack operation (default parameters & _OP_Closure) for(int i=old_pos+1, curr_pos = _fs->GetCurrentPos(); i <= curr_pos; ++i){ SQInstruction & inst = _fs->GetInstruction(i); _fs->SetIntructionParam(i, 0, inst._arg0 -1); } _fs->PopTarget(); #else CreateFunction(varname,eFunctionType::local); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); _fs->PopTarget(); _fs->PushLocalVariable(varname, _scope.nested, _VAR_CLOSURE); #endif return; } do { //param_type_name = NULL; CHECK_REF_DECLARATION(_token); varname = Expect(TK_IDENTIFIER); if(CheckNameIsType(varname)) { //ignore custom types for now //param_type_name = _stringval(varname); declType = _VAR_ANY; CHECK_REF_DECLARATION(_token); varname = Expect(TK_IDENTIFIER); } CheckLocalNameScope(varname, _scope.nested); if(_token == _SC('(')) { //C/C++ style function declaration Lex(); goto function_params_decl; } else if(_token == _SC(':')) { //type specifier like typescript Lex(); ExpectTypeToken(); //ignore for now } if(is_void_declaration) { Error(_SC("void type is invalid here")); } else if(_token == _SC('=')) { if(_is_parsing_extern) { Error(_SC("can not make assignment in external declarations")); } Lex(); Expression(); SQInteger src = _fs->PopTarget(); SQInteger dest = _fs->PushTarget(); if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src); declType = _VAR_ANY; } else if(is_const_declaration || is_reference_declaration) Error(_SC("const/reference '%s' need an initializer"), _stringval(varname)); else { //check_param_type: if(!_is_parsing_extern) { SQInteger dest = _fs->PushTarget(); switch(declType){ CASE_TK_LOCAL_CHAR_TYPES: _fs->AddInstruction(_OP_LOADNULLS, dest,1); declType = _VAR_STRING; break; case TK_LOCAL_BOOL_T: //default value false _fs->AddInstruction(_OP_LOADBOOL, dest,0); declType = _VAR_BOOL; break; case TK_LOCAL_TABLE_T: _fs->AddInstruction(_OP_LOADNULLS, dest,1); declType = _VAR_TABLE; break; case TK_LOCAL_ARRAY_T: _fs->AddInstruction(_OP_LOADNULLS, dest,1); declType = _VAR_ARRAY; break; CASE_TK_LOCAL_INT_TYPES: //default value 0 _fs->AddInstruction(_OP_LOADINT, dest,0); declType = _VAR_INTEGER; break; CASE_TK_LOCAL_FLOAT_TYPES: case TK_LOCAL_NUMBER_T: //start numbers as floats //default value 0.0 //_OP_LOADFLOAT is only valid when SQFloat size == SQInt32 size _fs->AddInstruction(_OP_LOADINT, dest,0); declType = _VAR_FLOAT; break; //case TK_LOCAL: default: //default value null _fs->AddInstruction(_OP_LOADNULLS, dest,1); declType = _VAR_ANY; } } } if(_is_parsing_extern) { if(CheckExternName(varname, true)) { Error(_SC("extern %s already declared"), varname); } } else { _fs->PopTarget(); _fs->PushLocalVariable(varname, _scope.nested, (is_const_declaration ? _VAR_CONST : declType) | (is_reference_declaration ? _VAR_REFERENCE : 0)); } if(_token == _SC(',')) Lex(); else break; } while(1); } void IfBlock() { if (_token == _SC('{')) { BEGIN_SCOPE(); Lex(); Statements(); Expect(_SC('}')); if (true) { END_SCOPE(); } else { END_SCOPE_NO_CLOSE(); } } else { //BEGIN_SCOPE(); Statement(); if (_lex.data->prevtoken != _SC('}') && _lex.data->prevtoken != _SC(';')) OptionalSemicolon(); //END_SCOPE(); } } void IfStatement() { SQInteger jmppos; bool haselse = false; Lex(); Expect(_SC('(')); CommaExpr(true); Expect(_SC(')')); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jnepos = _fs->GetCurrentPos(); BEGIN_SCOPE(); //there is a situation where the if statement has a statement enclosed by {} //and after the closing "}" there is no newline or semicolom //it's a valid construction but the compiler was complaining about it //for now added "&& _token != TK_IDENTIFIER" to the check after "Statement()" call /* local color = "blue"; if(color == "yellow"){ print(color); } print("Waht color is it ?"); */ Statement(); // if(_token != _SC('}') && _token != TK_ELSE && _token != TK_IDENTIFIER) OptionalSemicolon(); END_SCOPE(); SQInteger endifblock = _fs->GetCurrentPos(); if(_token == TK_ELSE){ haselse = true; BEGIN_SCOPE(); _fs->AddInstruction(_OP_JMP); jmppos = _fs->GetCurrentPos(); Lex(); Statement(); if(_token != TK_IDENTIFIER) OptionalSemicolon(); END_SCOPE(); _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); } _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); } void WhileStatement() { SQInteger jzpos, jmppos; jmppos = _fs->GetCurrentPos(); Lex(); Expect(_SC('(')); CommaExpr(true); Expect(_SC(')')); BEGIN_BREAKBLE_BLOCK(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); BEGIN_SCOPE(); Statement(); END_SCOPE(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); END_BREAKBLE_BLOCK(jmppos); } void DoWhileStatement() { Lex(); SQInteger jmptrg = _fs->GetCurrentPos(); BEGIN_BREAKBLE_BLOCK() BEGIN_SCOPE(); Statement(); END_SCOPE(); //fix proposed by frosch to correct line number info in stack dumps _fs->AddLineInfos(_lex.data->currentline, _lineinfo, true); Expect(TK_WHILE); SQInteger continuetrg = _fs->GetCurrentPos(); Expect(_SC('(')); CommaExpr(true); Expect(_SC(')')); _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), 1); _fs->AddInstruction(_OP_JMP, 0, jmptrg - _fs->GetCurrentPos() - 1); END_BREAKBLE_BLOCK(continuetrg); } void ForStatement() { Lex(); BEGIN_SCOPE(); Expect(_SC('(')); switch(_token){ case TK_IDENTIFIER:{ SQObject id = _fs->CreateString(_lex.data->svalue); if(!CheckTypeName(id)) goto lbl_commaexpr;//not a C/C++ type declaration; } //ignore custom types for now //fallthrough as local declaration CASE_TK_LOCAL_TYPES: case TK_LOCAL: LocalDeclStatement(); break; default: lbl_commaexpr: 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); END_SCOPE(); 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 BEGIN_SCOPE(); //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, _scope.nested); _fs->AddInstruction(_OP_LOADNULLS, indexpos,1); //push the value local var SQInteger valuepos = _fs->PushLocalVariable(valname, _scope.nested); _fs->AddInstruction(_OP_LOADNULLS, valuepos,1); //push reference index SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@")), _scope.nested); //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(); _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos); //generate the statement code BEGIN_BREAKBLE_BLOCK() Statement(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); _fs->SetIntructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos); END_BREAKBLE_BLOCK(foreachpos - 1); //restore the local variable stack(remove index,val and ref idx) _fs->PopTarget(); END_SCOPE(); } void SwitchStatement() { Lex(); Expect(_SC('(')); CommaExpr(true); 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) { if(!bfirst) { _fs->AddInstruction(_OP_JMP, 0, 0); skipcondjmp = _fs->GetCurrentPos(); _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); } //condition Lex(); ExpressionScalar() /*Expression()*/; Expect(_SC(':')); SQInteger trg = _fs->PopTarget(); SQInteger eqtarget = trg; bool local = _fs->IsLocal(trg); if(local) { eqtarget = _fs->PushTarget(); //we need to allocate a extra reg } _fs->AddInstruction(_OP_EQ, eqtarget, trg, expr); _fs->AddInstruction(_OP_JZ, eqtarget, 0); if(local) { _fs->PopTarget(); } //end condition if(skipcondjmp != -1) { _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); } tonextcondjmp = _fs->GetCurrentPos(); BEGIN_SCOPE(); Statements(); END_SCOPE(); bfirst = false; } if(tonextcondjmp != -1) _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); if(_token == TK_DEFAULT) { Lex(); Expect(_SC(':')); BEGIN_SCOPE(); Statements(); END_SCOPE(); } Expect(_SC('}')); _fs->PopTarget(); __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__); _fs->_breaktargets.pop_back(); } void TemplateStatement() { Lex(); //ignore for now Expect(_SC('<')); int nest_count = 1; while(_token > 0 && nest_count > 0){ Lex(); switch(_token) { case _SC('>'): --nest_count; break; case _SC('<'): nest_count++; } } if(nest_count == 0) Lex(); //last '>' ignore for now } 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); else CheckGlobalName(id, true); while(_token == TK_DOUBLE_COLON) { Lex(); id = Expect(TK_IDENTIFIER); //todo check if class function already exists _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); } Expect(_SC('(')); CreateFunction(id, eFunctionType_global); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); EmitDerefOp(_OP_NEWSLOT); _fs->PopTarget(); } void ClassStatement() { SQExpState es; SQObjectPtr class_name; Lex(); if(_token == TK_IDENTIFIER) { class_name = SQString::Create(_ss(_vm), _lex.data->svalue); CheckGlobalName(class_name, true); CheckTypeName(class_name, true); //to allow C/C++ style instance declarations } es = _es; _es.donot_get = true; PrefixedExpr(); if(_token == _SC(';')) //class forward declaration { //return; } if(_es.etype == EXPR) { Error(_SC("invalid class name")); } else if(_es.etype == OBJECT || _es.etype == BASE) { ClassExp(&class_name); EmitDerefOp(_OP_NEWSLOT); _fs->PopTarget(); } else { Error(_SC("cannot create a class in a local with the syntax(class )")); } _es = es; } SQObject ExpectScalar() { SQObject val; val._type = OT_NULL; val._unVal.nInteger = 0; //shut up GCC 4.x switch(_token) { case TK_INTEGER: val._type = OT_INTEGER; val._unVal.nInteger = _lex.data->nvalue; break; case TK_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = _lex.data->fvalue; break; case TK_STRING_LITERAL: val = _fs->CreateString(_lex.data->svalue,_lex.data->longstr.size()-1); break; case TK_TRUE: case TK_FALSE: val._type = OT_BOOL; val._unVal.nInteger = _token == TK_TRUE ? 1 : 0; break; case '-': Lex(); switch(_token) { case TK_INTEGER: val._type = OT_INTEGER; val._unVal.nInteger = -_lex.data->nvalue; break; case TK_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = -_lex.data->fvalue; break; default: Error(_SC("scalar expected : integer, float")); } break; default: Error(_SC("scalar expected : integer, float or string")); } Lex(); return val; } SQInteger ExpressionScalar() { SQInteger tk_type = _token; switch(_token) { case TK_INTEGER: EmitLoadConstInt(_lex.data->nvalue,-1); break; case TK_FLOAT: EmitLoadConstFloat(_lex.data->fvalue,-1); break; case TK_STRING_LITERAL: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex.data->svalue,_lex.data->longstr.size()-1))); break; case TK_IDENTIFIER: { SQObject id = _fs->CreateString(_lex.data->svalue); Lex(); ExpressionConstant(id); return tk_type; } break; case '-': Lex(); tk_type = _token; switch(_token) { case TK_INTEGER: EmitLoadConstInt(-_lex.data->nvalue,-1); break; case TK_FLOAT: EmitLoadConstFloat(-_lex.data->fvalue,-1); break; default: Error(_SC("scalar expected : integer, float")); } break; default: goto error; } Lex(); return tk_type; error: Error(_SC("constant or scalar expected : integer, float or string")); return 0; } void EnumStatement() { Lex(); SQObject id = Expect(TK_IDENTIFIER); Expect(_SC('{')); //CheckLocalNameScope(id, _scope.nested); SQObjectPtr strongid = id; CheckLocalNameScope(id, _scope.nested); SQObject table = _fs->CreateTable(); //_fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); SQInteger nval = 0; while(_token != _SC('}')) { SQObject key = Expect(TK_IDENTIFIER); SQObject val; if(_token == _SC('=')) { Lex(); val = ExpectScalar(); } else { val._type = OT_INTEGER; val._unVal.nInteger = nval++; } //SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOT, 0xFF, table, key, val); _table(table)->NewSlot(SQObjectPtr(key),SQObjectPtr(val)); if(_token == ',') Lex(); } ConstsNewSlot(SQObjectPtr(strongid),SQObjectPtr(table)); strongid.Null(); Lex(); } 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(); { BEGIN_SCOPE(); Statement(); END_SCOPE(); } _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(')')); { BEGIN_SCOPE(); SQInteger ex_target = _fs->PushLocalVariable(exid, _scope.nested); _fs->SetIntructionParam(trappos, 0, ex_target); Statement(); _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); END_SCOPE(); } } void FunctionExp(SQInteger ftype,bool lambda = false) { Lex(); Expect(_SC('(')); SQObjectPtr dummy; CreateFunction(dummy, lambda ? eFunctionType_lambda : eFunctionType_anonymous); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1); } void ClassExp(SQObjectPtr *class_name) { SQInteger base = -1; SQInteger attrs = -1; bool hasInheritance = false; switch(_token) { case _SC(':'): //C++ style class derivation case TK_EXTENDS: //squirrel style class derivation Lex(); hasInheritance = true; switch(_token) { case TK_PRIVATE: case TK_PUBLIC: Lex(); //ignore, accepted only to compile a subset of C++ } } if(hasInheritance) { Expression(); base = _fs->TopTarget(); } if(_token == TK_ATTR_OPEN) { Lex(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); ParseTableOrClass(_SC(','),TK_ATTR_CLOSE); attrs = _fs->TopTarget(); } Expect(_SC('{')); if(attrs != -1) _fs->PopTarget(); if(base != -1) _fs->PopTarget(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), base, attrs,NOT_CLASS); ParseTableOrClass(_SC(';'),_SC('}'), class_name); } void DeleteExpr() { SQExpState es; Lex(); es = _es; _es.donot_get = true; PrefixedExpr(); if(_es.etype==EXPR) Error(_SC("can't delete an expression")); if(_es.etype==OBJECT || _es.etype==BASE) { Emit2ArgsOP(_OP_DELETE); } else { Error(_SC("cannot delete an (outer) local")); } _es = es; } void PrefixIncDec(SQInteger token) { SQExpState es; SQInteger diff = (token==TK_MINUSMINUS) ? -1 : 1; Lex(); es = _es; _es.donot_get = true; PrefixedExpr(); if(_es.etype==EXPR) { Error(_SC("can't '++' or '--' an expression")); } else if(_es.etype==OBJECT || _es.etype==BASE) { Emit2ArgsOP(_OP_INC, diff); } else if(_es.etype==LOCAL) { SQInteger src = _fs->TopTarget(); _fs->AddInstruction(_OP_INCL, src, src, 0, diff); } else if(_es.etype==OUTER) { SQInteger tmp = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp, _es.epos); _fs->AddInstruction(_OP_INCL, tmp, tmp, 0, diff); _fs->AddInstruction(_OP_SETOUTER, tmp, _es.epos, tmp); } _es = es; } void CreateFunction(SQObject &name, int ftype, int stack_offset=0) { SQFuncState *funcstate = _fs->PushChildState(_ss(_vm)); funcstate->_name = name; SQObject paramname, type_name; funcstate->AddParameter(_fs->CreateString(_SC("this")), _scope.nested+1); funcstate->_sourcename = _sourcename; SQInteger defparams = 0; bool is_reference_declaration = 0; const SQChar *param_type_name = 0; bool isVoid = false; while(_token!=_SC(')')) { if(isVoid) { Error(_SC("void type is invalid here")); } is_reference_declaration = 0; //reset is_reference_declaration param_type_name = 0; //rest for each parameter if(_token == TK_VOID) { isVoid = true; Lex(); continue; } if(_token == TK_VARPARAMS) { if(defparams > 0) Error(_SC("function with default parameters cannot have variable number of parameters")); funcstate->AddParameter(_fs->CreateString(_SC("vargv")), _scope.nested+1); funcstate->_varparams = true; Lex(); if(_token != _SC(')')) Error(_SC("expected ')'")); break; } else { if(_token == TK_CONST) Lex(); //C/C++ const cualifiers for now switch(_token) { CASE_TK_LOCAL_TYPES: //accept C/C++ type parameter declarations param_type_name = _lex.GetTokenName(_token); Lex(); } CHECK_REF_DECLARATION(_token); paramname = Expect(TK_IDENTIFIER); if(CheckNameIsType(paramname)) { //ignore custom types for now param_type_name = _stringval(paramname); CHECK_REF_DECLARATION(_token); paramname = Expect(TK_IDENTIFIER); } funcstate->AddParameter(paramname, _scope.nested+1, is_reference_declaration ? _VAR_REFERENCE : _VAR_ANY); if(param_type_name) { funcstate->AddParameterTypeName(param_type_name); } if(_token == _SC('=')) { if(is_reference_declaration) Error(_SC("parameter passed by reference can't have default value")); Lex(); if(_token == _SC('[') || _token == _SC('{')) Error(_SC("default parameter with array/table values not supported")); Expression(); //stack_offset to compensate for local functions been relocated to allow recursion funcstate->AddDefaultParam(_fs->TopTarget()+stack_offset); defparams++; } else if(_token == _SC(':')){ //param type specifier like typescript if(param_type_name) { Error(_SC("parameter type already declared before %s"), _string(paramname)); } Lex(); type_name = ExpectTypeToken(); funcstate->AddParameterTypeName(type_name); //printf("%d %s\n", __LINE__, _stringval(type_name)); } else { if(defparams > 0) Error(_SC("expected '='")); } if(_token == _SC(',')) Lex(); else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); } } Expect(_SC(')')); switch(_token) { case _SC(':'):{ //return type specifier like typescript Lex(); type_name = ExpectTypeToken(); funcstate->_return_type = type_name; //printf("%d %s\n", __LINE__, _stringval(type_name)); } break; case TK_CONST: case TK_NOEXCEPT: case TK_VOLATILE: Lex(); //accept and ignore break; case TK_IDENTIFIER: if(ftype == eFunctionType_member) { if( (scstrcmp(_lex.data->svalue, _SC("final")) == 0) || (scstrcmp(_lex.data->svalue, _SC("override")) == 0)) { Lex(); //accept but ignore then } } } for(SQInteger n = 0; n < defparams; n++) { _fs->PopTarget(); } if(_is_parsing_extern) { if(_token == _SC(';')) //to parse thinscript { _fs->PopChildState(); return; } else _is_parsing_extern = false; } SQFuncState *currchunk = _fs; _fs = funcstate; if(ftype == eFunctionType_lambda) { Expression(); _fs->AddInstruction(_OP_RETURN, 1, _fs->PopTarget());} else { Statement(false); } ResolveGotos(); funcstate->AddLineInfos(_lex.data->prevtoken == _SC('\n')?_lex.data->lasttokenline:_lex.data->currentline, _lineinfo, true); funcstate->AddInstruction(_OP_RETURN, -1); funcstate->SetStackSize(0); SQFunctionProto *func = funcstate->BuildProto(); #ifdef _DEBUG_DUMP funcstate->Dump(func); #endif _fs = currchunk; _fs->_functions.push_back(func); _fs->PopChildState(); } void AdjustGotoInstruction(const SQGotoLabelsInfo &goto_info, const SQGotoLabelsInfo &label) { SQInteger target_pos = label.pos; SQInteger jump_pos = target_pos - goto_info.pos; //set the jmp instruction SQInteger poptraps = goto_info.traps - label.traps; SQInt32 ioffset = 1; while(poptraps) { SQInstruction &i_op_poptrap = _fs->GetInstruction(goto_info.pos-ioffset); if(i_op_poptrap.op != _OP_NOP) { if(ioffset < 2) { ++ioffset; continue; } Error(_SC("Compiler: This should not happen i_op_poptrap.op != _OP_NOP")); } //printf("poptraps for %s = %d : %d\n", _stringval(label.name), (int)poptraps, (int)i_op_poptrap._arg0); i_op_poptrap.op = _OP_POPTRAP; i_op_poptrap._arg0 = poptraps; break; } _fs->SetIntructionParams(goto_info.pos, 0, jump_pos, 0); } void ResolveGotos() { //first we walk through the labels and when we come back //nested labels we remove then because we only allow //goto/joump out of blocks/scopes SQInt16 last_nested = -1; for(SQUnsignedInteger idxlabel = 0; idxlabel < _fs->_gototargets.size(); ++idxlabel) { SQGotoLabelsInfo &label = _fs->_gototargets[idxlabel]; SQUnsignedInteger resolved = 0; for(SQUnsignedInteger idxGoto = 0; idxGoto < _fs->_unresolvedgotos.size(); ++idxGoto) { SQGotoLabelsInfo &goto_info = _fs->_unresolvedgotos[idxGoto]; if(_string(label.name) == _string(goto_info.name)) { if(label.nested > goto_info.nested) { _lex.data->currentline = goto_info.line; Error(_SC("Goto can not jump inside nested block/scope '%s'"), _stringval(goto_info.name)); } AdjustGotoInstruction(goto_info, label); //we dcrease idxGoto here to compensate the removal inside the for loop _fs->_unresolvedgotos.remove(idxGoto--); ++resolved; } } if(!resolved) { //if a label isn't resolved we have an unused label _lex.data->currentline = label.line; Error(_SC("Label not resolved at this point '%s'"), _stringval(label.name)); } if(label.nested < last_nested) { //remove any previous nested label to prevent goto/jump inside blocks/scopes SQUnsignedInteger i = idxlabel - 1; for(; i > 0; --i) { if(_fs->_gototargets[i].nested <= label.nested) break; _fs->_gototargets.remove(i); //we decrease the index idxlabel here due to remove before //the current idxlabel --idxlabel; } } last_nested = label.nested; } if(_fs->_unresolvedgotos.size()) { //if we still have any unresolved goto it's an error, let's show the first SQGotoLabelsInfo &goto_info = _fs->_unresolvedgotos[0]; _lex.data->currentline = goto_info.line; Error(_SC("Goto can not jump to undeclared label '%s'"), _stringval(goto_info.name)); } } 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; bool _show_warnings; bool _is_parsing_extern; SQInteger _debugline; SQInteger _debugop; SQExpState _es; SQScope _scope; SQChar *_compilererror; jmp_buf _errorjmp; SQVM *_vm; SQObjectPtrVec _scope_consts; SQObjectPtr _globals; SQObjectPtr _type_names; //to allow C/C++ style instance declarations SQObjectPtr _extern_names; //to allow C/C++ style extern declarations SQChar error_buf[MAX_COMPILER_ERROR_LEN]; SQInteger _max_nested_includes, _nested_includes_count; }; bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo, bool show_warnings, SQInteger max_nested_includes) { SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo, show_warnings, max_nested_includes); return p.Compile(out); } #endif