Browse Source

Module tweaks.

Mark Sibly 9 years ago
parent
commit
c53383850f

+ 8 - 3
modules/libc/libc.monkey2

@@ -91,17 +91,22 @@ Struct tm_t
 	Field tm_isdst:Int
 End
 
-Const CLOCKS_PER_SEC:Long="((bbLong)CLOCKS_PER_SEC)"
+Struct timeval
+	Field tv_sec:Long	'dodgy - should be time_t
+	Field tv_usec:Long	'dodyy - should be suseconds_t
+End
 
+'Note: clock() scary - pauses while process sleeps!
+Const CLOCKS_PER_SEC:Long="((bbLong)CLOCKS_PER_SEC)"
 Function clock:Long()="(bbLong)clock"
 
-'dodgy!
-Function tolong:long( timer:time_t )="bbLong"
+Function tolong:Long( timer:time_t )="bbLong"
 
 Function time:time_t( timer:time_t Ptr )
 Function localtime:tm_t Ptr( timer:time_t Ptr )
 Function gmtime:tm_t Ptr( timer:time_t Ptr )
 Function difftime:Double( endtime:time_t,starttime:time_t ) 
+Function gettimeofday:Int( tv:timeval Ptr,tz:Void Ptr )
 
 '***** unistd.h *****
 

+ 2 - 0
modules/libc/native/libc.cpp

