#include "sq_lexer_tokens.nut" #include "sqtypedefs.nut" //const SQUIRREL_VERSION = "SquiLu based on Squirrel 3.1 stable and Lua 5.1.5"); //const SQUIRREL_COPYRIGHT = "Copyright (C) 2003-2016 Alberto Demichelis, Domingo Alvarez Duarte"); //const SQUIRREL_AUTHOR = "Alberto Demichelis, Domingo Alvarez Duarte"); const SQUIRREL_VERSION_NUMBER = 310; const SQ_VMSTATE_IDLE = 0; const SQ_VMSTATE_RUNNING = 1; const SQ_VMSTATE_SUSPENDED = 2; const SQUIRREL_EOB = 0; const SQ_BYTECODE_STREAM_TAG = 0xFAFA; const SQOBJECT_REF_COUNTED = 0x08000000; const SQOBJECT_NUMERIC = 0x04000000; const SQOBJECT_DELEGABLE = 0x02000000; const SQOBJECT_CANBEFALSE = 0x01000000; const SQ_MATCHTYPEMASKSTRING = /*(*/-99999; const _RT_MASK = 0x00FFFFFF; //const _RAW_TYPE(type) (type&_RT_MASK); const _RT_NULL = 0x00000001; const _RT_INTEGER = 0x00000002; const _RT_FLOAT = 0x00000004; const _RT_BOOL = 0x00000008; const _RT_STRING = 0x00000010; const _RT_TABLE = 0x00000020; const _RT_ARRAY = 0x00000040; const _RT_USERDATA = 0x00000080; const _RT_CLOSURE = 0x00000100; const _RT_NATIVECLOSURE = 0x00000200; const _RT_GENERATOR = 0x00000400; const _RT_USERPOINTER = 0x00000800; const _RT_THREAD = 0x00001000; const _RT_FUNCPROTO = 0x00002000; const _RT_CLASS = 0x00004000; const _RT_INSTANCE = 0x00008000; const _RT_WEAKREF = 0x00010000; const _RT_OUTER = 0x00020000; /* //typedef //enum tagSQObjectType { int_t OT_NULL = (_RT_NULL|SQOBJECT_CANBEFALSE); int_t OT_INTEGER = (_RT_INTEGER|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE); int_t OT_FLOAT = (_RT_FLOAT|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE); int_t OT_BOOL = (_RT_BOOL|SQOBJECT_CANBEFALSE); int_t OT_STRING = (_RT_STRING|SQOBJECT_REF_COUNTED); int_t OT_TABLE = (_RT_TABLE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE); int_t OT_ARRAY = (_RT_ARRAY|SQOBJECT_REF_COUNTED); int_t OT_USERDATA = (_RT_USERDATA|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE); int_t OT_CLOSURE = (_RT_CLOSURE|SQOBJECT_REF_COUNTED); int_t OT_NATIVECLOSURE = (_RT_NATIVECLOSURE|SQOBJECT_REF_COUNTED); int_t OT_GENERATOR = (_RT_GENERATOR|SQOBJECT_REF_COUNTED); int_t OT_USERPOINTER = _RT_USERPOINTER; int_t OT_THREAD = (_RT_THREAD|SQOBJECT_REF_COUNTED); int_t OT_FUNCPROTO = (_RT_FUNCPROTO|SQOBJECT_REF_COUNTED); //internal usage only int_t OT_CLASS = (_RT_CLASS|SQOBJECT_REF_COUNTED); int_t OT_INSTANCE = (_RT_INSTANCE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE); int_t OT_WEAKREF = (_RT_WEAKREF|SQOBJECT_REF_COUNTED); int_t OT_OUTER = (_RT_OUTER|SQOBJECT_REF_COUNTED); //internal usage only //}SQObjectType; */ const OT_NULL = 1; const OT_INTEGER = 2; const OT_FLOAT = 3; const OT_BOOL = 4; const OT_STRING = 5; const OT_TABLE = 6; const OT_ARRAY = 7; const OT_USERDATA = 8; const OT_CLOSURE = 9; const OT_NATIVECLOSURE = 10; const OT_GENERATOR = 11; const OT_USERPOINTER = 12; const OT_THREAD = 13; const OT_FUNCPROTO = 14; const OT_CLASS = 15; const OT_INSTANCE = 16; const OT_WEAKREF = 17; const OT_OUTER = 18; enum eFunctionType { fn_global = 1, fn_local, fn_lambda, fn_member, fn_anonymous }; const EXPR = 1; const OBJECT = 2; const BASE = 3; const LOCAL = 4; const OUTER = 5; const 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 */ }; const MAX_COMPILER_ERROR_LEN = 256; struct SQScope { SQInteger outers; SQInteger stacksize; SQInteger nested; }; static SQInteger compilerReadFunc(SQUserPointer fp) { SQInteger c = fgetc(/*(FILE *)*/fp); if(c == EOF) { return 0; } return c; } class SQCompiler { //private: SQInteger _token; SQFuncState_ptr_t _fs; SQObjectPtr _sourcename; SQLex _lex; bool _lineinfo; bool _raiseerror; bool _show_warnings; bool _is_parsing_extern; SQInteger _debugline; SQInteger _debugop; SQExpState _es; SQScope _scope; SQChar_ptr_t _compilererror; jmp_buf _errorjmp; SQVM_ptr_t _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; //public: SQCompiler() { _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_ptr_t ud, const SQChar_ptr_t s) { SQCompiler_ptr_t c = /*(SQCompiler *)*/ud; c->Error(s); } void Error(const SQChar_ptr_t s, ...) //__attribute__((format(printf, 2, 3))) { throw s; 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_ptr_t 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_ptr_t found = NULL; if(CheckExternName(name)) { found = "extern"; } else if(_table(_globals)->Exists(name)) { found = "global"; } if(found) { if(checkLocals) Error("%s '%s' already declared", found, _stringval(name)); else Warning("%s:%d:%d warning %s '%s' already declared will be shadowed\n", _stringval(_sourcename), _lex._currentline, _lex._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("local '%s' already declared", _stringval(name)); else Warning("%s:%d:%d warning local '%s' already declared will be shadowed\n", _stringval(_sourcename), _lex._currentline, _lex._currentcolumn, _stringval(name)); } else { found = _fs->FindOuterVariable(name); if(found >= 0) Warning("%s:%d:%d warning outer variable '%s' already declared will be shadowed\n", _stringval(_sourcename), _lex._currentline, _lex._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("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("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("constant '%s' already exists\n", _stringval(key)); } if(found >= 0) Warning("%s:%d:%d warning an already defined constant '%s' will be shadowed\n", _stringval(_sourcename), _lex._currentline, _lex._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) { 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; default: ret = _fs->CreateString(_lex.GetTokenName(_token)); } 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_ptr_t etypename; if(tok > 255) { switch(tok) { case TK_IDENTIFIER: etypename = "IDENTIFIER"; break; case TK_STRING_LITERAL: etypename = "STRING_LITERAL"; break; case TK_INTEGER: etypename = "INTEGER"; break; case TK_FLOAT: etypename = "FLOAT"; break; default: etypename = _lex.Tok2Str(tok); } Error("expected '%s'", etypename); } Error("expected '%c'", tok); } } } SQObject Expect(SQInteger tok) { ErrorIfNotToken(tok); return GetTokenObject(tok); } SQObject ExpectTypeToken() { switch(_token) { //CASE_TK_LOCAL_TYPES: case TK_IDENTIFIER: break; default: ErrorIfNotToken(TK_IDENTIFIER); } return GetTokenObject(TK_IDENTIFIER); } bool IsEndOfStatement() { return ((_lex._prevtoken == '\n') || (_token == SQUIRREL_EOB) || (_token == '}') || (_token == ';')); } void OptionalSemicolon() { if(_token == ';') { Lex(); return; } if(!IsEndOfStatement()) { Error("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._currentline; //int column = _lex._currentcolumn; Lex(); SQObject id = Expect(TK_IDENTIFIER); if(scstrcmp(_stringval(id), "include") == 0) { //#if SQ_ENABLE_INCLUDES SQInteger nested_count = _nested_includes_count + 1; if((_max_nested_includes <= 0) || (nested_count > _max_nested_includes)) { Error("Error: too many nested includes %d %s\n", nested_count, _stringval(id)); } id = Expect(TK_STRING_LITERAL); //Warning("%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._currentline; SQInteger saved_column = _lex._currentcolumn; SQInteger saved_curdata = _lex._currdata; SQInteger saved_prevtoken = _lex._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._prevtoken != '}' && _lex._prevtoken != ';') 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._currdata = saved_curdata; _lex._prevtoken = saved_prevtoken; _lex._currentcolumn = saved_column; _lex._currentline = saved_line; _lex._readf = saved_readf; _lex._up = saved_up; --_nested_includes_count; //done let's continue working } else { Error("Error: opening include file %s\n", _stringval(id)); } //#else // Error("Error: includes are not enabled\n"); //#endif } else { _lex._currentline = line; Error("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), "main"); _fs = /*&*/funcstate; _fs->AddParameter(_fs->CreateString("this"), _scope.nested+1); _fs->AddParameter(_fs->CreateString("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._prevtoken != '}' && _lex._prevtoken != ';') OptionalSemicolon(); } _fs->SetStackSize(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):"unknown", _lex._currentline, _lex._currentcolumn); } _vm->_lasterror = SQString_Create(_ss(_vm), _compilererror, -1); _vm->_lasterror_line = _lex._currentline; _vm->_lasterror_column = _lex._currentcolumn; return false; } return true; } void Statements() { while(_token != '}' && _token != TK_DEFAULT && _token != TK_CASE) { Statement(); if(_lex._prevtoken != '}' && _lex._prevtoken != ';') OptionalSemicolon(); } } void Statement(bool closeframe = true) { _es.etype = EXPR_STATEMENT; SQObject id; _fs->AddLineInfos(_lex._currentline, _lineinfo); start_again: switch(_token) { case ';': 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_BREAK: if(_fs->_breaktargets.size() <= 0)Error("'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("'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 '{': { BEGIN_SCOPE(); Lex(); Statements(); Expect('}'); 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(); id = Expect(TK_IDENTIFIER); if(_token == ':') { //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("%s:%d:%d warning static cualifier is ignored\n", _stringval(_sourcename), _lex._currentline, _lex._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(';'); 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(';'); TypesNewSlot(strongid,SQObjectPtr(type_val)); } break; case TK_PRAGMA: Pragma(); break; case TK_UNSAFE: Lex(); //ignore for now goto start_again; break; case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); if(CheckTypeName(id)) //C/C++ type declaration; { 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(" '=' expected near '%s'"), _lex._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("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._svalue); } //else id = {}; //old compilers do not allow this LogicalOrExp(); switch(_token) { case '=': 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("can't assign expression"); else if(ds == BASE) Error("'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("can't 'create' a local slot"); break; case '=': //ASSIGN if(warningAssign) Warning("%s:%d:%d warning making assignment, maybe it's not what you want\n", _stringval(_sourcename), _lex._currentline, _lex._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 '?': { 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(':'); 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 == '|') { BIN_EXP(_OP_BITW,/*&*/SQCompiler_BitwiseXorExp,BW_OR); } else return; } void BitwiseXorExp() { BitwiseAndExp(); for(;;) if(_token == '^') { BIN_EXP(_OP_BITW,/*&*/SQCompiler_BitwiseAndExp,BW_XOR); } else return; } void BitwiseAndExp() { EqExp(); for(;;) if(_token == '&') { 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 '>': BIN_EXP(_OP_CMP, /*&*/SQCompiler_ShiftExp,CMP_G); break; case '<': 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 '+': case '-': BIN_EXP(ChooseArithOpByToken(_token), /*&*/SQCompiler_MultExp); break; default: return; } } void MultExp() { PrefixedExpr(); for(;;) switch(_token) { case '*': case '/': case '%': 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 '.': 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 '[': if(_lex._prevtoken == '\n') Error("cannot brake deref/or comma needed after [exp]=exp slot declaration"); Lex(); Expression(); Expect(']'); 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("can't '++' or '--' an expression"); break; case OBJECT: case BASE: if(_es.donot_get == true) { Error("can't '++' or '--' an expression"); //mmh dor this make sense? break; } 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 '(': 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() {} 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 '=': case '(': 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 == '.' || _token == '['))); } void FunctionCallArgs() { SQInteger nargs = 1;//this while(_token != ')') { Expression(); MoveIfCurrentTargetIsLocal(); nargs++; if(_token == ',') { Lex(); if(_token == ')') Error("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 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("class already has a member named: %s", _stringval(name)); } if(addIfNotExists) AddClassMemberExists(member_names, name); } //void CheckClassMemberExists(SQObjectPtr &member_names, const SQChar_ptr_t name){} void ParseTableOrClass(SQInteger separator,SQInteger terminator, SQObjectPtr_ptr_t class_name=NULL) {} void ExternDeclStatement() { _is_parsing_extern = true; Lex(); LocalDeclStatement(); _is_parsing_extern = false; } void LocalDeclStatement() {} void IfBlock() { if (_token == '{') { BEGIN_SCOPE(); Lex(); Statements(); Expect('}'); if (true) { END_SCOPE(); } else { END_SCOPE_NO_CLOSE(); } } else { //BEGIN_SCOPE(); Statement(); if (_lex._prevtoken != '}' && _lex._prevtoken != ';') OptionalSemicolon(); //END_SCOPE(); } } void IfStatement() { SQInteger jmppos; bool haselse = false; Lex(); Expect('('); CommaExpr(true); Expect(')'); _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 != '}' && _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('('); CommaExpr(true); Expect(')'); 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._currentline, _lineinfo, true); Expect(TK_WHILE); SQInteger continuetrg = _fs->GetCurrentPos(); Expect('('); CommaExpr(true); Expect(')'); _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('('); switch(_token) { case TK_IDENTIFIER: //ignore custom types for now //fallthrough as local declaration //CASE_TK_LOCAL_TYPES: case TK_LOCAL: LocalDeclStatement(); break; default: if(_token != ';') { CommaExpr(); _fs->PopTarget(); } } Expect(';'); _fs->SnoozeOpt(); SQInteger jmppos = _fs->GetCurrentPos(); SQInteger jzpos = -1; if(_token != ';') { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } Expect(';'); _fs->SnoozeOpt(); SQInteger expstart = _fs->GetCurrentPos() + 1; if(_token != ')') { CommaExpr(); _fs->PopTarget(); } Expect(')'); _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('('); valname = Expect(TK_IDENTIFIER); if(_token == ',') { idxname = valname; Lex(); valname = Expect(TK_IDENTIFIER); } else { idxname = _fs->CreateString("@INDEX@"); } Expect(TK_IN); //save the stack size BEGIN_SCOPE(); //put the table in the stack(evaluate the table expression) Expression(); Expect(')'); 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("@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('('); CommaExpr(true); Expect(')'); Expect('{'); 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(':'); SQInteger trg = _fs->PopTarget(); SQInteger eqtarget = trg; bool vlocal = _fs->IsLocal(trg); if(vlocal) { eqtarget = _fs->PushTarget(); //we need to allocate a extra reg } _fs->AddInstruction(_OP_EQ, eqtarget, trg, expr); _fs->AddInstruction(_OP_JZ, eqtarget, 0); if(vlocal) { _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(':'); BEGIN_SCOPE(); Statements(); END_SCOPE(); } Expect('}'); _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); 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('('); 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._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 == ';') //class forward declaration { //return; } if(_es.etype == EXPR) { Error("invalid class name"); } else if(_es.etype == OBJECT || _es.etype == BASE) { ClassExp(/*&*/class_name); EmitDerefOp(_OP_NEWSLOT); _fs->PopTarget(); } else { Error("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._nvalue; break; case TK_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = _lex._fvalue; break; case TK_STRING_LITERAL: val = _fs->CreateString(_lex._svalue,_lex._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._nvalue; break; case TK_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = -_lex._fvalue; break; default: Error("scalar expected : integer, float"); } break; default: Error("scalar expected : integer, float or string"); } Lex(); return val; } SQInteger ExpressionScalar() { SQInteger tk_type = _token; switch(_token) { case TK_INTEGER: EmitLoadConstInt(_lex._nvalue,-1); break; case TK_FLOAT: EmitLoadConstFloat(_lex._fvalue,-1); break; case TK_STRING_LITERAL: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); break; case TK_IDENTIFIER: { SQObject id = _fs->CreateString(_lex._svalue); Lex(); ExpressionConstant(id); return tk_type; } break; case '-': Lex(); tk_type = _token; switch(_token) { case TK_INTEGER: EmitLoadConstInt(-_lex._nvalue,-1); break; case TK_FLOAT: EmitLoadConstFloat(-_lex._fvalue,-1); break; default: Error("scalar expected : integer, float"); } break; default: goto error; } Lex(); return tk_type; error: Error("constant or scalar expected : integer, float or string"); return 0; } void EnumStatement() { Lex(); SQObject id = Expect(TK_IDENTIFIER); Expect('{'); //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 != '}') { SQObject key = Expect(TK_IDENTIFIER); SQObject val; if(_token == '=') { 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('('); exid = Expect(TK_IDENTIFIER); Expect(')'); { 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('('); 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_ptr_t class_name) { SQInteger vbase = -1; SQInteger attrs = -1; bool hasInheritance = false; switch(_token) { case ':': //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(); vbase = _fs->TopTarget(); } if(_token == TK_ATTR_OPEN) { Lex(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); ParseTableOrClass(',',TK_ATTR_CLOSE); attrs = _fs->TopTarget(); } Expect('{'); if(attrs != -1) _fs->PopTarget(); if(vbase != -1) _fs->PopTarget(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), vbase, attrs,NOT_CLASS); ParseTableOrClass(';','}', class_name); } void DeleteExpr() { SQExpState es; Lex(); es = _es; _es.donot_get = true; PrefixedExpr(); if(_es.etype==EXPR) Error("can't delete an expression"); if(_es.etype==OBJECT || _es.etype==BASE) { Emit2ArgsOP(_OP_DELETE); } else { Error("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("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_ptr_t funcstate = _fs->PushChildState(_ss(_vm)); funcstate->_name = name; SQObject paramname, type_name; funcstate->AddParameter(_fs->CreateString("this"), _scope.nested+1); funcstate->_sourcename = _sourcename; SQInteger defparams = 0; bool is_reference_declaration = 0; const SQChar_ptr_t param_type_name = 0; bool isVoid = false; while(_token!=')') { if(isVoid) { Error("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("function with default parameters cannot have variable number of parameters"); funcstate->AddParameter(_fs->CreateString("vargv"), _scope.nested+1); funcstate->_varparams = true; Lex(); if(_token != ')') Error("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 == '=') { if(is_reference_declaration) Error("parameter passed by reference can't have default value"); Lex(); if(_token == '[' || _token == '{') Error("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 == ':') { //param type specifier like typescript if(param_type_name) { Error("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("expected '='"); } if(_token == ',') Lex(); else if(_token != ')') Error("expected ')' or ','"); } } Expect(')'); switch(_token) { case ':': { //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._svalue, "final") == 0) || (scstrcmp(_lex._svalue, "override") == 0)) { Lex(); //accept but ignore then } } } for(SQInteger n = 0; n < defparams; n++) { _fs->PopTarget(); } if(_is_parsing_extern) { if(_token == ';') //to parse thinscript { _fs->PopChildState(); return; } else _is_parsing_extern = false; } SQFuncState_ptr_t currchunk = _fs; _fs = funcstate; if(ftype == eFunctionType_lambda) { Expression(); _fs->AddInstruction(_OP_RETURN, 1, _fs->PopTarget()); } else { Statement(false); } funcstate->AddLineInfos(_lex._prevtoken == '\n'?_lex._lasttokenline:_lex._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 ResolveBreaks(SQFuncState_ptr_t funcstate, SQInteger ntoresolve) {} void ResolveContinues(SQFuncState_ptr_t funcstate, SQInteger ntoresolve, SQInteger targetpos) {} //private: }; string_t txt = readfile("test-raii.nut") var lex = new SQLexer(txt, true /*we want comments*/); var first_enum_token = lex.first_enum_token(); var last_enum_token = lex.last_enum_token(); var nest_level = 0; var tok = lex.lex(); //lex.lex(); while(tok > 0) { local tkstr; if(tok > first_enum_token && tok < last_enum_token) tkstr = lex.tok2str(tok); //lex.tok2str(tok); else tkstr = tok.tochar(); /* print( tok, tkstr || "", lex.token_name(tok), //lex.token_name(tok), lex.svalue() || "", lex.lasttokenline(), lex.lasttokencolumn(), lex.currentline(), lex.currentcolumn() ); */ switch(tok) { case '{': case '}': default: print(tkstr); } tok = lex.lex(); } bool Compile(SQVM_ptr_t vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar_ptr_t 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); }