Kaynağa Gözat

First implementation of "#include" compiler directive like in C/C++ by default it's limited to max 10 nested includes

mingodad 9 yıl önce
ebeveyn
işleme
483b2feb5b

+ 1 - 0
SquiLu/include/squirrel.h

@@ -191,6 +191,7 @@ typedef unsigned char USQChar;
 #endif
 //#define scatoi		atoi
 #define scstrtoul	strtoul
+#define scfprintf	fprintf
 #define scvfprintf	vfprintf
 #define scvsprintf	vsprintf
 #define scvsnprintf	vsnprintf

+ 154 - 39
SquiLu/squirrel/sqcompiler.cpp

@@ -38,7 +38,7 @@ struct SQScope {
 					 ++_scope.nested; \
 					 _scope.outers = _fs->_outers; \
 					 _scope.stacksize = _fs->GetStackSize();\
-					 _scope_consts.push_back(SQTable::Create(_ss(_vm),0));
+					 _scope_consts->push_back(SQTable::Create(_ss(_vm),0));
 
 #define RESOLVE_OUTERS() if(_fs->GetStackSize() != _scope.stacksize) { \
 							if(_fs->CountOuters(_scope.stacksize)) { \
@@ -50,7 +50,7 @@ struct SQScope {
 							_fs->SetStackSize(_scope.stacksize); \
 						} \
 						_scope = __oldscope__; \
-						_scope_consts.pop_back();\
+						_scope_consts->pop_back();\
 					}
 
 #define END_SCOPE() {	SQInteger oldouters = _fs->_outers;\
@@ -102,11 +102,23 @@ struct SQScope {
 		case TK_LOCAL_TABLE_T: \
 		case TK_LOCAL_ARRAY_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)
+private:
+
+    void initCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename,
+            bool raiseerror, bool lineinfo, bool show_warnings,
+            SQInteger nest_compiling_count, SQObjectPtr *use_this_globals, SQInteger max_nested_includes)
 	{
 		_vm=v;
 		_lex.Init(_ss(v), rg, up,ThrowError,this);
@@ -117,11 +129,44 @@ public:
 		_scope.stacksize = 0;
 		_scope.nested = 0;
 		_compilererror = NULL;
-		_globals = SQTable::Create(_ss(_vm),0);
+
+		//indirections to allow recursive compilation using the same vm and SQFunctionstate
+		_nested_compile_count = nest_compiling_count;
+		_max_nested_includes = max_nested_includes;
+		if(use_this_globals)
+        {
+            assert(_nested_compile_count != 0);
+            _globals = *use_this_globals;
+        }
+        else
+        {
+            assert(_nested_compile_count == 0);
+            _globals = SQTable::Create(_ss(_vm),0);
+            _scope_consts = new SQObjectPtrVec();
+        }
+	}
+
+    SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename,
+            bool raiseerror, bool lineinfo, bool show_warnings,
+            SQInteger nest_compiling_count, SQObjectPtr *use_this_globals, SQInteger max_nested_includes)
+	{
+	    initCompiler(v, rg, up, sourcename, raiseerror, lineinfo, show_warnings,
+                  nest_compiling_count, use_this_globals, max_nested_includes);
+	}
+
+public:
+	SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename,
+            bool raiseerror, bool lineinfo, bool show_warnings, SQInteger max_nested_includes)
+	{
+	    initCompiler(v, rg, up, sourcename, raiseerror, lineinfo, show_warnings, 0, NULL, max_nested_includes);
 	}
 	~SQCompiler(){
-	    _table(_globals)->Finalize();
-	    _globals.Null();
+	    if(_nested_compile_count == 0)
+        {
+            _table(_globals)->Finalize();
+            _globals.Null();
+            delete _scope_consts;
+        }
 	}
 
 	static void ThrowError(void *ud, const SQChar *s) {
@@ -151,7 +196,7 @@ public:
 	void CheckGlobalName(const SQObject &name, bool addIfNotExists=false, bool checkLocals=true){
 	    if(_table(_globals)->Exists(name)){
             if(checkLocals) Error(_SC("global '%s' already declared"), _stringval(name));
-            else Warning(_SC("WARNING: %s:%d:%d global '%s' already declared will be shadowed\n"),
+            else Warning(_SC("%s:%d:%d warning global '%s' already declared will be shadowed\n"),
                         _stringval(_sourcename), _lex._currentline, _lex._currentcolumn, _stringval(name));
 	    }
 	    else if(checkLocals) CheckLocalNameScope(name, -1, false);
@@ -168,22 +213,24 @@ public:
 	        if(lvi._scope == scope)
                 Error(_SC("local '%s' already declared"), _stringval(name));
             else
-                Warning(_SC("WARNING: %s:%d:%d local '%s' already declared will be shadowed\n"),
+                Warning(_SC("%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(_SC("WARNING: %s:%d:%d outer variable '%s' already declared will be shadowed\n"),
+	        if(found >= 0) Warning(_SC("%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)) {
+	        if(_table((*_scope_consts)[i])->Get(name,val)) {
 	            e = val;
 	            return true;
 	        }
@@ -231,7 +278,7 @@ public:
 	void CheckConstsExists(const SQObjectPtr &key){
 	    int found = -1;
 	    for(int i=_scope.nested-1; i >= 0; --i){
-	        if(_table(_scope_consts[i])->Exists(key)) {
+	        if(_table((*_scope_consts)[i])->Exists(key)) {
 	            found = i+1;
 	            break;
 	        }
@@ -240,19 +287,19 @@ public:
         if(found == _scope.nested) {
             Error(_SC("constant '%s' already exists\n"), _stringval(key));
         }
-        if(found >= 0) Warning(_SC("WARNING: %s:%d:%d an already defined constant '%s' will be shadowed\n"),
+        if(found >= 0) Warning(_SC("%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;
+	        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);
+	    if(_scope.nested) return _table((*_scope_consts)[_scope.nested-1])->NewSlot(key,val);
 	    return _table(_ss(_vm)->_consts)->NewSlot(key,val);
 	}
 
@@ -341,18 +388,67 @@ public:
 			_fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg);
 		}
 	}
-	bool Compile(SQObjectPtr &o)
+
+private:
+
+    void Pragma()
+    {
+        int line = _lex._currentline;
+        //int column = _lex._currentcolumn;
+        Lex();
+        SQObject id = Expect(TK_IDENTIFIER);
+        if(scstrcmp(_stringval(id), _SC("include")) == 0)
+        {
+            SQInteger nested_count = _nested_compile_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));
+            }
+
+            id = Expect(TK_STRING_LITERAL);
+            //Warning(_SC("%s:%d:%d warning pragma include %s\n"),
+            //        _stringval(_sourcename), line, column, _stringval(id));
+
+            FILE *fp = fopen(_stringval(id), "r");
+            if(fp != NULL)
+            {
+                SQCompiler p(_vm, compilerReadFunc, fp, _stringval(id), _raiseerror, _lineinfo, _show_warnings,
+                             nested_count, &_globals, _max_nested_includes);
+
+                SQObjectPtr out, saved_source_name = _fs->_sourcename;
+                _fs->_sourcename = id;
+
+                SQInteger rc = p.CompileWithFuncstate(out, _fs, &_scope, _scope_consts);
+
+                _fs->_sourcename = saved_source_name;
+                fclose(fp);
+
+                if(!rc)
+                {
+                    Error(_SC("Error: compiling include file %s\n"), _stringval(id));
+                }
+            }
+            else
+            {
+                Error(_SC("Error: opening include file %s\n"), _stringval(id));
+            }
+        }
+        else
+        {
+            _lex._currentline = line;
+            Error(_SC("Error: unknown pragma %s\n"), _stringval(id));
+        }
+    }
+
+	bool CompileWithFuncstate(SQObjectPtr &o, SQFuncState *with_fs, SQScope *with_scope, SQObjectPtrVec *scv)
 	{
 		_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;
+		_fs = with_fs;
+		if(with_scope)_scope = *with_scope;
+		if(scv) _scope_consts = scv;
+
 		SQInteger stacksize = _fs->GetStackSize();
 		if(setjmp(_errorjmp) == 0) {
 			Lex();
@@ -360,14 +456,17 @@ public:
 				Statement();
 				if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();
 			}
-			_fs->SetStackSize(stacksize);
-			_fs->AddLineInfos(_lex._currentline, _lineinfo, true);
-			_fs->AddInstruction(_OP_RETURN, 0xFF);
-			_fs->SetStackSize(0);
-			o =_fs->BuildProto();
+			if(!scv)
+            {
+                _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));
+                _fs->Dump(_funcproto(o));
 #endif
+            }
 		}
 		else {
 			if(_raiseerror && _ss(_vm)->_compilererrorhandler) {
@@ -381,6 +480,20 @@ public:
 		}
 		return true;
 	}
+
+public:
+
+	bool Compile(SQObjectPtr &o)
+	{
+		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;
+		return CompileWithFuncstate(o, _fs, NULL, NULL);
+	}
 	void Statements()
 	{
 		while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) {
@@ -486,15 +599,17 @@ public:
 			Expect('=');
 			SQObjectPtr strongid = id;
 			CheckLocalNameScope(id, _scope.nested);
-			CheckConstsExists(strongid);
+			//CheckConstsExists(strongid);
 			SQObject val = ExpectScalar();
 			OptionalSemicolon();
 			ConstsNewSlot(strongid,SQObjectPtr(val));
-			strongid.Null();
-
 			}
 			break;
 
+		case TK_PRAGMA:
+		    Pragma();
+		    break;
+
 		default:
 			CommaExpr();
 			_fs->DiscardTarget();
@@ -602,8 +717,8 @@ public:
 					Error(_SC("can't 'create' a local slot"));
 				break;
 			case _SC('='): //ASSIGN
-                if(warningAssign) Warning(_SC("WARNING: making assignment at line %d:%d maybe is not what you want\n"),
-                                          _lex._currentline, _lex._currentcolumn);
+                if(warningAssign) Warning(_SC("%s:%d:%d warning making assignment, maybe it's not what you want\n"),
+                                          _stringval(_sourcename), _lex._currentline, _lex._currentcolumn);
 				switch(ds) {
 				case LOCAL:
 					{
@@ -1731,7 +1846,6 @@ error:
         //CheckLocalNameScope(id, _scope.nested);
 		SQObjectPtr strongid = id;
 		CheckLocalNameScope(id, _scope.nested);
-        CheckConstsExists(strongid);
 		SQObject table = _fs->CreateTable();
 		//_fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE);
 		SQInteger nval = 0;
@@ -1975,15 +2089,16 @@ private:
 	SQChar *_compilererror;
 	jmp_buf _errorjmp;
 	SQVM *_vm;
-	SQObjectPtrVec _scope_consts;
+	SQObjectPtrVec *_scope_consts;
 	SQObjectPtr _globals;
 	SQChar error_buf[MAX_COMPILER_ERROR_LEN];
+	SQInteger _nested_compile_count, _max_nested_includes;
 };
 
 bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out,
-             bool raiseerror, bool lineinfo, bool show_warnings)
+             bool raiseerror, bool lineinfo, bool show_warnings, SQInteger max_nested_includes)
 {
-	SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo, show_warnings);
+	SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo, show_warnings, max_nested_includes);
 	return p.Compile(out);
 }
 

+ 12 - 10
SquiLu/squirrel/sqcompiler.h

@@ -1,10 +1,10 @@
-/*	see copyright notice in squirrel.h */
-#ifndef _SQCOMPILER_H_
-#define _SQCOMPILER_H_
-
-struct SQVM;
+/*	see copyright notice in squirrel.h */
+#ifndef _SQCOMPILER_H_
+#define _SQCOMPILER_H_
 
-enum SQKeywordsEnum {
+struct SQVM;
+
+enum SQKeywordsEnum {
     TK_FIRST_ENUM_TOKEN = 258,
     /*
     the above token is only for internal purposes
@@ -114,14 +114,16 @@ enum SQKeywordsEnum {
     TK_BIT_XOR_EQ,
     TK_BIT_SHIFT_LEFT_EQ,
     TK_BIT_SHIFT_RIGHT_EQ,
+
+    TK_PRAGMA,
     /*
     the next token is only for internal purposes
     like calculate total enum_tokens = TK_LAST_ENUM_TOKEN - TK_FIRST_ENUM_TOKEN -1
     */
     TK_LAST_ENUM_TOKEN
 };
-
-typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s);
+
+typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s);
 bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out,
-             bool raiseerror, bool lineinfo, bool show_warnings);
-#endif //_SQCOMPILER_H_
+             bool raiseerror, bool lineinfo, bool show_warnings, SQInteger max_nested_includes=10);
+#endif //_SQCOMPILER_H_

+ 9 - 1
SquiLu/squirrel/sqlexer.cpp

@@ -191,7 +191,15 @@ SQInteger SQLexer::Lex()
 			NEXT();
 			_currentcolumn=1;
 			continue;
-		case _SC('#'): LexLineComment(); continue;
+		case _SC('#'):
+		    NEXT();
+		    if(CUR_CHAR == '!') //shell shebang
+            {
+                LexLineComment();
+                continue;
+            }
+            RETURN_TOKEN(TK_PRAGMA);
+            continue;
 		case _SC('/'):
 			NEXT();
 			switch(CUR_CHAR){