@@ -24,6 +24,8 @@ int system_( const char *cmd ){
 
 #if _WIN32
 
+//	return system( cmd );
+
 	bbString tmp=BB_T( "cmd /S /C\"" )+BB_T( cmd )+BB_T( "\"" );
 
 	PROCESS_INFORMATION pi={0};

+ 1 - 0
modules/libc/native/libc.h

@@ -8,6 +8,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <dirent.h>
 #include <time.h>
 

+ 125 - 125
modules/litehtml/litehtml/src/web_color.cpp

@@ -1,28 +1,28 @@
-#include "html.h"
-#include "web_color.h"
-#include <string.h>
-
-litehtml::def_color litehtml::g_def_colors[] = 
-{
-	{_t("transparent"),_t("rgba(0, 0, 0, 0)")},
-	{_t("AliceBlue"),_t("#F0F8FF")},
-	{_t("AntiqueWhite"),_t("#FAEBD7")},
-	{_t("Aqua"),_t("#00FFFF")},
-	{_t("Aquamarine"),_t("#7FFFD4")},
-	{_t("Azure"),_t("#F0FFFF")},
-	{_t("Beige"),_t("#F5F5DC")},
-	{_t("Bisque"),_t("#FFE4C4")},
-	{_t("Black"),_t("#000000")},
-	{_t("BlanchedAlmond"),_t("#FFEBCD")},
-	{_t("Blue"),_t("#0000FF")},
-	{_t("BlueViolet"),_t("#8A2BE2")},
-	{_t("Brown"),_t("#A52A2A")},
-	{_t("BurlyWood"),_t("#DEB887")},
-	{_t("CadetBlue"),_t("#5F9EA0")},
-	{_t("Chartreuse"),_t("#7FFF00")},
-	{_t("Chocolate"),_t("#D2691E")},
-	{_t("Coral"),_t("#FF7F50")},
-	{_t("CornflowerBlue"),_t("#6495ED")},
+#include "html.h"
+#include "web_color.h"
+#include <string.h>
+
+litehtml::def_color litehtml::g_def_colors[] = 
+{
+	{_t("transparent"),_t("rgba(0, 0, 0, 0)")},
+	{_t("AliceBlue"),_t("#F0F8FF")},
+	{_t("AntiqueWhite"),_t("#FAEBD7")},
+	{_t("Aqua"),_t("#00FFFF")},
+	{_t("Aquamarine"),_t("#7FFFD4")},
+	{_t("Azure"),_t("#F0FFFF")},
+	{_t("Beige"),_t("#F5F5DC")},
+	{_t("Bisque"),_t("#FFE4C4")},
+	{_t("Black"),_t("#000000")},
+	{_t("BlanchedAlmond"),_t("#FFEBCD")},
+	{_t("Blue"),_t("#0000FF")},
+	{_t("BlueViolet"),_t("#8A2BE2")},
+	{_t("Brown"),_t("#A52A2A")},
+	{_t("BurlyWood"),_t("#DEB887")},
+	{_t("CadetBlue"),_t("#5F9EA0")},
+	{_t("Chartreuse"),_t("#7FFF00")},
+	{_t("Chocolate"),_t("#D2691E")},
+	{_t("Coral"),_t("#FF7F50")},
+	{_t("CornflowerBlue"),_t("#6495ED")},
 	{_t("Cornsilk"),_t("#FFF8DC")},
 	{_t("Crimson"),_t("#DC143C")},
 	{_t("Cyan"),_t("#00FFFF")},
@@ -149,103 +149,103 @@ litehtml::def_color litehtml::g_def_colors[] =
 	{_t("White"),_t("#FFFFFF")},
 	{_t("WhiteSmoke"),_t("#F5F5F5")},
 	{_t("Yellow"),_t("#FFFF00")},
-	{_t("YellowGreen"),_t("#9ACD32")},
-	{0,0}
-};
-
-
-litehtml::web_color litehtml::web_color::from_string( const tchar_t* str )
-{
-	if(!str)
-	{
-		return web_color(0, 0, 0);
-	}
-	if(str[0] == _t('#'))
-	{
-		tstring red		= _t("");
-		tstring green		= _t("");
-		tstring blue		= _t("");
-		if(t_strlen(str + 1) == 3)
-		{
-			red		+= str[1];
-			red		+= str[1];
-			green	+= str[2];
-			green	+= str[2];
-			blue	+= str[3];
-			blue	+= str[3];
-		} else if(t_strlen(str + 1) == 6)
-		{
-			red		+= str[1];
-			red		+= str[2];
-			green	+= str[3];
-			green	+= str[4];
-			blue	+= str[5];
-			blue	+= str[6];
-		}
-		tchar_t* sss = 0;
-		web_color clr;
-		clr.red		= (byte) t_strtol(red.c_str(),	&sss, 16);
-		clr.green	= (byte) t_strtol(green.c_str(),	&sss, 16);
-		clr.blue	= (byte) t_strtol(blue.c_str(),	&sss, 16);
-		return clr;
-	} else if(!t_strncmp(str, _t("rgb"), 3))
-	{
-		tstring s = str;
-
-		tstring::size_type pos = s.find_first_of(_t("("));
-		if(pos != tstring::npos)
-		{
-			s.erase(s.begin(), s.begin() + pos + 1);
-		}
-		pos = s.find_last_of(_t(")"));
-		if(pos != tstring::npos)
-		{
-			s.erase(s.begin() + pos, s.end());
-		}
-
-		std::vector<tstring> tokens;
-		split_string(s, tokens, _t(", \t"));
-
-		web_color clr;
-
-		if(tokens.size() >= 1)	clr.red		= (byte) t_atoi(tokens[0].c_str());
-		if(tokens.size() >= 2)	clr.green	= (byte) t_atoi(tokens[1].c_str());
-		if(tokens.size() >= 3)	clr.blue	= (byte) t_atoi(tokens[2].c_str());
-		if(tokens.size() >= 4)	clr.alpha	= (byte) (t_strtod(tokens[3].c_str(), 0) * 255.0);
-
-		return clr;
-	} else
-	{
-		const tchar_t* rgb = resolve_name(str);
-		if(rgb)
-		{
-			return from_string(rgb);
-		}
-	}
-	return web_color(0, 0, 0);
-}
-
-const litehtml::tchar_t* litehtml::web_color::resolve_name( const tchar_t* name )
-{
-	for(int i=0; g_def_colors[i].name; i++)
-	{
-		if(!t_strcasecmp(name, g_def_colors[i].name))
-		{
-			return g_def_colors[i].rgb;
-		}
-	}
-	return 0;
-}
-
-bool litehtml::web_color::is_color( const tchar_t* str )
-{
-	if(!t_strncasecmp(str, _t("rgb"), 3) || str[0] == _t('#'))
-	{
-		return true;
-	}
-	if(resolve_name(str))
-	{
-		return true;
-	}
-	return false;
-}
+	{_t("YellowGreen"),_t("#9ACD32")},
+	{0,0}
+};
+
+
+litehtml::web_color litehtml::web_color::from_string( const tchar_t* str )
+{
+	if(!str)
+	{
+		return web_color(0, 0, 0);
+	}
+	if(str[0] == _t('#'))
+	{
+		tstring red		= _t("");
+		tstring green		= _t("");
+		tstring blue		= _t("");
+		if(t_strlen(str + 1) == 3)
+		{
+			red		+= str[1];
+			red		+= str[1];
+			green	+= str[2];
+			green	+= str[2];
+			blue	+= str[3];
+			blue	+= str[3];
+		} else if(t_strlen(str + 1) == 6)
+		{
+			red		+= str[1];
+			red		+= str[2];
+			green	+= str[3];
+			green	+= str[4];
+			blue	+= str[5];
+			blue	+= str[6];
+		}
+		tchar_t* sss = 0;
+		web_color clr;
+		clr.red		= (byte) t_strtol(red.c_str(),	&sss, 16);
+		clr.green	= (byte) t_strtol(green.c_str(),	&sss, 16);
+		clr.blue	= (byte) t_strtol(blue.c_str(),	&sss, 16);
+		return clr;
+	} else if(!t_strncmp(str, _t("rgb"), 3))
+	{
+		tstring s = str;
+
+		tstring::size_type pos = s.find_first_of(_t("("));
+		if(pos != tstring::npos)
+		{
+			s.erase(s.begin(), s.begin() + pos + 1);
+		}
+		pos = s.find_last_of(_t(")"));
+		if(pos != tstring::npos)
+		{
+			s.erase(s.begin() + pos, s.end());
+		}
+
+		std::vector<tstring> tokens;
+		split_string(s, tokens, _t(", \t"));
+
+		web_color clr;
+
+		if(tokens.size() >= 1)	clr.red		= (byte) t_atoi(tokens[0].c_str());
+		if(tokens.size() >= 2)	clr.green	= (byte) t_atoi(tokens[1].c_str());
+		if(tokens.size() >= 3)	clr.blue	= (byte) t_atoi(tokens[2].c_str());
+		if(tokens.size() >= 4)	clr.alpha	= (byte) (t_strtod(tokens[3].c_str(), 0) * 255.0);
+
+		return clr;
+	} else
+	{
+		const tchar_t* rgb = resolve_name(str);
+		if(rgb)
+		{
+			return from_string(rgb);
+		}
+	}
+	return web_color(0, 0, 0);
+}
+
+const litehtml::tchar_t* litehtml::web_color::resolve_name( const tchar_t* name )
+{
+	for(int i=0; g_def_colors[i].name; i++)
+	{
+		if(!t_strcasecmp(name, g_def_colors[i].name))
+		{
+			return g_def_colors[i].rgb;
+		}
+	}
+	return 0;
+}
+
+bool litehtml::web_color::is_color( const tchar_t* str )
+{
+	if(!t_strncasecmp(str, _t("rgb"), 3) || str[0] == _t('#'))
+	{
+		return true;
+	}
+	if(resolve_name(str))
+	{
+		return true;
+	}
+	return false;
+}

BIN
modules/litehtml/native/litehtml_glue.h


+ 2 - 1
modules/modules.txt

@@ -11,5 +11,6 @@ std
 gles20
 sdl2
 sdl2-mixer
-mojo2
+freetype
+mojo
 litehtml

+ 11 - 0
modules/monkey/native/bbarray.h

@@ -3,6 +3,7 @@
 #define BB_ARRAY_H
 
 #include "bbgc.h"
+#include "bbdebug.h"
 #include "bbassert.h"
 
 template<class T,int D> class bbArray : public bbGCNode{
@@ -171,4 +172,14 @@ template<class T,int D> class bbArray : public bbGCNode{
 	}
 };
 
+template<class T,int N> bbDBType *bbDBTypeOf( bbArray<T,N>** ){
+	struct type : public bbDBType{
+		bbDBType *elemType=bbDBTypeOf<T>();
+		int rank=N;
+		bbString name(){ return elemType->name()+BB_T("[]"); }
+	};
+	static type _type;
+	return &_type;
+}
+
 #endif

+ 99 - 34
modules/monkey/native/bbdebug.cpp

@@ -2,28 +2,57 @@
 #include "bbdebug.h"
 #include "bbarray.h"
 
+#if _WIN32
+#include <windows.h>
+#else
+#include <signal.h>
+#endif
+
 namespace bbDB{
 
-	bbDBFrame *frames;
-	bbDBVar localsBuf[1024];
-	bbDBVar *locals=localsBuf;
-	int srcpos;
-	bool stopper;
+	bbDBContext *currentContext;
+	
+#if _WIN32
+	BOOL WINAPI stopHandler( DWORD dwCtrlType ){
+		if( dwCtrlType==CTRL_BREAK_EVENT ){
+//			printf( "CTRL_BREAK_EVENT\n" );fflush( stdout );
+			currentContext->stopped=0;
+			return TRUE;
+		}
+		return FALSE;
+	}
+#else
+	void sighandler( int sig ){
+//		printf( "SIGTSTP\n" );fflush( stdout );
+		currentContext->stopped=0;
+	}
+#endif
 	
-	void dumpStack(){
+	void init(){
 	
-		bbDBVar *ev=locals;
+		currentContext=new bbDBContext;
+		currentContext->init();
 		
-		for( bbDBFrame *f=frames;f;f=f->succ ){
+#if _WIN32
+		SetConsoleCtrlHandler( stopHandler,TRUE );
+#else		
+		signal( SIGTSTP,sighandler );
+#endif
+	}
+	
+	void emitStack(){
+		bbDBVar *ev=currentContext->locals;
+		
+		for( bbDBFrame *f=currentContext->frames;f;f=f->succ ){
 
-			printf( "%s\n",f->decl );
+			printf( ">%s;%s;%i\n",f->decl,f->srcFile,f->srcPos );
 			
 			for( bbDBVar *v=f->locals;v!=ev;++v ){
 				char id[64],type[64],value[128];
-				strcpy( id,v->ident().c_str() );
-				strcpy( type,v->typeName().c_str() );
-				strcpy( value,v->getValue().c_str() );
-				printf( "   %s:%s=%s\n",id,type,value );
+				strcpy( id,v->name );
+				strcpy( type,v->type->name().c_str() );
+				strcpy( value,v->type->value( v->var ).c_str() );
+				printf( "%s:%s=%s\n",id,type,value );
 			}
 
 			ev=f->locals;
@@ -31,24 +60,46 @@ namespace bbDB{
 	}
 	
 	void stop(){
-		dumpStack();
+
+		currentContext->stopped=0;
 	}
 	
 	void stopped(){
-		dumpStack();
-		stopper=false;
+
+		printf( "{{!DEBUG!}}\n" );
+		
+		emitStack();
+
+		printf( "\n" );
+		fflush( stdout );
+		
+		char buf[256];
+		char *e=fgets( buf,256,stdin );
+		if( !e ) exit( -1 );
+		
+		switch( e[0] ){
+		case 's':currentContext->stopped=0;return;
+		case 'e':currentContext->stopped=1;return;
+		case 'l':currentContext->stopped=-1;return;
+		case 'r':currentContext->stopped=-0x10000000;return;
+		case 'q':
+			printf( "Quitting!!!!!\n" );fflush( stdout );
+			exit( 0 );
+		}
+		printf( "???? %s\n",buf );fflush( stdout );
+		exit( -1 );
 	}
 	
 	bbArray<bbString> *stack(){
 	
 		int n=0;
-		for( bbDBFrame *frame=frames;frame;frame=frame->succ ) ++n;
+		for( bbDBFrame *frame=currentContext->frames;frame;frame=frame->succ ) ++n;
 		
 		//TODO: Fix GC issues! Can't have a free local like this in case bbString ctors cause gc sweep!!!!
 		bbArray<bbString> *st=bbArray<bbString>::create( n );
 		
 		int i=0;
-		for( bbDBFrame *frame=frames;frame;frame=frame->succ ){
+		for( bbDBFrame *frame=currentContext->frames;frame;frame=frame->succ ){
 			st->at( i++ )=BB_T( frame->srcFile )+" ["+bbString( frame->srcPos>>12 )+"] "+frame->decl;
 		}
 		
@@ -56,25 +107,39 @@ namespace bbDB{
 	}
 }
 
-bbString bbDBVar::ident()const{
-	const char *p=strchr( decl,':' );
-	return bbString( decl,p-decl );
+void bbDBContext::init(){
+	if( !localsBuf ) localsBuf=new bbDBVar[16384];
+	locals=localsBuf;
+	frames=nullptr;
+	stopped=0;
 }
 
-bbString bbDBVar::typeName()const{
-	const char *p=strchr( decl,':' )+1;
-	return bbTypeName( p );
+bbDBContext::~bbDBContext(){
+	delete[] localsBuf;
 }
 
-bbString bbDBVar::getValue()const{
-	const char *p=strchr( decl,':' )+1;
-	switch( *p ){
-	case 'i':return bbString( *(int*)ptr );
-	case 'f':return bbString( *(float*)ptr );
-	case 's':return "\""+bbString( *(bbString*)ptr )+"\"";
-	case 'A':return "[...]";
-	}
-	
-	return "????";
+template<> bbDBType *bbDBTypeOf( void* ){
+	struct type : public bbDBType{
+		bbString name(){ return "Void"; }
+	};
+	static type _type;
+	return &_type;
 }
 
+template<> bbDBType *bbDBTypeOf( bbInt* ){
+	struct type : public bbDBType{
+		bbString name(){ return "Int"; }
+		bbString value( void *var ){ return *(bbInt*)var; }
+	};
+	static type _type;
+	return &_type;
+}
+
+template<> bbDBType *bbDBTypeOf( bbString* ){
+	struct type : public bbDBType{
+		bbString name(){ return "String"; }
+		bbString value( void *var ){ return BB_T("\"")+*(bbString*)var+BB_T( "\"" ); }
+	};
+	static type _type;
+	return &_type;
+}

+ 68 - 21
modules/monkey/native/bbdebug.h

@@ -4,23 +4,59 @@
 
 #include "bbstring.h"
 
+struct bbDBFiber;
+
 struct bbDBFrame;
 
+struct bbDBVar;
+
+//subclasses can't add data!
+struct bbDBType{
+	virtual bbString name(){ return "<?>"; }
+	virtual bbString value( void *var ){ return "?????"; }
+//	virtual void members( bbDBVar *var,bbDBVar **vars ){}
+};
+
+template<class T> bbDBType *bbDBTypeOf( T* ){
+	static bbDBType _type;
+	return &_type;
+}
+
+template<class T> bbDBType *bbDBTypeOf( T** ){
+	struct type : public bbDBType{
+		bbString name(){ return bbDBTypeOf( (T*)0 )->name()+" Ptr"; }
+	};
+	static type _type;
+	return &_type;
+}
+
+template<class T> bbDBType *bbDBTypeOf(){
+	return bbDBTypeOf( (T*)0 );
+}
+
 struct bbDBVar{
-	const char *decl;
-	void *ptr;
-	
-	bbString ident()const;
+	const char *name;
+	bbDBType *type;
+	void *var;
+};
+
+struct bbDBContext{
+
+	bbDBFrame *frames=nullptr;
+	bbDBVar *localsBuf=nullptr;
+	bbDBVar *locals=nullptr;
+	int stopped;
 	
-	bbString typeName()const;
+	~bbDBContext();
 	
-	bbString getValue()const;
+	void init();
 };
 
 namespace bbDB{
-	extern bbDBFrame *frames;
-	extern bbDBVar *locals;
-	extern bool stopper;
+
+	extern bbDBContext *currentContext;
+	
+	void init();
 	
 	void stop();
 	
@@ -36,34 +72,45 @@ struct bbDBFrame{
 	const char *srcFile;
 	int srcPos;
 	
-	bbDBFrame( const char *decl,const char *srcFile ):succ( bbDB::frames ),locals( bbDB::locals ),decl( decl ),srcFile( srcFile ){
-		bbDB::frames=this;
+	bbDBFrame( const char *decl,const char *srcFile ):succ( bbDB::currentContext->frames ),locals( bbDB::currentContext->locals ),decl( decl ),srcFile( srcFile ){
+		bbDB::currentContext->frames=this;
+		--bbDB::currentContext->stopped;
 	}
 	
 	~bbDBFrame(){
-		bbDB::locals=locals;
-		bbDB::frames=succ;
+		++bbDB::currentContext->stopped;
+		bbDB::currentContext->locals=locals;
+		bbDB::currentContext->frames=succ;
 	}
 };
 
 struct bbDBBlock{
 	bbDBVar *locals;
-	bbDBBlock():locals( bbDB::locals ){
+	bbDBBlock():locals( bbDB::currentContext->locals ){
+		--bbDB::currentContext->stopped;
 	}
 	~bbDBBlock(){
-		bbDB::locals=locals;
+		++bbDB::currentContext->stopped;
+		bbDB::currentContext->locals=locals;
 	}
 };
 
 inline void bbDBStmt( int srcPos ){
-	bbDB::frames->srcPos=srcPos;
-	if( bbDB::stopper ) bbDB::stopped();
+	bbDB::currentContext->frames->srcPos=srcPos;
+	if( bbDB::currentContext->stopped>=0 ) bbDB::stopped();
 }
 
-inline void bbDBLocal( const char *decl,void *ptr ){
-	bbDB::locals->decl=decl;
-	bbDB::locals->ptr=ptr;
-	++bbDB::locals;
+template<class T> void bbDBLocal( const char *name,T *var ){
+	bbDB::currentContext->locals->name=name;
+	bbDB::currentContext->locals->type=bbDBTypeOf<T>();
+	bbDB::currentContext->locals->var=var;
+	++bbDB::currentContext->locals;
 }
 
+template<> bbDBType *bbDBTypeOf( void* );
+
+template<> bbDBType *bbDBTypeOf( bbInt* );
+
+template<> bbDBType *bbDBTypeOf( bbString* );
+
 #endif

+ 10 - 0
modules/monkey/native/bbfunction.h

@@ -4,6 +4,7 @@
 
 #include "bbtypes.h"
 #include "bbgc.h"
+#include "bbdebug.h"
 
 template<class T> class bbFunction;
 
@@ -257,4 +258,13 @@ template<class R,class...A> int bbCompare( const bbFunction<R(A...)> &x,const bb
 	return x._rep->compare( y._rep );
 }
 
+template<class R,class...A> struct bbDBFuncType : public bbDBType{
+	bbString name(){ return bbDBTypeOf<R>()->name()+"(...)"; }
+};
+
+template<class R,class...A> bbDBType *bbDBTypeOf( bbFunction<R(A...)>* ){
+	static bbDBFuncType<R,A...> _type;
+	return &_type;
+}
+
 #endif

+ 27 - 27
modules/monkey/native/bbgc.cpp

@@ -86,12 +86,6 @@ namespace bbGC{
 		
 #if BBGC_DEBUG
 
-//		p->~bbGCNode();
-			
-//		size_t sz=(size_t)&((bbGCNode*)0)->flags;
-		
-//		memset( (char*)p+sz,0xaa,size-sz );
-		
 		p->flags=3;
 
 #else
@@ -106,7 +100,8 @@ namespace bbGC{
 		while( freeList.succ!=&freeList ){
 		
 			bbGCNode *p=freeList.succ;
-			size_t psize=p->gcSize();
+			
+			size_t psize=bbMallocSize( p );
 			
 			remove( p );
 			destroy( p );
@@ -117,13 +112,14 @@ namespace bbGC{
 	}
 	
 	void mark( bbGCNode *p ){
-		if( !p || (p->flags&3)==markedBit ) return;
+		if( !p || p->flags==markedBit ) return;
 		
 		remove( p );
 		insert( p,markedList );
 		
-		p->flags=(p->flags & ~3)|markedBit;
-		markedBytes+=p->gcSize();
+		p->flags=markedBit;
+		
+		markedBytes+=bbMallocSize( p );
 
 		p->gcMark();
 	}
@@ -165,7 +161,8 @@ namespace bbGC{
 			markQueue=p->succ;
 			
 			insert( p,markedList );
-			markedBytes+=p->gcSize();
+			
+			markedBytes+=bbMallocSize( p );
 			
 			p->gcMark();
 		}
@@ -187,7 +184,7 @@ namespace bbGC{
 			freeList.pred->succ=unmarkedList->succ;
 			freeList.pred=unmarkedList->pred;
 			
-			//clear unmarked
+			//clear unmarkedmpor
 			unmarkedList->succ=unmarkedList->pred=unmarkedList;
 		}
 		
@@ -196,43 +193,46 @@ namespace bbGC{
 		
 		unmarkedBytes=markedBytes;
 
-		markedBytes=allocedBytes=0;
+		markedBytes=0;
+		
+		allocedBytes=0;
 		
 		markRoots();
 	}
-
+	
 	bbGCNode *alloc( size_t size ){
 	
-		size=(size+7)&~7;
-		
-		allocedBytes+=size;
-		
 		if( allocedBytes>=BBGC_TRIGGER ){
-		
+
 			sweep();
-			
+		
 #if BBGC_AGGRESSIVE
 			reclaim();
 #endif
 		}else{
 		
 #if BBGC_INCREMENTAL
-			size_t tomark=double(allocedBytes) / double(BBGC_TRIGGER) * double(unmarkedBytes+allocedBytes);
 
+//			size_t tomark=double( allocedBytes ) / double( BBGC_TRIGGER ) * double( unmarkedBytes + allocedBytes );
+			size_t tomark=double( allocedBytes ) / double( BBGC_TRIGGER ) * double( unmarkedBytes + BBGC_TRIGGER );
+	
 			markQueued( tomark );
 #endif
 		}
-		
-#if !BBGC_AGGRESSIVE
-		reclaim( size );
-#endif
-
+	
 		bbGCNode *p=(bbGCNode*)bbMalloc( size );
 		
 		*((void**)p)=(void*)0xcafebabe;
 		
-		p->flags=size;
+		p->flags=0;
+		
+		size=bbMallocSize( p );
+		
+		allocedBytes+=size;
 		
+#if !BBGC_AGGRESSIVE
+		reclaim( size );
+#endif
 		return p;
 	}
 	

+ 5 - 9
modules/monkey/native/bbgc.h

@@ -26,7 +26,7 @@
 
 #if BBGC_DEBUG
 #define BBGC_VALIDATE( P ) \
-	if( (P) && ((P)->flags&3)==3 ){ \
+	if( (P) && (P)->flags==3 ){ \
 		printf( "Attempt to use deleted object %p of type '%s'\n",(P),(P)->typeName() ); \
 		fflush( stdout ); \
 		abort(); \
@@ -71,10 +71,6 @@ struct bbGCNode{
 	virtual ~bbGCNode(){
 	}
 
-	size_t gcSize(){
-		return flags&~7;
-	}
-	
 	virtual void gcMark(){
 	}
 	
@@ -149,13 +145,13 @@ namespace bbGC{
 	inline void enqueue( bbGCNode *p ){
 		BBGC_VALIDATE( p )
 
-		if( !p || (p->flags&3)!=unmarkedBit ) return;
+		if( !p || p->flags!=unmarkedBit ) return;
 		
 		remove( p );
 		p->succ=markQueue;
 		markQueue=p;
 		
-		p->flags=(p->flags & ~3)|markedBit;
+		p->flags=markedBit;
 	}
 	
 	inline void beginCtor( bbGCNode *p ){
@@ -168,9 +164,9 @@ namespace bbGC{
 #if BBGC_INCREMENTAL
 		p->succ=markQueue;
 		markQueue=p;
-		p->flags|=markedBit;
+		p->flags=markedBit;
 #else
-		p->flags|=unmarkedBit;
+		p->flags=unmarkedBit;
 		insert( p,unmarkedList );
 #endif
 	}

+ 46 - 46
modules/monkey/native/bbmemory.cpp

@@ -3,72 +3,72 @@
 
 #include <cstring>
 
-static size_t malloced;
+namespace{
 
-static size_t max_malloced;
+	void *pools[32];
+	
+	unsigned char *poolBuf;
+	size_t poolBufSize;
+}
 
-static void *pools[256>>3];
+size_t bbMallocedBytes;
 
 void *bbMalloc( size_t size ){
 
-	size+=sizeof(size_t);
+	size=(size+sizeof( size_t )+7)&~7;
 	
-	size=(size+7)&~7;
+	void *p;
 	
-	size_t *p;
-	
-/*	
-	if( size<256 && pools[size>>3] ){
-		p=(size_t*)pools[size>>3];
-		pools[size>>3]=*((void**)p);
+	if( size<256 ){
+		if( pools[size>>3] ){
+			p=pools[size>>3];
+			pools[size>>3]=*(void**)p;
+		}else{
+			if( size>poolBufSize ){
+				if( poolBufSize ){
+					*(void**)poolBuf=pools[poolBufSize>>3];
+					pools[poolBufSize>>3]=poolBuf;
+				}
+				poolBufSize=65536;
+				poolBuf=(unsigned char*)::malloc( poolBufSize );
+			}
+			p=poolBuf;
+			poolBuf+=size;
+			poolBufSize-=size;
+		}
 	}else{
-		p=(size_t*)malloc( size );
+		p=::malloc( size );
 	}
-*/
-	p=(size_t*)malloc( size );
-
-	malloced+=size;
-	
-	memset( p,0,size );
 	
-	*p=size;
-	
-	/*
-	if( malloced>max_malloced ){
-		max_malloced=malloced;
-		printf( "Max malloced:%ul\n",max_malloced );
-		fflush( stdout );
-	}
-	*/
+	bbMallocedBytes+=size;
 
-	return p+1;
+	size_t *q=(size_t*)p;
+	*q++=size;
+	return q;
 }
 
-size_t bbMallocSize( const void *q ){
-	return *((const size_t*)q-1);
-}
+size_t bbMallocSize( void *p ){
 
-void bbFree( void *q ){
+	if( p ) return *((size_t*)p-1);
+	
+	return 0;
+}
 
-	if( !q ) return;
+void bbFree( void *p ){
 
-	size_t *p=(size_t*)q-1;
-	
-	size_t size=*p;
+	if( !p ) return;
 	
-	free( p );
+	size_t *q=(size_t*)p-1;
 	
-	malloced-=size;
+	size_t size=*q;
 	
-	/*
+	bbMallocedBytes-=size;
+
 	if( size<256 ){
-		*((void**)p)=pools[size>>3];
-		pools[size>>3]=p;
+		*(void**)q=pools[size>>3];
+		pools[size>>3]=q;
 	}else{
-		free( p );
+		::free( q );
 	}
-	*/
-	
-//	printf( "Freed:%p, size=%i, total=%i\n",p,size,malloced );
-	//fflush( stdout );
+
 }

+ 3 - 1
modules/monkey/native/bbmemory.h

@@ -4,9 +4,11 @@
 
 #include "bbtypes.h"
 
+extern size_t bbMallocedBytes;
+
 void *bbMalloc( size_t size );
 
-size_t bbMallocSize( const void *p );
+size_t bbMallocSize( void *p );
 
 void bbFree( void *p );
 

+ 3 - 1
modules/monkey/native/bbmonkey.cpp

@@ -90,10 +90,12 @@ int main( int argc,char **argv ){
 
 //	printf( "bbMain() : sizeof(bbBool)=%i sizeof(bbByte)=%i sizeof(bbShort)=%i sizeof(bbInt)=%i, sizeof(bbLong)=%i, sizeof(bbChar)=%i sizeof(void*)=%i, sizeof(size_t)=%i\n",(int)sizeof(bbBool),(int)sizeof(bbByte),(int)sizeof(bbShort),(int)sizeof(bbInt),(int)sizeof(bbLong),(int)sizeof(bbChar),(int)sizeof(void*),(int)sizeof(size_t) );
 //	fflush( stdout );
-	
+
 	try{
 	
 		bbGC::init();
+		
+		bbDB::init();
 
 		{		
 			bbDBFrame( "_void()","" );

+ 11 - 0
modules/monkey/native/bbobject.cpp

@@ -3,6 +3,8 @@
 #include "bbdebug.h"
 #include "bbarray.h"
 
+bbNullCtor_t bbNullCtor;
+
 bbException::bbException(){
 
 	_debugStack=bbDB::stack();
@@ -12,3 +14,12 @@ bbException::bbException( bbString message ):bbException(){
 
 	_message=message;
 }
+
+template<> bbDBType *bbDBTypeOf( bbObject** ){
+	struct type : public bbDBType{
+		bbString name(){ return "Object"; }
+	};
+	static type _type;
+	return &_type;
+}
+

+ 19 - 0
modules/monkey/native/bbobject.h

@@ -4,6 +4,7 @@
 
 #include "bbgc.h"
 #include "bbstring.h"
+#include "bbdebug.h"
 
 struct bbObject : public bbGCNode{
 
@@ -17,6 +18,9 @@ struct bbObject : public bbGCNode{
 	virtual const char *typeName()const{
 		return "monkey.Object";
 	}
+	
+	virtual void debugVars( bbDBVar *p )const{
+	}
 
 	void *operator new( size_t size ){
 		return bbGC::alloc( size );
@@ -59,10 +63,25 @@ struct bbInterface{
 	}
 };
 
+struct bbNullCtor_t{
+};
+
+extern bbNullCtor_t bbNullCtor;
+
 template<class T,class...A> T *bbGCNew( A...a ){
 	T *p=new T( a... );
 	bbGC::endCtor( p );
 	return p;
 }
 
+inline bbDBType *bbDBTypeOf( const bbObject*& ){
+	struct type : public bbDBType{
+		bbString name(){ return "Object"; }
+	};
+	static type _type;
+	return &_type;
+}
+
+template<> bbDBType *bbDBTypeOf( bbObject** );
+
 #endif

+ 230 - 178
modules/std/collections/list.monkey2

@@ -2,9 +2,7 @@
 Namespace std.collections
 
 Alias IntList:List<Int>
-
 Alias FloatList:List<Float>
-
 Alias StringList:List<String>
 
 #rem monkeydoc The List class.
@@ -12,8 +10,8 @@ Alias StringList:List<String>
 #end
 Class List<T> Implements IContainer<T>
 
-	Private
-	
+	#rem monkeydoc The List.Node class.
+	#end
 	Class Node
 	
 		Private
@@ -23,28 +21,95 @@ Class List<T> Implements IContainer<T>
 		Field _value:T
 		
 		Public
-		
+
+		#rem monkeydoc Creates a new node not in any list.
+		#end		
 		Method New( value:T )
 			_value=value
+			_succ=Self
+			_pred=Self
 		End
 		
-		Property Value:T()
-			Return _value
+		#rem monkeydoc Creates a new node with the given successor node.
+		
+		Warning! No error checking is performed!
+		
+		This method should not be used while iterating over the list containing `succ`.
+		
+		#endif
+		Method New( value:T,succ:Node )
+			_value=value
+			_succ=succ
+			_pred=succ._pred
+			_pred._succ=Self
+			succ._pred=Self
 		End
 		
+		#rem monkeydoc Gets the node after this node.
+		#end
 		Property Succ:Node()
 			Return _succ
 		End
 		
+		#rem monkeydoc Gets the node before this node.
+		#end
 		Property Pred:Node()
 			Return _pred
 		End
+
+		#rem monkeydoc Gets the value contained in this node.
+		#end		
+		Property Value:T()
+			Return _value
+		Setter( value:T )
+			_value=value
+		End
+		
+		#rem monkeydoc Inserts the node before another node.
+		
+		Warning! No error checking is performed!
+		
+		This method should not be used while iterating over the list containing `node`.
+		
+		#end
+		Method InsertBefore( node:Node )
+			_succ=node
+			_pred=node._pred
+			_pred._succ=Self
+			node._pred=Self
+		End
+		
+		#rem monkeydoc Inserts the node after another node.
+		
+		Warning! No error checking is performed! 
+		
+		This method should not be used while iterating over the list containing `node`.
+		
+		#end
+		Method InsertAfter( node:Node )
+			_pred=node
+			_succ=node._succ
+			_succ._pred=Self
+			node._succ=Self
+		End
+		
+		#rem monkeydoc Removes this node.
+		
+		Warning! No error checking is performed!
+		
+		This method should not be used while iterating over the list containing this node.
+		
+		#end
+		Method Remove()
+			_succ._pred=_pred
+			_pred._succ=_succ
+		End
 		
 	End
 	
-	Public
-	
-	Struct Iterator 'Implements IIterator<T>
+	#rem monkeydoc The List.Iterator struct.
+	#end
+	Struct Iterator
 	
 		Private
 	
@@ -62,15 +127,19 @@ Class List<T> Implements IContainer<T>
 		
 		Public
 		
+		#rem monkeydoc Creates a new iterator.
+		#end
 		Method New( list:List,node:Node )
 			_list=list
 			_node=node
 			_seq=list._seq
 		End
-		
+
+		#rem monkeydoc Checks whether the iterator has reached the end of the list.
+		#end
 		Property AtEnd:Bool()
 			AssertSeq()
-			Return _node=Null
+			Return _node=_list._head
 		End
 		
 		#rem monkeydoc @hidden
@@ -79,6 +148,8 @@ Class List<T> Implements IContainer<T>
 			Return Not AtEnd
 		End
 		
+		#rem monkeydoc The value contained in the node pointed to by the iterator.
+		#end
 		Property Current:T()
 			AssertCurrent()
 			Return _node._value
@@ -88,60 +159,91 @@ Class List<T> Implements IContainer<T>
 			_node._value=current
 		End
 		
+		#rem monkeydoc Bumps the iterator so it points to the next node in the list.
+		#end
 		Method Bump()
 			AssertCurrent()
 			_node=_node._succ
 		End
 		
+		#rem monkeydoc Safely erases the node referenced by the iterator.
+		
+		After calling this method, the iterator will point to the node after the removed node.
+		
+		Therefore, if you are manually iterating through a list you should not call [[Bump]] after calling this method or you
+		will end up skipping a node.
+		
+		#end
 		Method Erase()
 			AssertSeq()
-			_node=_list.Erase( _node )
+			_node=_node._succ
+			_node._pred.Remove()
+			_list._seq+=1
 			_seq=_list._seq
 		End
 		
+		#rem monkeydoc Safely insert a value before the iterator.
+
+		After calling this method, the iterator will point to the newly added node.
+		
+		#end
 		Method Insert( value:T )
 			AssertSeq()
-			_node=_list.Insert( _node,value )
+			_node=New Node( value,_node )
+			_list._seq+=1
 			_seq=_list._seq
 		End
 	End
 	
 	Private
 	
-	Field _first:Node
-	Field _last:Node
-	Field _length:Int
+'	Field _head:=New Node	'FIXME, causes internal compiler error...
+	Field _head:Node
 	Field _seq:Int
 	
 	Public
 	
-	#rem monkeydoc Creates a new empty list.
+	#rem monkeydoc Creates a new list.
+	
+	New() create a new empty list.
+	
+	New( T[] ) creates a new list with the elements of an array.
+	
+	New( List<T> ) creates a new list with the contents of another list.
+	
+	New( Stack<T> ) create a new list the contents of a stack.
+	
+	@param values An existing array, list or stack.
+	
 	#end
 	Method New()
+		_head=New Node( Null )
 	End
 
-	#rem monkeydoc Creates a new list with the contents of an array.
+	Method New( values:T[] )
+		Self.New()
+		AddAll( values )
+	End
 	
-	@param data The array to create the list with.
+	Method New( values:Stack<T> )
+		Self.New()
+		AddAll( values )
+	End
 	
-	#end
-	Method New( values:T[] )
+	Method New( values:List<T> )
+		Self.New()
 		AddAll( values )
 	End
 	
-	#rem monkeydoc Gets an iterator to the list.
-
-	@return A list iterator.
+	#rem monkeydoc Gets an iterator for all nodes in the list.
 	
-	#end
-'	Method GetIterator:Iterator()
-'		Return New Iterator( Self,_first )
-'	End
+	Returns an iterator suitable for use with [[Eachin]], or for manual iteration.
+	
+	@return A list iterator.
 	
-	#rem monkeydoc @hidden
 	#end
 	Method All:Iterator()
-		Return New Iterator( Self,_first )
+		Return New Iterator( Self,_head._succ )
 	End
 	
 	#rem monkeydoc Checks whether the list is empty.
@@ -150,18 +252,24 @@ Class List<T> Implements IContainer<T>
 	
 	#end
 	Property Empty:Bool()
-		Return _length=0
+		Return _head._succ=_head
 	End
 	
-	#rem monkeydoc Gets the number of values in the list.
+	#rem monkeydoc Counts the number of values in the list.
+	
+	Note: This method can be slow when used with large lists, as it must visit each value. If you just
+	want to know whether a list is empty or not, use [[Empty]] instead.
 
 	@return The number of values in the list.
 	
-	@see Empty
-	
 	#end
-	Property Length:Int()
-		Return _length
+	Method Count:Int()
+		Local node:=_head._succ,n:=0
+		While node<>_head
+			node=node._succ
+			n+=1
+		Wend
+		Return n
 	End
 	
 	#rem monkeydoc Converts the list to an array.
@@ -170,8 +278,9 @@ Class List<T> Implements IContainer<T>
 	
 	#end
 	Method ToArray:T[]()
-		Local data:=New T[_length],node:=_first
-		For Local i:=0 Until _length
+		Local n:=Count()
+		Local data:=New T[n],node:=_head._succ
+		For Local i:=0 Until n
 			data[i]=node._value
 			node=node._succ
 		Next
@@ -187,7 +296,8 @@ Class List<T> Implements IContainer<T>
 	#end
 	Property First:T()
 		DebugAssert( Not Empty )
-		Return _first._value
+		
+		Return _head._succ._value
 	End
 	
 	#rem monkeydoc Gets the last value in the list
@@ -199,16 +309,16 @@ Class List<T> Implements IContainer<T>
 	#end
 	Property Last:T()
 		DebugAssert( Not Empty )
-		Return _last._value
+		
+		Return _head._pred._value
 	End
 	
 	#rem monkeydoc Removes all values from the list.
 	
 	#end
 	Method Clear()
-		_first=Null
-		_last=Null
-		_length=0
+		_head._succ=_head
+		_head._pred=_head
 		_seq+=1
 	End
 	
@@ -216,38 +326,26 @@ Class List<T> Implements IContainer<T>
 	
 	@param value The value to add to the list.
 	
-	@return The new node containing the value.
+	@return A new node containing the value.
 
 	#end
-	Method AddFirst( value:T )
-		Local node:=New Node( value )
-		If _first
-			node._succ=_first
-			_first._pred=node
-		Else
-			_last=node
-		Endif
-		_first=node
-		_length+=1
+	Method AddFirst:Node( value:T )
+		Local node:=New Node( value,_head._succ )
 		_seq+=1
+		Return node
 	End
 	
 	#rem monkeydoc Adds a value to the end of the list.
 
 	@param value The value to add to the list.
 	
+	@return A new node containing the value.
+
 	#end
-	Method AddLast( value:T )
-		Local node:=New Node( value )
-		If _last
-			node._pred=_last
-			_last._succ=node
-		Else
-			_first=node
-		Endif
-		_last=node
-		_length+=1
+	Method AddLast:Node( value:T )
+		Local node:=New Node( value,_head )
 		_seq+=1
+		Return node
 	End
 	
 	#rem monkeydoc Removes the first value in the list equal to a given value.
@@ -260,7 +358,8 @@ Class List<T> Implements IContainer<T>
 	Method Remove:Bool( value:T )
 		Local node:=FindNode( value )
 		If Not node Return False
-		Erase( node )
+		node.Remove()
+		_seq+=1
 		Return True
 	End
 	
@@ -274,7 +373,8 @@ Class List<T> Implements IContainer<T>
 	Method RemoveLast:Bool( value:T )
 		Local node:=FindLastNode( value )
 		If Not node Return False
-		Erase( node )
+		node.Remove()
+		_seq+=1
 		Return True
 	End
 	
@@ -286,15 +386,17 @@ Class List<T> Implements IContainer<T>
 	
 	#end
 	Method RemoveEach:Int( value:T )
-		Local node:=_first,n:=0
-		While node
+		Local node:=_head._succ,n:=0
+		While node<>_head
 			If node._value=value
-				node=Erase( node )
+				node=node._succ
+				node._pred.Remove()
 				n+=1
 			Else
 				node=node._succ
 			Endif
 		Wend
+		If n _seq+=1
 		Return n
 	End
 	
@@ -306,12 +408,10 @@ Class List<T> Implements IContainer<T>
 
 	#end
 	Method RemoveFirst:T()
-		DebugAssert( _length )
+		DebugAssert( Not Empty )
 		
-		Local value:=_first._value
-		_first=_first._succ
-		If _first _first._pred=Null Else _last=Null
-		_length-=1
+		Local value:=_head._succ._value
+		_head._succ.Remove()
 		_seq+=1
 		Return value
 	End
@@ -324,36 +424,13 @@ Class List<T> Implements IContainer<T>
 
 	#end
 	Method RemoveLast:T()
-		DebugAssert( _length )
+		DebugAssert( Not Empty )
 		
-		Local value:=_last._value
-		_last=_last._pred
-		If _last _last._succ=Null Else _first=Null
-		_length-=1
+		Local value:=_head._pred._value
+		_head._pred.Remove()
 		_seq+=1
 		Return value
 	End
-
-	#rem monkeydoc Gets an iterator for the value at a given index in the list.
-	
-	Warning! This method can be SLOW if index is greater than 0 as the list must be linearly searched to find `index`.
-	
-	In debug builds, a runtime error will occur if index is out of range.
-
-	@param index The index of the iterator to find.
-	
-	#end
-	Method Index:Iterator( index:Int )
-		DebugAssert( index>=0 And index<_length )
-		
-		Local node:=_first
-		While node
-			If Not index Return New Iterator( Self,node )
-			node=node._succ
-			index-=1
-		Wend
-		Return New Iterator( Self,Null )
-	End
 	
 	#rem monkeydoc Adds a value to the end of the list.
 	
@@ -366,7 +443,9 @@ Class List<T> Implements IContainer<T>
 		AddLast( value )
 	End
 	
-	#rem monkeydoc Adds all values in an array to the end of the list.
+	#rem monkeydoc Adds all values in an array or container to the end of the list.
+	
+	@param values The values to add.
 	
 	@param values The values to add.
 	
@@ -377,11 +456,6 @@ Class List<T> Implements IContainer<T>
 		Next
 	End
 
-	#rem monkedoc Adds all values in a container to the end of the list.
-	
-	@param values The values to add.
-	
-	#end	
 	Method AddAll<C>( values:C ) Where C Implements IContainer<T>
 		For Local value:=Eachin values
 			AddLast( value )
@@ -409,29 +483,26 @@ Class List<T> Implements IContainer<T>
 
 	Method Sort( compareFunc:Int( x:T,y:T ) )
 	
-		If _first=_last Return
-	
 		Local insize:=1
 		
 		Repeat
 		
 			Local merges:=0
-			Local p:=_first,tail:Node
-			
-			While p
+			Local tail:=_head
+			Local p:=_head._succ
 
+			While p<>_head
 				merges+=1
 				Local q:=p._succ,qsize:=insize,psize:=1
 				
-				While psize<insize And q
+				While psize<insize And q<>_head
 					psize+=1
 					q=q._succ
 				Wend
 
 				Repeat
 					Local t:Node
-					
-					If psize And qsize And q
+					If psize And qsize And q<>_head
 						Local cc:=compareFunc( p._value,q._value )
 						If cc<=0
 							t=p
@@ -446,40 +517,28 @@ Class List<T> Implements IContainer<T>
 						t=p
 						p=p._succ
 						psize-=1
-					Else If qsize And q
+					Else If qsize And q<>_head
 						t=q
 						q=q._succ
 						qsize-=1
 					Else
 						Exit
 					Endif
-					
 					t._pred=tail
-					If tail tail._succ=t Else _first=t
+					tail._succ=t
 					tail=t
-					
-'					t._pred=tail
-'					tail._succ=t
-'					tail=t
-					
 				Forever
-				
 				p=q
-				
 			Wend
-			
-			tail._succ=Null
-			_last=tail
-			
-'			tail._succ=_head
-'			_head._pred=tail
+			tail._succ=_head
+			_head._pred=tail
 
 			If merges<=1 Return
 
 			insize*=2
 		Forever
 
-	End Method
+	End
 	
 	#rem monkeydoc Joins the values in the string list.
 	
@@ -492,67 +551,60 @@ Class List<T> Implements IContainer<T>
 		Return separator.Join( ToArray() )
 	End
 
-	Private
+	#rem monkeydoc Gets the head node of the list.
+	#end
+	Method HeadNode:Node()
+		Return _head
+	End
 	
+	#rem monkeydoc Gets the first node in the list.
+	
+	@return The first node in the list, or null if the list is empty.
+	
+	#end
 	Method FirstNode:Node()
-		Return _first
+		If Not Empty Return _head._succ
+		Return Null
 	End
 	
+	#rem monkeydoc Gets the last node in the list.
+	
+	@return The last node in the list, or null if the list is empty.
+	
+	#end
 	Method LastNode:Node()
-		Return _last
+		If Not Empty Return _head._pred
+		Return Null
 	End
 	
+	#rem monkeydoc Finds the first node in the list containing a value.
+	
+	@param value The value to find.
+	
+	@return The first node containing the value, or null if the value was not found.
+	
+	#end
 	Method FindNode:Node( value:T )
-		Local node:=_first
-		While node And node._value<>value
+		Local node:=_head._succ
+		While node<>_head And node._value<>value
 			node=node._succ
 		Wend
-		Return node
+		Return Null
 	End
 	
+	#rem monkeydoc Finds the last node in the list containing a value.
+	
+	@param value The value to find.
+	
+	@return The last node containing the value, or null if the value was not found.
+	
+	#end
 	Method FindLastNode:Node( value:T )
-		Local node:=_last
-		While node And node._value<>value
+		Local node:=_head._pred
+		While node<>_head And node._value<>value
 			node=node._pred
 		Wend
-		Return node
-	End
-	
-	Method Erase:Node( node:Node )
-		If Not node Return Null	'OK to erase tail element...
-		Local succ:=node._succ
-		If node=_first
-			_first=succ
-			If _first _first._pred=Null Else _last=Null
-		Else If node=_last
-			_last=node._pred
-			_last._succ=Null
-		Else
-			node._pred._succ=succ
-			node._succ._pred=node._pred
-		Endif
-		_length-=1
-		_seq+=1
-		Return succ
-	End
-	
-	Method Insert:Node( succ:Node,value:T )
-		Local node:=New Node( value )
-		If succ
-			node._succ=succ
-			If succ=_first _first=node Else succ._pred._succ=node
-			succ._pred=node
-		Else If _last
-			node._pred=_last
-			_last._succ=node
-			_last=node
-		Else
-			_first=node
-			_last=node
-		Endif
-		_length+=1
-		_seq+=1
-		Return node
+		Return Null
 	End
 	
 End

+ 56 - 14
modules/std/collections/stack.monkey2

@@ -11,6 +11,8 @@ Alias StringStack:Stack<String>
 #end
 Class Stack<T> Implements IContainer<T>
 
+	#rem monkeydoc The Stack.Iterator class
+	#end
 	Struct Iterator 'Implements IIterator<T>
 	
 		Private
@@ -29,12 +31,16 @@ Class Stack<T> Implements IContainer<T>
 		
 		Public
 		
+		#rem monkeydoc Creates a new iterator.
+		#end
 		Method New( stack:Stack,index:Int )
 			_stack=stack
 			_index=index
 			_seq=stack._seq
 		End
 		
+		#rem monkeydoc Checks if the iterator has reached the end of the stack.
+		#end
 		Property AtEnd:Bool()
 			AssertSeq()
 			Return _index=_stack._length
@@ -46,6 +52,8 @@ Class Stack<T> Implements IContainer<T>
 			Return Not AtEnd
 		End
 		
+		#rem monkeydoc The value currently pointed to by the iterator.
+		#end
 		Property Current:T()
 			AssertCurrent()
 			Return _stack._data[_index]
@@ -54,17 +62,32 @@ Class Stack<T> Implements IContainer<T>
 			_stack._data[_index]=current
 		End
 		
+		#rem monkeydoc Bumps the iterator so it points to the next value in the stack.
+		#end
 		Method Bump()
 			AssertCurrent()
 			_index+=1
 		End
 		
+		#rem monkeydoc Safely erases the value pointed to by the iterator.
+		
+		After calling this method, the iterator will point to the value after the removed value.
+		
+		Therefore, if you are manually iterating through a stack you should not call [[Bump]] after calling this method or you
+		will end up skipping a value.
+		
+		#end
 		Method Erase()
 			AssertSeq()
 			_stack.Erase( _index )
 			_seq=_stack._seq
 		End
 		
+		#rem monkeydoc Safely inserts a value before the value pointed to by the iterator.
+
+		After calling this method, the iterator will point to the newly added value.
+		
+		#end
 		Method Insert( value:T )
 			AssertSeq()
 			_stack.Insert( _index,value )
@@ -81,20 +104,45 @@ Class Stack<T> Implements IContainer<T>
 	Public
 	
 	#rem monkeydoc Creates a new stack.
+	
+	New() creates an empty stack.
+	
+	New( length:Int ) creates a stack that initially contains `length` null values.
+	
+	New( values:T[] ) creates a stack with the contents of an array.
+	
+	New( values:List<T> ) creates a stack with the contents of a list.
+	
+	New( values:Stack<T> ) create a stack with the contents of another stack.
+	
+	@param length The length of the stack.
+	
+	@param values An array, list or stack.
+	
 	#end
 	Method New()
 		_data=New T[10]
 	End
-
-	#rem monkeydoc Creates a new stack with the contents of an array.
-
-	@param data The array to create the stack with.
 	
-	#end
+	Method New( length:Int )
+		_length=length
+		_data=New T[_length]
+	End
+
 	Method New( values:T[] )
 		AddAll( values )
 	End
 	
+	Method New( values:Stack<T> )
+		_length=values.Length
+		_data=New T[_length]
+		values.Data.CopyTo( _data,0,0,_length )
+	End
+	
+	Method New( values:List<T> )
+		AddAll( values )
+	End
+	
 	#rem monkeydoc Checks if the stack is empty.
 	
 	@return True if the stack is empty.
@@ -104,17 +152,13 @@ Class Stack<T> Implements IContainer<T>
 		Return _length=0
 	End
 
-	#rem monkeydoc Gets an iterator to the stack.
+	#rem monkeydoc Gets an iterator to all values in the stack.
+	
+	Returns an iterator suitable for use with [[Eachin]], or for manual iteration.
 	
 	@return A stack iterator.
 	
 	#end
-'	Method GetIterator:Iterator()
-'		Return New Iterator( Self,0 )
-'	End
-
-	#rem monkeydoc @hidden
-	#end
 	Method All:Iterator()
 		Return New Iterator( Self,0 )
 	End
@@ -614,8 +658,6 @@ Class Stack<T> Implements IContainer<T>
 		Sort( compareFunc,x,hi )
 	End
 	
-	'***** Stack style extensions *****
-	
 	#rem monkeydoc Gets the top element of the stack
 	
 	In debug builds, a runtime error will occur if the stack is empty.

+ 45 - 2
modules/std/geom/mat4.monkey2

@@ -14,12 +14,55 @@ Struct Mat4<T>
 		i.x=1;j.y=1;k.z=1;t.w=1
 	End
 	
-	Method New( i:T,j:T,k:T,t:T )
-		Self.i.x=i;Self.j.y=j;Self.k.z=k;Self.t.w=t
+	Method New( ix:T,jy:T,kz:T,tw:T )
+		i.x=ix;j.y=jy;k.z=kz;t.w=tw
 	End
 	
 	Method New( i:Vec4<T>,j:Vec4<T>,k:Vec4<T>,t:Vec4<T> )
 		Self.i=i;Self.j=j;Self.k=k;Self.t=t
 	End
 	
+	Method New( m:AffineMat3<T> )
+		i.x=m.i.x;i.y=m.i.y
+		j.x=m.j.x;j.y=m.j.y
+		k.z=1
+		t.x=m.t.x;t.y=m.t.y;t.w=1
+	End
+	
+	Operator*:Mat4( m:Mat4 )
+		Local r:Mat4
+		
+		r.i.x=i.x*m.i.x + j.x*m.i.y + k.x*m.i.z + t.x*m.i.w 
+		r.i.y=i.y*m.i.x + j.y*m.i.y + k.y*m.i.z + t.y*m.i.w
+		r.i.z=i.z*m.i.x + j.z*m.i.y + k.z*m.i.z + t.z*m.i.w
+		r.i.w=i.w*m.i.x + j.w*m.i.y + k.w*m.i.z + t.w*m.i.w
+		
+		r.j.x=i.x*m.j.x + j.x*m.j.y + k.x*m.j.z + t.x*m.j.w 
+		r.j.y=i.y*m.j.x + j.y*m.j.y + k.y*m.j.z + t.y*m.j.w
+		r.j.z=i.z*m.j.x + j.z*m.j.y + k.z*m.j.z + t.z*m.j.w
+		r.j.w=i.w*m.j.x + j.w*m.j.y + k.w*m.j.z + t.w*m.j.w
+		
+		r.k.x=i.x*m.k.x + j.x*m.k.y + k.x*m.k.z + t.x*m.k.w 
+		r.k.y=i.y*m.k.x + j.y*m.k.y + k.y*m.k.z + t.y*m.k.w
+		r.k.z=i.z*m.k.x + j.z*m.k.y + k.z*m.k.z + t.z*m.k.w
+		r.k.w=i.w*m.k.x + j.w*m.k.y + k.w*m.k.z + t.w*m.k.w
+		
+		r.t.x=i.x*m.t.x + j.x*m.t.y + k.x*m.t.z + t.x*m.t.w 
+		r.t.y=i.y*m.t.x + j.y*m.t.y + k.y*m.t.z + t.y*m.t.w
+		r.t.z=i.z*m.t.x + j.z*m.t.y + k.z*m.t.z + t.z*m.t.w
+		r.t.w=i.w*m.t.x + j.w*m.t.y + k.w*m.t.z + t.w*m.t.w
+		
+		Return r
+	End
+	
+	Function Ortho:Mat4( left:Float,right:Float,bottom:Float,top:Float,near:Float,far:Float )
+
+		Local w:=right-left,h:=top-bottom,d:=far-near
+
+		Local r:Mat4
+		r.i.x=2/w ; r.j.y=2/h ; r.k.z=2/d
+		r.t=New Vec4<T>( -(right+left)/w,-(top+bottom)/h,-(far+near)/d,1 )
+		Return r
+	End
+	
 End

+ 9 - 0
modules/std/geom/rect.monkey2

@@ -233,3 +233,12 @@ Struct Rect<T>
 	End
 	
 End
+
+Function TransformRecti<T>:Recti( rect:Recti,matrix:AffineMat3<T> )
+	
+	Local min:=matrix * New Vec2f( rect.min.x,rect.min.y )
+	Local max:=matrix * New Vec2f( rect.max.x,rect.max.y )
+		
+	Return New Recti( Round( min.x ),Round( min.y ),Round( max.x ),Round( max.y ) )
+End
+

+ 1 - 1
modules/std/graphics/pixmap.monkey2

@@ -411,7 +411,7 @@ Class Pixmap
 	#end
 	Function Load:Pixmap( path:String,format:PixelFormat=PixelFormat.Unknown )
 	
-		Return internal.LoadPixmap( path,format )
+		Return LoadPixmap( path,format )
 
 	End
 	

+ 1 - 1
modules/std/graphics/pixmaploader.monkey2

@@ -1,5 +1,5 @@
 
-Namespace std.graphics.internal
+Namespace std.graphics
 
 #Import "<stb-image>"
 

+ 66 - 36
modules/std/misc/json.monkey2

@@ -3,7 +3,7 @@ Namespace std.json
 
 #rem monkeydoc JsonError class.
 #end
-Class JsonError Extends Throwable
+Class JsonError Extends Exception
 End
 
 #rem monkeydoc JsonValue class.
@@ -28,24 +28,16 @@ Class JsonValue Abstract
 		Return Null
 	End
 	
-	Operator[]:JsonValue( index:Int ) Virtual
+	Method ToArray:Stack<JsonValue>() Virtual
 		Assert( False )
 		Return Null
 	End
 	
-	Operator[]=( index:Int,value:JsonValue ) Virtual
-		Assert( False )
-	End
-	
-	Operator[]:JsonValue( key:String ) Virtual
+	Method ToObject:StringMap<JsonValue>() Virtual
 		Assert( False )
 		Return Null
 	End
 	
-	Operator[]=( key:String,value:JsonValue ) Virtual
-		Assert( False )
-	End
-	
 	Method ToJson:String() Virtual
 		Local buf:=New StringStack
 		PushJson( buf )
@@ -211,8 +203,15 @@ End
 #end
 Class JsonArray Extends JsonValue
 
-	Method New( data:Stack<JsonValue> =Null )
-		If Not data data=New Stack<JsonValue>
+	Method New( length:Int=0 )
+		_data=New Stack<JsonValue>( length )
+	End
+
+	Method New( data:JsonValue[] )
+		_data=New Stack<JsonValue>( data )
+	End
+	
+	Method New( data:Stack<JsonValue> )
 		_data=data
 	End
 	
@@ -222,12 +221,16 @@ Class JsonArray Extends JsonValue
 		_data=data
 	End
 	
-	Operator[]:JsonValue( index:Int ) Override
-		Return _data[index]
+	Property Length:Int()
+		Return _data.Length
+	End
+	
+	Method Add( value:JsonValue )
+		_data.Add( value )
 	End
 	
-	Operator[]=( index:Int,value:JsonValue ) Override
-		_data[index]=value
+	Method ToArray:Stack<JsonValue>() Override
+		Return _data
 	End
 	
 	Private
@@ -262,14 +265,26 @@ Class JsonObject Extends JsonValue
 		_data=data
 	End
 	
-	Operator[]:JsonValue( key:String ) Override
+	Method Contains:Bool( key:String )
+		Return _data.Contains( key )
+	End
+	
+	Operator[]:JsonValue( key:String )
 		Return _data[key]
 	End
 	
-	Operator[]=( key:String,value:JsonValue ) Override
+	Operator[]=( key:String,value:JsonValue )
 		_data[key]=value
 	End
 	
+	Method ToObject:StringMap<JsonValue>() Override
+		Return Data
+	End
+	
+	Function Load:JsonObject( path:String )
+		Return Cast<JsonObject>( JsonValue.Load( path ) )
+	End
+	
 	Private
 	
 	Field _data:StringMap<JsonValue>
@@ -299,15 +314,15 @@ Class JsonParser
 	
 	Method ParseValue:JsonValue()
 		If TokeType=T_STRING Return New JsonString( ParseString() )
-		If TokeType=T_NUMBER Return New JsonNumber( Double( ParseNumber() ) )
+		If TokeType=T_NUMBER Return New JsonNumber( ParseNumber() )
 		If Toke="{" Return New JsonObject( ParseObject() )
 		If Toke="[" Return New JsonArray( ParseArray() )
-		If CParse("true") Return JsonBool.TrueValue
-		If CParse("false") Return JsonBool.FalseValue
-		If CParse("null") Return Null
+		If CParse( "true" ) Return JsonBool.TrueValue
+		If CParse( "false" ) Return JsonBool.FalseValue
+		If CParse( "null" ) Return Null
 		Return Null
 	End
-
+	
 	Private
 	
 	Const T_EOF:=0
@@ -373,7 +388,14 @@ Class JsonParser
 				If chr=92 GetChar()
 			Forever
 			_type=T_STRING
-		Else If chr=45 Or (chr>=48 And chr<=57)
+		Else If chr=39
+			Repeat
+				Local chr:=GetChar()
+				If chr=39 Exit
+				If chr=92 GetChar()
+			Forever
+			_type=T_STRING
+		Else If (chr>=48 And chr<=57) Or chr=45
 			If chr=45 '-
 				chr=GetChar()
 				If chr<48 Or chr>57 Throw New JsonError()
@@ -389,9 +411,9 @@ Class JsonParser
 				If Not CParseDigits() Throw New JsonError()
 			Endif
 			_type=T_NUMBER
-		Else If (chr>=65 And chr<91) Or (chr>=97 And chr<123)
+		Else If (chr>=65 And chr<91) Or (chr>=97 And chr<123) Or chr=95
 			chr=PeekChar()
-			While (chr>=65 And chr<91) Or (chr>=97 And chr<123)
+			While (chr>=65 And chr<91) Or (chr>=97 And chr<123) Or (chr>=48 And chr<58) Or chr=95
 				GetChar()
 				chr=PeekChar()
 			Wend
@@ -407,7 +429,7 @@ Class JsonParser
 		Return _toke
 	End
 	
-	property TokeType:Int()
+	Property TokeType:Int()
 		Return _type
 	End
 	
@@ -426,7 +448,12 @@ Class JsonParser
 		Local map:=New StringMap<JsonValue>
 		If CParse( "}" ) Return map
 		Repeat
-			Local name:=ParseString()
+			Local name:=Toke
+			If TokeType=T_IDENT
+				Bump()
+			Else
+				name=ParseString()
+			Endif
 			Parse( ":" )
 			Local value:=ParseValue()
 			map.Set( name,value )
@@ -437,19 +464,22 @@ Class JsonParser
 	
 	Method ParseArray:Stack<JsonValue>()
 		Parse( "[" )
-		If CParse( "]" ) Return Null
 		Local stack:=New Stack<JsonValue>
+		If CParse( "]" ) Return stack
 		Repeat
 			Local value:=ParseValue()
-			stack.Push( value )
+			stack.Add( value )
 		Until Not CParse( "," )
 		Parse( "]" )
 		Return stack
 	End
 	
 	Method ParseString:String()
+	
 		If TokeType<>T_STRING Throw New JsonError()
+		
 		Local toke:=Toke.Slice( 1,-1 )
+		
 		Local i:=toke.Find( "\" )
 		If i<>-1
 			Local frags:=New StringStack,p:=0,esc:=""
@@ -457,10 +487,10 @@ Class JsonParser
 				If i+1>=toke.Length Throw New JsonError()
 				frags.Push( toke.Slice( p,i ) )
 				Select toke[i+1]
-				Case 34  esc="~q"				'\"
+				Case 34  esc="~q"					'\"
 				Case 92  esc="\"					'\\
 				Case 47  esc="/"					'\/
-				Case 98  esc=String.FromChar( 8 )		'\b
+				Case 98  esc=String.FromChar( 8 )	'\b
 				Case 102 esc=String.FromChar( 12 )	'\f
 				Case 114 esc=String.FromChar( 13 )	'\r
 				Case 110 esc=String.FromChar( 10 )	'\n
@@ -497,11 +527,11 @@ Class JsonParser
 		Return toke
 	End
 	
-	Method ParseNumber:String()
+	Method ParseNumber:Double()
 		If TokeType<>T_NUMBER Throw New JsonError()
 		Local toke:=Toke
 		Bump()
-		Return toke
+		Return Double( toke )
 	End
-
+	
 End

+ 24 - 0
modules/std/misc/random.monkey2

@@ -53,3 +53,27 @@ End
 Function Rnd:Double()
 	Return Double( RndULong() & ULong( $1fffffffffffff ) ) / Double( $20000000000000 )
 End
+
+#rem monkeydoc Generates a random double value greater than or equal to 0 and less than a given value.
+
+@param max Maximum value to return.
+
+@return A random double value in the range 0 (inclusive) to `max` (exclusive).
+
+#end
+Function Rnd:Double( max:Double )
+	Return Rnd()*max
+End
+
+#rem monkeydoc Generates a random double value within a given range.
+
+@param min Minimum value to return.
+
+@param max Maximum value to return.
+
+@return A random double value in the range `min` (inclusive) to `max` (exclusive).
+
+#end
+Function Rnd:Double( min:Double,max:Double )
+	Return Rnd(max-min)+min
+End

+ 19 - 6
modules/std/misc/time.monkey2

@@ -3,6 +3,10 @@ Namespace std.time
 
 Private
 
+Global _us0:Long
+
+Public
+
 #rem monkeydoc @hidden Time class.
 #end
 Class Time
@@ -105,16 +109,25 @@ Class Time
 	
 End
 
-#rem monkeydoc @hidden Gets the number of seconds since the app started.
+#rem monkeydoc Gets the number of microseconds since the app started.
 #end
-Function Seconds:Double()
-	Return Double(clock())/Double(CLOCKS_PER_SEC)
+Function Microsecs:Long()
+	Local tv:timeval
+	gettimeofday( Varptr tv,Null )
+	Local us:=tv.tv_sec*1000000+tv.tv_usec
+	If _us0 Return us-_us0
+	_us0=us
+	Return 0
 End
 
 #rem monkeydoc Gets the number of milliseconds since the app started.
 #end
 Function Millisecs:Int()
-	'Note:CLOCKS_PER_SECOND=1000000 on macos/linux, 1000 on windows...
-	If CLOCKS_PER_SEC>=1000 Return clock()/(CLOCKS_PER_SEC/1000)
-	Return clock()*(1000/CLOCKS_PER_SEC)	'is that right?!?
+	Return Microsecs()/1000
+End
+
+#rem monkeydoc @hidden Gets the number of seconds since the app started.
+#end
+Function Seconds:Double()
+	Return Microsecs()/1000000.0
 End

+ 43 - 38
modules/std/std.monkey2

@@ -3,49 +3,54 @@ Namespace std
 
 #Import "<libc.monkey2>"
 
-#Import "collections/container.monkey2"
-#Import "collections/stack.monkey2"
-#Import "collections/list.monkey2"
-#Import "collections/map.monkey2"
-
-#Import "memory/byteorder.monkey2"
-#Import "memory/databuffer.monkey2"
-
-#Import "stream/stream.monkey2"
-#Import "stream/filestream.monkey2"
-#Import "stream/datastream.monkey2"
-#Import "stream/zipstream.monkey2"
-
-#Import "geom/vec2.monkey2"
-#Import "geom/vec3.monkey2"
-#Import "geom/vec4.monkey2"
-#Import "geom/mat3.monkey2"
-#Import "geom/mat4.monkey2"
-#Import "geom/affinemat3.monkey2"
-'#Import "geom/affinemat4.monkey2"
-#Import "geom/rect.monkey2"
-#Import "geom/axis.monkey2"
-
-#Import "graphics/pixelformat.monkey2"
-#Import "graphics/pixmap.monkey2"
-#Import "graphics/pixmaploader.monkey2"
-#Import "graphics/color.monkey2"
-
-#Import "misc/random.monkey2"
-#Import "misc/chartype.monkey2"
-#Import "misc/filesystem.monkey2"
-#Import "misc/stringio.monkey2"
-#Import "misc/json.monkey2"
-#Import "misc/markdown.monkey2"
-#Import "misc/time.monkey2"
-
-'#Import "_dev/fiber.monkey2"
-'#Import "_dev/generator.monkey2"
+#Import "collections/container"
+#Import "collections/stack"
+#Import "collections/list"
+#Import "collections/map"
+
+#Import "memory/byteorder"
+#Import "memory/databuffer"
+
+#Import "stream/stream"
+#Import "stream/filestream"
+#Import "stream/datastream"
+#Import "stream/zipstream"
+
+#Import "geom/vec2"
+#Import "geom/vec3"
+#Import "geom/vec4"
+#Import "geom/mat3"
+#Import "geom/mat4"
+#Import "geom/affinemat3"
+'#Import "geom/affinemat4"
+#Import "geom/rect"
+#Import "geom/axis"
+
+#Import "graphics/pixelformat"
+#Import "graphics/pixmap"
+#Import "graphics/pixmaploader"
+#Import "graphics/color"
+
+#Import "misc/random"
+#Import "misc/chartype"
+#Import "misc/filesystem"
+#Import "misc/stringio"
+#Import "misc/json"
+#Import "misc/markdown"
+#Import "misc/time"
+
+#Import "fiber/fiber"
+#Import "fiber/future"
+'#Import "_dev/fiber"
+'#Import "_dev/generator"
 
 Private
 
 Function Main()
 
+	'capture app start time
+	std.time.Microsecs()
+
 	Stream.OpenFuncs["file"]=Lambda:Stream( proto:String,path:String,mode:String )
 
 		Return FileStream.Open( path,mode )