Bläddra i källkod

Merge remote-tracking branch 'remotes/official/master' into finally

# Conflicts:
#	ctranslator.bmx
#	parser.bmx
#	translator.bmx
HurryStarfish 7 år sedan
förälder
incheckning
3bec54518f
18 ändrade filer med 949 tillägg och 639 borttagningar
  1. 1 1
      base.stringhelper.bmx
  2. 1 1
      base64.bmx
  3. 3 1
      bcc.bmx
  4. 481 471
      config.bmx
  5. 39 15
      ctranslator.bmx
  6. 191 87
      decl.bmx
  7. 75 8
      expr.bmx
  8. 1 1
      iparser.bmx
  9. 2 2
      options.bmx
  10. 99 24
      parser.bmx
  11. 11 2
      stmt.bmx
  12. 1 1
      stringbuffer_common.bmx
  13. 1 1
      stringbuffer_core.bmx
  14. 1 1
      stringbuffer_glue.c
  15. 2 2
      toker.bmx
  16. 1 1
      transform.c
  17. 5 1
      translator.bmx
  18. 34 19
      type.bmx

+ 1 - 1
base.stringhelper.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2014-2017 Bruce A Henderson
+' Copyright (c) 2014-2018 Bruce A Henderson
 ' Copyright (c) 2014-2017 Ronny Otto
 '
 ' This software is provided 'as-is', without any express or implied

+ 1 - 1
base64.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2008-2017 Bruce A Henderson
+' Copyright (c) 2008-2018 Bruce A Henderson
 ' 
 ' Permission is hereby granted, free of charge, to any person obtaining a copy
 ' of this software and associated documentation files (the "Software"), to deal

+ 3 - 1
bcc.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -47,6 +47,8 @@ If opt_buildtype = BUILDTYPE_MODULE Then
 	End If
 End If
 
+TGenProcessor.processor = New TParser
+
 Local app:TAppDecl 
 Local trans:TCTranslator 
 Try

+ 481 - 471
config.bmx

@@ -1,471 +1,481 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
-'
-' Based on the public domain Monkey "trans" by Mark Sibly
-'
-' This software is provided 'as-is', without any express or implied
-' warranty. In no event will the authors be held liable for any damages
-' arising from the use of this software.
-'
-' Permission is granted to anyone to use this software for any purpose,
-' including commercial applications, and to alter it and redistribute it
-' freely, subject to the following restrictions:
-'
-'    1. The origin of this software must not be misrepresented; you must not
-'    claim that you wrote the original software. If you use this software
-'    in a product, an acknowledgment in the product documentation would be
-'    appreciated but is not required.
-'
-'    2. Altered source versions must be plainly marked as such, and must not be
-'    misrepresented as being the original software.
-'
-'    3. This notice may not be removed or altered from any source
-'    distribution.
-'
-SuperStrict
-
-Import BRL.LinkedList
-Import BRL.Map
-Import BRL.FileSystem
-Import Pub.zlib
-
-Import "options.bmx"
-Import "base.stringhelper.bmx"
-Import "base64.bmx"
-
-' debugging help
-Const DEBUG:Int = False
-Const ABORT_ON_NULL:Int = True
-Const PROFILER:Int = False
-Const DEBUGSTOP_ON_ERROR:Int = False
-
-Global ENV_LANG$
-
-Global _errInfo$
-Global _errStack:TList = New TList
-
-' bytes offset to the first field
-Global OBJECT_BASE_OFFSET:Int = 8
-' 4 bytes on 32-bit, 8 bytes on 64-bit
-Global POINTER_SIZE:Int = 4
-
-Global _symbols$[]=[ "..","[]",":*",":/",":+",":-",":|",":&",":~~",":shr",":shl",":sar",":mod"]
-Global _symbols_map$[]=[ "..","[]","*=","/=","+=","-=","|=","&=","^=",">>=", "<<=",">>=","%=" ]
-
-Function PushErr( errInfo$ )
-	_errStack.AddLast _errInfo
-	_errInfo=errInfo
-End Function
-
-Function PopErr()
-	_errInfo=String(_errStack.RemoveLast())
-End Function
-
-Function Err( err$ )
-	If DEBUGSTOP_ON_ERROR Then
-		DebugStop ' useful for debugging!
-	End If
-	Throw "Compile Error: "+err + "~n" + _errInfo + "~n"
-End Function
-
-Function Warn( err$ )
-	'If DEBUGSTOP_ON_ERROR Then
-	'	DebugStop ' useful for debugging!
-	'End If
-	Print "Compile Warning: "+err + "~n" + _errInfo + "~n"
-End Function
-
-Function FormatError:String(path:String, line:Int, char:Int)
-	Return "[" + path + ";" + line + ";" + char + "]"
-End Function
-
-Function InternalErr()
-	If DEBUGSTOP_ON_ERROR Then
-		DebugStop ' useful for debugging!
-	End If
-	Throw "Internal Error.~n" + _errInfo + "~n"
-End Function
-
-Function IsSpace:Int( ch:Int )
-	Return ch<=Asc(" ") Or ch=$A0 ' NO-BREAK SPACE (U+00A0)
-End Function
-
-Function IsDigit:Int( ch:Int )
-	Return ch>=Asc("0") And ch<=Asc("9")
-End Function
-
-Function IsAlpha:Int( ch:Int )
-	Return (ch>=Asc("A") And ch<=Asc("Z")) Or (ch>=Asc("a") And ch<=Asc("z"))
-End Function
-
-Function IsBinDigit:Int( ch:Int )
-	Return ch=Asc("0") Or ch=Asc("1")
-End Function
-
-Function IsHexDigit:Int( ch:Int )
-	Return IsDigit(ch) Or (ch>=Asc("A") And ch<=Asc("F")) Or (ch>=Asc("a") And ch<=Asc("f"))
-End Function
-
-Function Todo() 
-	Err "TODO!"
-End Function
-
-Function IsStandardFunc:Int(func:String)
-	func = func.ToLower()
-	
-	Global funcs:String = ";isalnum;isalpha;isascii;isblank;iscntrl;isdigit;isgraph;islower;isprint;ispunct;isspace;isupper;isxdigit;" + ..
-		"strlen;_wgetenv;_wputenv;"
-	
-	Return funcs.Find(func) > 0
-End Function
-
-Function mapSymbol:String(sym:String)
-	For Local i:Int = 0 Until _symbols.length
-		If sym = _symbols[i] Then
-			Return _symbols_map[i]
-		End If
-	Next
-	Return sym
-End Function
-
-
-'enquote depending on ENV_LANG
-'
-Function LangEnquote$( str$ )
-	str=EscapeString(str)
-'	str=str.Replace( "~0","\0" )	'Fix me?
-	For Local i:Int=0 Until str.Length
-		If str[i]>=32 And str[i]<128 Continue
-		Local t$,n:Int=str[i]
-		While n
-			Local c:Int=(n&15)+48
-			If c>=58 c:+97-58
-			t=Chr( c )+t
-			n=(n Shr 4) & $0fffffff
-		Wend
-		If Not t t="0"
-		If ENV_LANG = "cpp" Then
-		'Case "cpp"
-			t="~q~q\x"+t+"~q~q"
-		Else
-			t="\u"+("0000"+t)[-4..]
-		End If
-		str=str[..i]+t+str[i+1..]
-		i:+t.Length-1
-	Next
-	str="~q"+str+"~q"
-	If ENV_LANG="cpp" str="L"+str
-	Return str
-End Function
-
-Function EscapeString$(str$)
-	str=str.Replace( "\","\\" )
-	str=str.Replace( "~q","\~q" )
-	str=str.Replace( "~n","\n" )
-	str=str.Replace( "~r","\r" )
-	str=str.Replace( "~t","\t" )
-	Return str
-End Function
-
-Function BmxEnquote$( str$ )
-	str=str.Replace( "~~","~~~~" )
-	str=str.Replace( "~q","~~q" )
-	str=str.Replace( "~n","~~n" )
-	str=str.Replace( "~r","~~r" )
-	str=str.Replace( "~t","~~t" )
-	str=str.Replace( "~0","~~0" )
-	str="~q"+str+"~q"
-	Return str
-End Function
-
-Function BmxUnquote$( str$, unicodeConvert:Int = False )
-	If str.length = 1 Or str[str.length - 1] <> Asc("~q") Then
-		Err "Expecting expression but encountered malformed string literal"
-	End If
-	str=str[1..str.Length-1]
-	If unicodeConvert Then
-		Local pos:Int = str.Find("~~")
-		While pos <> -1
-			If pos + 1 < str.length Then
-				If str[pos + 1] >= Asc("1") And str[pos + 1] <= Asc("9") Then
-					Local p2:Int = str.Find("~~", pos + 1)
-					If p2 <> -1 Then
-						Local s:String = Chr(str[pos + 1.. p2].ToInt())
-						str = str[..pos] + s + str[p2 + 1..]
-					End If
-				End If
-			End If
-		
-			pos = str.Find("~~", pos + 1)
-		Wend
-	End If
-	str=str.Replace( "~~~~","~~z" )	'a bit dodgy - uses bad esc sequence ~z 
-	str=str.Replace( "~~q","~q" )
-	str=str.Replace( "~~n","~n" )
-	str=str.Replace( "~~r","~r" )
-	str=str.Replace( "~~t","~t" )
-	str=str.Replace( "~~0","~0" )
-	str=str.Replace( "~~z","~~" )
-	Return str
-End Function
-
-Type TStack Extends TList
-
-	Method Push(obj:Object)
-		AddFirst(obj)
-	End Method
-
-	Method Length:Int()
-		Return count()
-	End Method
-	
-	Method Get:Object(index:Int)
-		Return ValueAtIndex(index)
-	End Method
-	
-	Method Pop:Object()
-		Return RemoveFirst()
-	End Method
-	
-End Type
-
-Type TStringList Extends TList
-	Method Join:String(s:String)
-		Local arr:String[] = New String[count()]
-		Local index:Int
-		For Local t:String = EachIn Self
-			arr[index] = t
-			index :+ 1
-		Next
-		
-		Return s.Join(arr)
-	End Method
-End Type
-
-Type TKeyValue
-	Field key:Object
-	Field value:Object
-	
-	Method Create:TKeyValue(key:Object,value:Object)
-		Self.key = key
-		Self.value = value
-		Return Self
-	End Method
-	
-	Method Compare:Int(other:Object)
-		If Not TKeyValue(other) Return 0
-		Return key.Compare(TKeyValue(other).key)
-	End Method
-	
-End Type
-
-Type TUnorderedMap
-
-	Field list:TList = New TList
-	Field map:TMap = New TMap
-	
-	Field valuesList:TList = New TList
-
-	Method Insert( key:Object,value:Object )
-		list.AddLAst(New TKeyValue.Create(key, value))
-		valuesList.AddLast(value)
-		map.Insert(key, value)
-	End Method
-	
-	Method Keys:TList()
-		Local klist:TList = New TList
-		For Local kv:TKeyValue = EachIn list
-			klist.AddLast(kv.key)
-		Next
-		Return klist
-	End Method
-	
-	Method Values:TList()
-		'Local vlist:TList = New TList
-		'For Local kv:TKeyValue = EachIn list
-		'	vlist.AddLast(kv.value)
-		'Next
-		Return valuesList
-	End Method
-	
-	Method Contains:Int( key:Object )
-		Return map.Contains(key)
-	End Method
-	
-	Method ValueForKey:Object( key:Object )
-		Return map.ValueForKey(key)
-	End Method
-End Type
-
-Function MakeKeywords:String()
-	Local keywords:String
-	
-	keywords :+ "import brl.classes~n"
-	keywords :+ "Asc%(v$)=~qbrl_blitz_keywords_asc~q~n"
-	keywords :+ "Sgn#(v#)=~qbrl_blitz_keywords_sgn~q~n"
-	keywords :+ "Chr$(v%)=~qbrl_blitz_keywords_chr~q~n"
-	keywords :+ "Len%(v:Object)=~qbrl_blitz_keywords_len~q~n"
-	keywords :+ "Min%(v1%,v2%)=~qbrl_blitz_keywords_min~q~n"
-	keywords :+ "Max%(v1%,v2%)=~qbrl_blitz_keywords_max~q~n"
-	'keywords :+ "SizeOf%(v%)=~qbrl_blitz_keywords_sizeof~q~n"
-	'keywords :+ "Incbin(v$)=~qbrl_blitz_keywords_incbin~q~n"
-	keywords :+ "IncbinPtr@*(v$)=~qbbIncbinPtr~q~n"
-	keywords :+ "IncbinLen%(v$)=~qbbIncbinLen~q~n"
- 
-	Return keywords
-End Function
-
-Function FilePath:String(path:String)
-	Local baseDir:String = ExtractDir(path)
-	Local bmxDir:String = baseDir + "/.bmx"
-	
-	If FileType(bmxDir) <> FILETYPE_DIR Then
-		Throw "Missing : " + bmxDir
-	End If
-	
-	Return bmxDir
-End Function
-
-Function BuildHeaderName:String(path:String)
-	If opt_buildtype = BUILDTYPE_MODULE Then
-		path = opt_modulename + "_" + StripDir(path)
-	Else
-		Local dir:String = ExtractDir(path).ToLower().Replace("/.bmx","")
-		dir = dir[dir.findLast("/") + 1..]
-		If dir.EndsWith(".mod") Then
-			dir = dir.Replace(".mod", "")
-		End If
-		Local file:String = StripDir(path).ToLower()
-		path = dir + "_" + file
-	End If
-	
-	Return TStringHelper.Sanitize(path, , True)
-End Function
-
-Rem
-bbdoc: Get the header file name from a given module ident, optionally with include path.
-End Rem
-Function ModuleHeaderFromIdent:String(ident:String, includePath:Int = False)
-	Local ns:String = ident[..ident.find(".")]
-	Local name:String = ident[ident.find(".") + 1..]
-	
-	Local file:String = name + ".bmx" + FileMung() + ".h"
-	
-	If includePath Then
-		file = ns + ".mod/" + name + ".mod/.bmx/" + file
-	End If
-	
-	Return file
-End Function
-
-Function HeaderFile:String(path:String, mung:String)
-	Local fileDir:String = FilePath(path)
-	Local file:String = StripDir(path)
-	
-	Return fileDir + "/" + file + mung + ".h"
-End Function
-
-Function OutputFilePath:String(path:String, mung:String, suffix:String, bmxDir:Int = False)
-	Local fileDir:String = FilePath(path)
-	If bmxDir Then
-		fileDir :+ "/.bmx"
-	End If
-	Local file:String = StripDir(path)
-	
-	Return fileDir + "/" + file + mung + "." + suffix
-End Function
-
-Function FileMung:String(makeApp:Int = False)
-	Local m:String = "."
-	
-	If makeApp Then
-		Select opt_apptype
-			Case APPTYPE_CONSOLE
-				m :+ "console."
-			Case APPTYPE_GUI
-				m :+ "gui."
-		End Select
-	End If
-	
-	If opt_release Then
-		m :+ "release"
-	Else
-		m :+ "debug"
-	End If
-	
-'	If opt_threaded Then
-'		m :+ ".mt"
-'	End If
-	
-	m :+ "." + opt_platform
-	
-	m :+ "." + opt_arch
-	
-	Return m
-End Function
-
-Function HeaderComment:String()
-	' TODO
-End Function
-
-
-Type TTemplateRecord
-
-	Field start:Int
-	Field file:String
-	Field source:String
-	
-	Method Create:TTemplateRecord(start:Int, file:String, source:String)
-		Self.start = start
-		Self.file = file
-		Self.source = source
-		Return Self
-	End Method
-	
-	Method ToString:String()
-
-		Local s:Byte Ptr = source.ToUTF8String()
-		Local slen:Int = strlen_(s)
-
-?Not bmxng		
-		Local dlen:Int = slen + 12
-?bmxng And (win32 Or ptr32)
-		Local dlen:UInt = slen + 12
-?bmxng And ptr64 And Not win32
-		Local dlen:ULong = slen + 12
-?
-		Local data:Byte[dlen]
-		
-		compress2(data, dlen, s, slen, 9)
-		
-		MemFree(s)
-		
-		Local t:String = "{" + start +","+ slen +","+ LangEnquote(file) + ","
-		
-		t :+ LangEnquote(TBase64.Encode(data, dlen, 0, TBase64.DONT_BREAK_LINES))
-
-		Return t + "}"
-
-	End Method
-	
-	Function Load:TTemplateRecord(start:Int, file:String, size:Int, source:String)
-		
-?Not bmxng		
-		Local dlen:Int = size + 1
-?bmxng And (win32 Or ptr32)
-		Local dlen:UInt = size + 1
-?bmxng And ptr64 And Not win32
-		Local dlen:ULong = size + 1
-?
-		Local data:Byte[dlen]
-		
-		Local s:Byte[] = TBase64.Decode(source)
-		
-		uncompress(data, dlen, s, s.length)
-	
-		Return New TTemplateRecord.Create(start, file, String.FromUTF8String(data))
-	End Function
-End Type
-
-Extern
-	Function strlen_:Int(s:Byte Ptr)="strlen"
-End Extern
+' Copyright (c) 2013-2018 Bruce A Henderson
+'
+' Based on the public domain Monkey "trans" by Mark Sibly
+'
+' This software is provided 'as-is', without any express or implied
+' warranty. In no event will the authors be held liable for any damages
+' arising from the use of this software.
+'
+' Permission is granted to anyone to use this software for any purpose,
+' including commercial applications, and to alter it and redistribute it
+' freely, subject to the following restrictions:
+'
+'    1. The origin of this software must not be misrepresented; you must not
+'    claim that you wrote the original software. If you use this software
+'    in a product, an acknowledgment in the product documentation would be
+'    appreciated but is not required.
+'
+'    2. Altered source versions must be plainly marked as such, and must not be
+'    misrepresented as being the original software.
+'
+'    3. This notice may not be removed or altered from any source
+'    distribution.
+'
+SuperStrict
+
+Import BRL.LinkedList
+Import BRL.Map
+Import BRL.FileSystem
+Import Pub.zlib
+
+Import "options.bmx"
+Import "base.stringhelper.bmx"
+Import "base64.bmx"
+
+' debugging help
+Const DEBUG:Int = False
+Const ABORT_ON_NULL:Int = True
+Const PROFILER:Int = False
+Const DEBUGSTOP_ON_ERROR:Int = False
+
+Global ENV_LANG$
+
+Global _errInfo$
+Global _errStack:TList = New TList
+
+' bytes offset to the first field
+Global OBJECT_BASE_OFFSET:Int = 8
+' 4 bytes on 32-bit, 8 bytes on 64-bit
+Global POINTER_SIZE:Int = 4
+
+Global _symbols$[]=[ "..","[]",":*",":/",":+",":-",":|",":&",":~~",":shr",":shl",":sar",":mod"]
+Global _symbols_map$[]=[ "..","[]","*=","/=","+=","-=","|=","&=","^=",">>=", "<<=",">>=","%=" ]
+
+Function PushErr( errInfo$ )
+	_errStack.AddLast _errInfo
+	_errInfo=errInfo
+End Function
+
+Function PopErr()
+	_errInfo=String(_errStack.RemoveLast())
+End Function
+
+Function Err( err$ )
+	If DEBUGSTOP_ON_ERROR Then
+		DebugStop ' useful for debugging!
+	End If
+	Throw "Compile Error: "+err + "~n" + _errInfo + "~n"
+End Function
+
+Function Warn( err$ )
+	'If DEBUGSTOP_ON_ERROR Then
+	'	DebugStop ' useful for debugging!
+	'End If
+	Print "Compile Warning: "+err + "~n" + _errInfo + "~n"
+End Function
+
+Function FormatError:String(path:String, line:Int, char:Int)
+	Return "[" + path + ";" + line + ";" + char + "]"
+End Function
+
+Function InternalErr()
+	If DEBUGSTOP_ON_ERROR Then
+		DebugStop ' useful for debugging!
+	End If
+	Throw "Internal Error.~n" + _errInfo + "~n"
+End Function
+
+Function IsSpace:Int( ch:Int )
+	Return ch<=Asc(" ") Or ch=$A0 ' NO-BREAK SPACE (U+00A0)
+End Function
+
+Function IsDigit:Int( ch:Int )
+	Return ch>=Asc("0") And ch<=Asc("9")
+End Function
+
+Function IsAlpha:Int( ch:Int )
+	Return (ch>=Asc("A") And ch<=Asc("Z")) Or (ch>=Asc("a") And ch<=Asc("z"))
+End Function
+
+Function IsBinDigit:Int( ch:Int )
+	Return ch=Asc("0") Or ch=Asc("1")
+End Function
+
+Function IsHexDigit:Int( ch:Int )
+	Return IsDigit(ch) Or (ch>=Asc("A") And ch<=Asc("F")) Or (ch>=Asc("a") And ch<=Asc("f"))
+End Function
+
+Function Todo() 
+	Err "TODO!"
+End Function
+
+Function IsStandardFunc:Int(func:String)
+	func = func.ToLower()
+	
+	Global funcs:String = ";isalnum;isalpha;isascii;isblank;iscntrl;isdigit;isgraph;islower;isprint;ispunct;isspace;isupper;isxdigit;" + ..
+		"strlen;_wgetenv;_wputenv;"
+	
+	Return funcs.Find(func) > 0
+End Function
+
+Function mapSymbol:String(sym:String)
+	For Local i:Int = 0 Until _symbols.length
+		If sym = _symbols[i] Then
+			Return _symbols_map[i]
+		End If
+	Next
+	Return sym
+End Function
+
+
+'enquote depending on ENV_LANG
+'
+Function LangEnquote$( str$ )
+	str=EscapeString(str)
+'	str=str.Replace( "~0","\0" )	'Fix me?
+	For Local i:Int=0 Until str.Length
+		If str[i]>=32 And str[i]<128 Continue
+		Local t$,n:Int=str[i]
+		While n
+			Local c:Int=(n&15)+48
+			If c>=58 c:+97-58
+			t=Chr( c )+t
+			n=(n Shr 4) & $0fffffff
+		Wend
+		If Not t t="0"
+		If ENV_LANG = "cpp" Then
+		'Case "cpp"
+			t="~q~q\x"+t+"~q~q"
+		Else
+			t="\u"+("0000"+t)[-4..]
+		End If
+		str=str[..i]+t+str[i+1..]
+		i:+t.Length-1
+	Next
+	str="~q"+str+"~q"
+	If ENV_LANG="cpp" str="L"+str
+	Return str
+End Function
+
+Function EscapeString$(str$)
+	str=str.Replace( "\","\\" )
+	str=str.Replace( "~q","\~q" )
+	str=str.Replace( "~n","\n" )
+	str=str.Replace( "~r","\r" )
+	str=str.Replace( "~t","\t" )
+	Return str
+End Function
+
+Function BmxEnquote$( str$ )
+	str=str.Replace( "~~","~~~~" )
+	str=str.Replace( "~q","~~q" )
+	str=str.Replace( "~n","~~n" )
+	str=str.Replace( "~r","~~r" )
+	str=str.Replace( "~t","~~t" )
+	str=str.Replace( "~0","~~0" )
+	str="~q"+str+"~q"
+	Return str
+End Function
+
+Function BmxUnquote$( str$, unicodeConvert:Int = False )
+	If str.length = 1 Or str[str.length - 1] <> Asc("~q") Then
+		Err "Expecting expression but encountered malformed string literal"
+	End If
+	str=str[1..str.Length-1]
+	If unicodeConvert Then
+		Local pos:Int = str.Find("~~")
+		While pos <> -1
+			If pos + 1 < str.length Then
+				If str[pos + 1] >= Asc("1") And str[pos + 1] <= Asc("9") Then
+					Local p2:Int = str.Find("~~", pos + 1)
+					If p2 <> -1 Then
+						Local s:String = Chr(str[pos + 1.. p2].ToInt())
+						str = str[..pos] + s + str[p2 + 1..]
+					End If
+				End If
+			End If
+		
+			pos = str.Find("~~", pos + 1)
+		Wend
+	End If
+	str=str.Replace( "~~~~","~~z" )	'a bit dodgy - uses bad esc sequence ~z 
+	str=str.Replace( "~~q","~q" )
+	str=str.Replace( "~~n","~n" )
+	str=str.Replace( "~~r","~r" )
+	str=str.Replace( "~~t","~t" )
+	str=str.Replace( "~~0","~0" )
+	str=str.Replace( "~~z","~~" )
+	Return str
+End Function
+
+Type TStack Extends TList
+
+	Method Push(obj:Object)
+		AddFirst(obj)
+	End Method
+
+	Method Length:Int()
+		Return count()
+	End Method
+	
+	Method Get:Object(index:Int)
+		Return ValueAtIndex(index)
+	End Method
+	
+	Method Pop:Object()
+		Return RemoveFirst()
+	End Method
+	
+End Type
+
+Type TStringList Extends TList
+	Method Join:String(s:String)
+		Local arr:String[] = New String[count()]
+		Local index:Int
+		For Local t:String = EachIn Self
+			arr[index] = t
+			index :+ 1
+		Next
+		
+		Return s.Join(arr)
+	End Method
+End Type
+
+Type TKeyValue
+	Field key:Object
+	Field value:Object
+	
+	Method Create:TKeyValue(key:Object,value:Object)
+		Self.key = key
+		Self.value = value
+		Return Self
+	End Method
+	
+	Method Compare:Int(other:Object)
+		If Not TKeyValue(other) Return 0
+		Return key.Compare(TKeyValue(other).key)
+	End Method
+	
+End Type
+
+Type TUnorderedMap
+
+	Field list:TList = New TList
+	Field map:TMap = New TMap
+	
+	Field valuesList:TList = New TList
+
+	Method Insert( key:Object,value:Object )
+		list.AddLAst(New TKeyValue.Create(key, value))
+		valuesList.AddLast(value)
+		map.Insert(key, value)
+	End Method
+	
+	Method Keys:TList()
+		Local klist:TList = New TList
+		For Local kv:TKeyValue = EachIn list
+			klist.AddLast(kv.key)
+		Next
+		Return klist
+	End Method
+	
+	Method Values:TList()
+		'Local vlist:TList = New TList
+		'For Local kv:TKeyValue = EachIn list
+		'	vlist.AddLast(kv.value)
+		'Next
+		Return valuesList
+	End Method
+	
+	Method Contains:Int( key:Object )
+		Return map.Contains(key)
+	End Method
+	
+	Method ValueForKey:Object( key:Object )
+		Return map.ValueForKey(key)
+	End Method
+End Type
+
+Function MakeKeywords:String()
+	Local keywords:String
+	
+	keywords :+ "import brl.classes~n"
+	keywords :+ "Asc%(v$)=~qbrl_blitz_keywords_asc~q~n"
+	keywords :+ "Sgn#(v#)=~qbrl_blitz_keywords_sgn~q~n"
+	keywords :+ "Chr$(v%)=~qbrl_blitz_keywords_chr~q~n"
+	keywords :+ "Len%(v:Object)=~qbrl_blitz_keywords_len~q~n"
+	keywords :+ "Min%(v1%,v2%)=~qbrl_blitz_keywords_min~q~n"
+	keywords :+ "Max%(v1%,v2%)=~qbrl_blitz_keywords_max~q~n"
+	'keywords :+ "SizeOf%(v%)=~qbrl_blitz_keywords_sizeof~q~n"
+	'keywords :+ "Incbin(v$)=~qbrl_blitz_keywords_incbin~q~n"
+	keywords :+ "IncbinPtr@*(v$)=~qbbIncbinPtr~q~n"
+	keywords :+ "IncbinLen%(v$)=~qbbIncbinLen~q~n"
+ 
+	Return keywords
+End Function
+
+Function FilePath:String(path:String)
+	Local baseDir:String = ExtractDir(path)
+	Local bmxDir:String = baseDir + "/.bmx"
+	
+	If FileType(bmxDir) <> FILETYPE_DIR Then
+		Throw "Missing : " + bmxDir
+	End If
+	
+	Return bmxDir
+End Function
+
+Function BuildHeaderName:String(path:String)
+	If opt_buildtype = BUILDTYPE_MODULE Then
+		path = opt_modulename + "_" + StripDir(path)
+	Else
+		Local dir:String = ExtractDir(path).ToLower().Replace("/.bmx","")
+		dir = dir[dir.findLast("/") + 1..]
+		If dir.EndsWith(".mod") Then
+			dir = dir.Replace(".mod", "")
+		End If
+		Local file:String = StripDir(path).ToLower()
+		path = dir + "_" + file
+	End If
+	
+	Return TStringHelper.Sanitize(path, , True)
+End Function
+
+Rem
+bbdoc: Get the header file name from a given module ident, optionally with include path.
+End Rem
+Function ModuleHeaderFromIdent:String(ident:String, includePath:Int = False)
+	Local ns:String = ident[..ident.find(".")]
+	Local name:String = ident[ident.find(".") + 1..]
+	
+	Local file:String = name + ".bmx" + FileMung() + ".h"
+	
+	If includePath Then
+		file = ns + ".mod/" + name + ".mod/.bmx/" + file
+	End If
+	
+	Return file
+End Function
+
+Function HeaderFile:String(path:String, mung:String)
+	Local fileDir:String = FilePath(path)
+	Local file:String = StripDir(path)
+	
+	Return fileDir + "/" + file + mung + ".h"
+End Function
+
+Function OutputFilePath:String(path:String, mung:String, suffix:String, bmxDir:Int = False)
+	Local fileDir:String = FilePath(path)
+	If bmxDir Then
+		fileDir :+ "/.bmx"
+	End If
+	Local file:String = StripDir(path)
+	
+	Return fileDir + "/" + file + mung + "." + suffix
+End Function
+
+Function FileMung:String(makeApp:Int = False)
+	Local m:String = "."
+	
+	If makeApp Then
+		Select opt_apptype
+			Case APPTYPE_CONSOLE
+				m :+ "console."
+			Case APPTYPE_GUI
+				m :+ "gui."
+		End Select
+	End If
+	
+	If opt_release Then
+		m :+ "release"
+	Else
+		m :+ "debug"
+	End If
+	
+'	If opt_threaded Then
+'		m :+ ".mt"
+'	End If
+	
+	m :+ "." + opt_platform
+	
+	m :+ "." + opt_arch
+	
+	Return m
+End Function
+
+Function HeaderComment:String()
+	' TODO
+End Function
+
+
+Type TTemplateRecord
+
+	Field start:Int
+	Field file:String
+	Field source:String
+	
+	Method Create:TTemplateRecord(start:Int, file:String, source:String)
+		Self.start = start
+		Self.file = file
+		Self.source = source
+		Return Self
+	End Method
+	
+	Method ToString:String()
+
+		Local s:Byte Ptr = source.ToUTF8String()
+?Not bmxng
+		Local slen:Int = strlen_(s)
+?bmxng
+		Local slen:UInt = strlen_(s)
+?
+
+?Not bmxng		
+		Local dlen:Int = slen + 12
+?bmxng And (win32 Or ptr32)
+		Local dlen:UInt = slen + 12
+?bmxng And ptr64 And Not win32
+		Local dlen:ULong = slen + 12
+?
+		Local data:Byte[dlen]
+		
+		compress2(data, dlen, s, slen, 9)
+		
+		MemFree(s)
+		
+		Local t:String = "{" + start +","+ slen +","+ LangEnquote(file) + ","
+		
+		t :+ LangEnquote(TBase64.Encode(data, Int(dlen), 0, TBase64.DONT_BREAK_LINES))
+
+		Return t + "}"
+
+	End Method
+	
+	Function Load:TTemplateRecord(start:Int, file:String, size:Int, source:String)
+		
+?Not bmxng		
+		Local dlen:Int = size + 1
+?bmxng And (win32 Or ptr32)
+		Local dlen:UInt = size + 1
+?bmxng And ptr64 And Not win32
+		Local dlen:ULong = size + 1
+?
+		Local data:Byte[dlen]
+		
+		Local s:Byte[] = TBase64.Decode(source)
+?Not bmxng
+		uncompress(data, dlen, s, s.length)
+?bmxng
+		uncompress(data, dlen, s, UInt(s.length))
+?	
+		Return New TTemplateRecord.Create(start, file, String.FromUTF8String(data))
+	End Function
+End Type
+
+Type TCallback
+	Method Callback(obj:Object) Abstract
+End Type
+
+Extern
+	Function strlen_:Int(s:Byte Ptr)="strlen"
+End Extern

+ 39 - 15
ctranslator.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -705,6 +705,11 @@ t:+"NULLNULLNULL"
 				initTrans = "=" + cast + init.Trans()
 			End If
 		End If
+		
+		Local volTrans:String = " "
+		If decl.volatile Then
+			volTrans = " volatile "
+		End If
 	
 		If Not declare And opt_debug Then
 			Local ty:TType = decl.ty
@@ -738,21 +743,21 @@ t:+"NULLNULLNULL"
 						If TObjectType(ty).classdecl.IsInterface() Then
 							Return TransType( ty, decl.munged )+" "+decl.munged + initTrans
 						Else
-							Return TransType( ty, decl.munged )+" "+decl.munged + initTrans
+							Return TransType( ty, decl.munged )+ volTrans +decl.munged + initTrans
 						End If
 					Else
 						If TObjectType(ty).classdecl.IsStruct() Then
 							Return TransType( ty, decl.munged )+" "+decl.munged + initTrans
 						Else
-							If decl.volatile Then
-								Return TransType( ty, decl.munged )+" volatile "+decl.munged + initTrans
-							Else
-								Return TransType( ty, decl.munged )+" "+decl.munged + initTrans
-							End If
+							'If decl.volatile Then
+								Return TransType( ty, decl.munged )+ volTrans +decl.munged + initTrans
+							'Else
+							'	Return TransType( ty, decl.munged )+" "+decl.munged + initTrans
+							'End If
 						End If
 					End If
 				Else
-					Return TransType( ty, decl.munged )+" "+decl.munged + initTrans
+					Return TransType( ty, decl.munged )+ volTrans +decl.munged + initTrans
 				End If
 			End If
 		End If
@@ -783,7 +788,11 @@ t:+"NULLNULLNULL"
 					End If
 				End If
 			Else
-				Return TransType( decl.ty, decl.munged )+" "+decl.munged + "=" + TransValue(decl.ty, "")
+				If TLocalDecl(decl) And TLocalDecl(decl).volatile Then
+					Return TransType( decl.ty, decl.munged )+" volatile "+decl.munged + "=" + TransValue(decl.ty, "")
+				Else
+					Return TransType( decl.ty, decl.munged )+" "+decl.munged + "=" + TransValue(decl.ty, "")
+				End If
 			End If
 		End If
 	End Method
@@ -1018,8 +1027,15 @@ t:+"NULLNULLNULL"
 										Return "_" + decl.munged+TransArgs( args,decl, TransSubExpr( lhs ) )
 									End If
 								Else
-									Local class:String = Bra(TransSubExpr( lhs )) + "->clas" + tSuper
-									Return class + "->" + TransFuncPrefix(cdecl, decl) + FuncDeclMangleIdent(decl)+TransArgs( args,decl, TransSubExpr( lhs ) )
+									Local obj:String = TransSubExpr( lhs )
+									Local preObj:String = obj
+									
+									If opt_debug Then
+										preObj = TransDebugNullObjectError(obj, cdecl)
+									End If
+									
+									Local class:String = Bra(preObj) + "->clas" + tSuper
+									Return class + "->" + TransFuncPrefix(cdecl, decl) + FuncDeclMangleIdent(decl)+TransArgs( args,decl, obj )
 '									Local class:String = Bra(lvarInit) + "->clas" + tSuper
 '									Return class + "->" + TransFuncPrefix(cdecl, decl) + FuncDeclMangleIdent(decl)+TransArgs( args,decl, lvar )
 								End If
@@ -1589,7 +1605,11 @@ t:+"NULLNULLNULL"
 			End If
 
 			If expr.instanceExpr Then
-				t = "_" + ctorMunged + "_ObjectNew" + TransArgs( expr.args,expr.ctor, Bra(expr.instanceExpr.Trans()) + "->clas" )
+				If expr.classDEcl.IsStruct() Then
+					t = ctorMunged + "_ObjectNew" + TransArgs( expr.args,expr.ctor)
+				Else
+					t = "_" + ctorMunged + "_ObjectNew" + TransArgs( expr.args,expr.ctor, Bra(expr.instanceExpr.Trans()) + "->clas" )
+				End If
 			Else
 				If ClassHasObjectField(expr.classDecl) And Not expr.classDecl.IsStruct() Then
 					t = "_" + ctorMunged + "_ObjectNew" + TransArgs( expr.args,expr.ctor, "&" + expr.classDecl.actual.munged )
@@ -4391,6 +4411,10 @@ End Rem
 			' field initialisation
 			For Local decl:TFieldDecl=EachIn classDecl.Decls()
 			
+				If Not decl.IsSemanted() Then
+					decl.Semant()
+				End If
+			
 				Local fld:String
 	
 				' ((int*)((char*)o + 5))[0] =
@@ -4749,11 +4773,11 @@ End Rem
 
 	Method TransFieldRef:String(decl:TFieldDecl, variable:String, exprType:TType = Null)
 		Local s:String = variable
-'DebugStop
+		
 		Local ind:String = "->"
 		If decl.scope And TClassDecl(decl.scope) And TClassDecl(decl.scope).IsStruct() Then
-			Local exprIsStruct:Int = TObjectType(exprType) And TObjectType(exprType).classDecl.attrs & CLASS_STRUCT
-			If exprType And (exprIsStruct Or Not IsPointerType(exprType)) And variable <> "o" Then
+			Local exprIsStruct:Int = Not exprType Or (TObjectType(exprType) And TObjectType(exprType).classDecl.attrs & CLASS_STRUCT)
+			If (exprIsStruct Or (exprType And Not IsPointerType(exprType))) And variable <> "o" Then
 				ind = "."
 			End If
 		End If

+ 191 - 87
decl.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -42,6 +42,7 @@ Const DECL_PROTECTED:Int=    $4000000
 Const DECL_API_CDECL:Int=   $00000000
 Const DECL_API_STDCALL:Int= $10000000
 Const DECL_API_DEFAULT:Int=DECL_API_CDECL
+Const DECL_API_FLAGS:Int=   DECL_API_CDECL | DECL_API_STDCALL
 
 Const DECL_NESTED:Int=      $20000000
 
@@ -399,7 +400,7 @@ Type TValDecl Extends TDecl
 					End If
 				End If
 			End If
-			
+
 			ty=declTy.Semant()
 
 			If Not deferInit Then
@@ -555,7 +556,11 @@ Type TConstDecl Extends TValDecl
 	End Method
 
 	Method OnCopy:TDecl(deep:Int = True)
-		Return New TConstDecl.Create( ident,ty,CopyInit(), attrs )
+		If IsSemanted() Then
+			Return New TConstDecl.Create( ident,ty,CopyInit(), attrs )
+		Else
+			Return New TConstDecl.Create( ident, declTy, declInit, attrs)
+		End If
 	End Method
 	
 	Method OnSemant()
@@ -582,9 +587,10 @@ End Type
 Type TLocalDecl Extends TVarDecl
 
 	Field done:Int
-	Field volatile:Int = True
+	Field volatile:Int = False
+	Field declaredInTry:Int
 
-	Method Create:TLocalDecl( ident$,ty:TType,init:TExpr,attrs:Int=0, generated:Int = False, volatile:Int = True )
+	Method Create:TLocalDecl( ident$,ty:TType,init:TExpr,attrs:Int=0, generated:Int = False, volatile:Int = False )
 		Self.ident=ident
 		Self.declTy=ty
 		Self.declInit=init
@@ -599,13 +605,23 @@ Type TLocalDecl Extends TVarDecl
 		decl.scope = scope
 		decl.ty = ty
 		decl.init = init
+		decl.declaredInTry = declaredInTry
 		Return decl
 	End Method
 
 	Method GetDeclPrefix:String()
 		Return "Local "
 	End Method
-	
+
+	Method OnSemant()
+		If declTy Then
+			If TObjectType(declTy) Or TArrayType(declTy) Then
+				volatile = True
+			End If
+		End If
+		Super.OnSemant()
+	End Method
+		
 	Method ToString$()
 		Return GetDeclPrefix() + Super.ToString()
 	End Method
@@ -970,8 +986,11 @@ Type TScopeDecl Extends TDecl
 				End If
 				
 				Local found:Int
+				' remove matching functions from decl list.
+				' a match should match exactly. If an arg is a subclass of another
+				' then that is not a match, and we will distance test later..
 				For Local func:TFuncDecl = EachIn declList
-					If func.equalsFunc(fdecl) Then
+					If func.equalsFunc(fdecl, True) Then
 						found = True
 						Exit
 'Else
@@ -1123,7 +1142,7 @@ Type TScopeDecl Extends TDecl
 		Return decl
 	End Method
 
-	Method FindType:TType( ident$,args:TType[] )
+	Method FindType:TType( ident$,args:TType[], callback:TCallback = Null )
 'DebugLog Self.ident + "::FindType::" + ident
 		Local decl:Object=(GetDecl( ident ))
 		If decl Then
@@ -1139,13 +1158,13 @@ Type TScopeDecl Extends TDecl
 			If cdecl
 				cdecl.AssertAccess
 				If Not cdecl.instanceof Then
-					cdecl=cdecl.GenClassInstance( args )
+					cdecl=cdecl.GenClassInstance( args, False, callback, Null )
 					cdecl.Semant
 				End If
 				Return cdecl.objectType
 			EndIf
 		EndIf
-		If scope Return scope.FindType( ident,args )
+		If scope Return scope.FindType( ident,args, callback )
 	End Method
 	
 	Method FindScopeDecl:TScopeDecl( ident$ )
@@ -1592,7 +1611,20 @@ End Rem
 		
 		If scope Return scope.FindLoop( ident )
 	End Method
-	
+
+	Method FindTry:TTryStmtDecl()
+
+		If TTryStmtDecl(Self) Then
+			Return TTryStmtDecl(Self)
+		End If
+
+		If TFuncDecl(scope) Or TModuleDecl(scope)
+			Return Null
+		End If
+		
+		If scope Return scope.FindTry()
+	End Method
+
 	Method OnSemant()
 	End Method
 	
@@ -1855,12 +1887,17 @@ Type TFuncDecl Extends TBlockDecl
 		Return (attrs & FUNC_FIELD)<>0
 	End Method
 		
-	Method EqualsArgs:Int( decl:TFuncDecl ) ' careful, this is not commutative!
+	' exactMatch requires args to be equal. If an arg is a subclass, that is not a match.
+	Method EqualsArgs:Int( decl:TFuncDecl, exactMatch:Int = False ) ' careful, this is not commutative!
 		If argDecls.Length<>decl.argDecls.Length Return False
 		For Local i:Int=0 Until argDecls.Length
+			' ensure arg decls have been semanted
+			decl.argDecls[i].Semant()
+			argDecls[i].Semant()
+			
 			' objects can be subclasses as well as the same.
 			If TObjectType(decl.argDecls[i].ty) Then
-				If Not decl.argDecls[i].ty.EqualsType( argDecls[i].ty ) And Not decl.argDecls[i].ty.ExtendsType( argDecls[i].ty ) Return False
+				If Not decl.argDecls[i].ty.EqualsType( argDecls[i].ty ) And (exactMatch Or Not decl.argDecls[i].ty.ExtendsType( argDecls[i].ty )) Return False
 			Else
 				If Not decl.argDecls[i].ty.EqualsType( argDecls[i].ty ) Return False
 			End If
@@ -1868,12 +1905,13 @@ Type TFuncDecl Extends TBlockDecl
 		Return True
 	End Method
 
-	Method EqualsFunc:Int( decl:TFuncDecl ) ' careful, this is not commutative!
+	' exactMatch requires args to be equal. If an arg is a subclass, that is not a match.
+	Method EqualsFunc:Int( decl:TFuncDecl, exactMatch:Int = False) ' careful, this is not commutative!
 		If IsCtor() Then
-			Return EqualsArgs( decl )
+			Return EqualsArgs( decl, exactMatch )
 		Else
 			' matching args?
-			If EqualsArgs( decl ) Then
+			If EqualsArgs( decl, exactMatch ) Then
 				' matching return type?
 				If TObjectType(retType) Or TArrayType(retType) Or TStringType(retType) Then
 					Return retType.EqualsType( decl.retType ) Or retType.ExtendsType( decl.retType )' Or decl.retType.EqualsType( retType )) And EqualsArgs( decl )
@@ -1923,7 +1961,6 @@ Type TFuncDecl Extends TBlockDecl
 					End If
 				End If
 			End If
-		
 			retType=retTypeExpr.Semant()
 			
 			' for Strict code, a void return type becomes Int
@@ -1936,7 +1973,7 @@ Type TFuncDecl Extends TBlockDecl
 		If TArrayType( retType ) And Not retType.EqualsType( retType.ActualType() )
 '			Err "Return type cannot be an array of generic objects."
 		EndIf
-		
+
 		'semant args
 		For Local arg:TArgDecl=EachIn argDecls
 			InsertDecl arg
@@ -2176,6 +2213,16 @@ Type TNullDecl Extends TClassDecl
 
 End Type
 
+' used to handle recursive generics
+' by setting the superclass as soon as we know it,
+' which allows the semanting of the instance to complete.
+Type TClassDeclCallback Extends TCallback
+	Field decl:TClassDecl
+	Method callback(obj:Object)
+		decl.superClass = TClassDecl(obj)
+	End Method
+End Type
+
 Type TClassDecl Extends TScopeDecl
 
 	Field lastOffset:Int
@@ -2300,7 +2347,7 @@ Rem
 		Return inst
 	End Method
 End Rem
-	Method GenClassInstance:TClassDecl( instArgs:TType[], declImported:Int = False )
+	Method GenClassInstance:TClassDecl( instArgs:TType[], declImported:Int = False, callback:TCallback = Null, templateDets:TTemplateDets = Null )
 
 		If instanceof InternalErr
 		
@@ -2312,9 +2359,34 @@ End Rem
 			Next
 		EndIf
 		
+		Local originalInstArgs:TType[] = instArgs
+		
 		'check number of args
-		If args.Length<>instArgs.Length
-			Err "Wrong number of type arguments for class "+ToString()
+		If args.Length<>instArgs.Length Then
+			If Not templateDets Or args.Length > instArgs.Length Then
+				Err "Wrong number of type arguments for class "+ToString()
+			Else
+				' create new instArgs with matched aliases
+				Local newInstArgs:TType[] = New TType[args.length]
+				For Local i:Int = 0 Until args.length
+					Local arg:TTemplateArg = args[i]
+					Local instArg:TType
+					' find match
+					For Local n:Int = 0 Until templateDets.args.length
+						Local templateArg:TTemplateArg = templateDets.args[n]
+						If templateArg.ident.ToLower() = arg.ident.ToLower() Then
+							instArg = instArgs[n]
+							Exit
+						End If
+					Next
+					If Not instArg Then
+						Err "Cannot find argument type '" + arg.ident + "' for class " + ToString()
+					End If
+					newInstArgs[i] = instArg
+				Next
+				
+				instArgs = newInstArgs
+			End If
 		EndIf
 		
 		'look for existing instance
@@ -2328,8 +2400,19 @@ End Rem
 			Next
 			If equal Return inst
 		Next
+		
+		If Not templateDets Then
+			' pass in the original instargs, as an inner-inner type will be able to see all of the originals
+			templateDets = New TTemplateDets.Create(originalInstArgs, args)
+		End If
 
-		Local inst:TClassDecl=New TClassDecl.Create( ident,Null,superTy,impltys, attrs )
+		Local inst:TClassDecl = TClassDecl(TGenProcessor.processor.ParseGeneric(templateSource, templateDets))
+		inst.ident=ident
+		inst.args=Null
+		inst.instances = Null
+		inst.superTy=superTy
+		inst.impltys=impltys
+		inst.attrs=attrs
 
 		inst.attrs:&~DECL_SEMANTED
 
@@ -2343,26 +2426,52 @@ End Rem
 		
 		inst.declImported = declImported
 
+		If callback Then
+			callback.callback(inst)
+		End If
+
+		PushEnv inst
+		
+		' install aliases
+		For Local i:Int=0 Until args.Length
+			inst.InsertDecl New TAliasDecl.Create( args[i].ident,instArgs[i],0 )
+		Next
+
+		' process parameter types
 		For Local i:Int=0 Until args.Length
 		
+			Local arg:TTemplateArg = args[i]
+
 			' ensure parameter types are compatible
-			If args[i].superTy Then
-				args[i].superTy = args[i].superTy.Semant()
-				If Not instArgs[i].EqualsType(args[i].superTy) And Not instArgs[i].ExtendsType(args[i].superTy) Then
-					Err "Type parameter '" + instArgs[i].ToString() + "' is not within its bound; should extend '" + args[i].superTy.ToString() + "'"
+			If arg.superTy Then
+
+				'If Not instArgs[i].IsSemanted() Then
+				If TObjectType(instArgs[i]) Then
+					TObjectType(instArgs[i]).classDecl.Semant()
 				End If
+				'End If
+			
+				For Local n:Int = 0 Until arg.superTy.length
+
+					arg.superTy[n] = arg.superTy[n].Semant()
+
+					If Not instArgs[i].EqualsType(arg.superTy[n]) And Not instArgs[i].ExtendsType(arg.superTy[n]) Then
+						Err "Type parameter '" + instArgs[i].ToString() + "' is not within its bound; should extend '" + arg.superTy[n].ToString() + "'"
+					End If
+				Next
 			End If
 		
-			inst.InsertDecl New TAliasDecl.Create( args[i].ident,instArgs[i],0 )
 		Next
+		
+		PopEnv
 
-		For Local decl:TDecl=EachIn _decls
-			If TClassDecl(decl) Then
-				inst.InsertDecl TClassDecl(decl).GenClassInstance(instArgs, declImported), True
-			Else
-				inst.InsertDecl decl.Copy(), True
-			End If
-		Next
+'		For Local decl:TDecl=EachIn _decls
+'			If TClassDecl(decl) Then
+'				inst.InsertDecl TClassDecl(decl).GenClassInstance(instArgs, declImported), True
+'			Else
+'				inst.InsertDecl decl.Copy(), True
+'			End If
+'		Next
 
 		If Not declImported Then
 			inst.scope = _env.ModuleScope()
@@ -2680,9 +2789,11 @@ End Rem
 
 		'Semant superclass		
 		If superTy
-			'superClass=superTy.FindClass()
+			Local cb:TClassDeclCallback = New TClassDeclCallback
+			cb.decl = Self
+			
 			attrs :| DECL_CYCLIC
-			superClass=superTy.SemantClass()
+			superClass=superTy.SemantClass(cb)
 			attrs :~ DECL_CYCLIC
 			If superClass.IsInterface() Then
 				If Not IsExtern() Or Not superClass.IsExtern() Err superClass.ToString()+" is an interface, not a class."
@@ -2742,18 +2853,7 @@ End Rem
 		'If IsTemplateInst()
 		'	Return
 		'EndIf
-		
-		'Are we abstract?
-		If Not IsAbstract()
-			For Local decl:TDecl=EachIn _decls
-				Local fdecl:TFuncDecl=TFuncDecl( decl )
-				If fdecl And fdecl.IsAbstract()
-					attrs:|DECL_ABSTRACT
-					Exit
-				EndIf
-			Next
-		EndIf
-		
+				
 		If Not lastOffset And superClass Then
 			lastOffset = superClass.LastOffset
 		End If
@@ -2895,45 +2995,23 @@ End Rem
 		PushErr errInfo
 		
 		If Not IsInterface()
-			'
-			'check for duplicate fields! - BlitzMax supports fields with the same name in subclasses..
-			'
-			'For Local decl:TDecl=EachIn Semanted()
-			'	Local fdecl:TFieldDecl=TFieldDecl( decl )
-			'	If Not fdecl Continue
-			'	Local cdecl:TClassDecl=superClass
-			'	While cdecl
-			'		For Local decl:TDecl=EachIn cdecl.Semanted()
-			'			If decl.ident=fdecl.ident Err "Field '"+fdecl.ident+"' in class "+ToString()+" overrides existing declaration in class "+cdecl.ToString()
-			'		Next
-			'		cdecl=cdecl.superClass
-			'	Wend
-			'Next
-			'
-			'Check we implement all abstract methods!
-			'
+
+			' BlitzMax types are promoted to Abstract if they have an abstract method
+			If Not IsAbstract()
+				For Local fdecl:TFuncDecl = EachIn GetAllFuncDecls()
+					If fdecl.IsMethod() And fdecl.IsAbstract()
+						attrs:|DECL_ABSTRACT
+					End If
+				Next
+			End If
+
+			' Check we implement all abstract methods!
 			If IsInstanced()
-				Local cdecl:TClassDecl=Self
-				Local impls:TList=New TList'<TFuncDecl>
-				While cdecl
-					For Local decl:TFuncDecl=EachIn cdecl.SemantedMethods()
-						If decl.IsAbstract()
-							Local found:Int
-							For Local decl2:TFuncDecl=EachIn impls
-								If decl.IdentLower() = decl2.IdentLower() And decl2.EqualsFunc( decl )
-									found=True
-									Exit
-								EndIf
-							Next
-							If Not found
-								Err "Can't create instance of type "+ToString()+" due to abstract method "+decl.ToString()+"."
-							EndIf
-						Else
-							impls.AddLast decl
-						EndIf
-					Next
-					cdecl=cdecl.superClass
-				Wend
+				For Local fdecl:TFuncDecl = EachIn GetAllFuncDecls()
+					If fdecl.IsAbstract() Then
+						Err "Can't create instance of type "+ToString()+" due to abstract "+fdecl.ToString()+"."
+					End If
+				Next
 			EndIf
 			'
 			'Check we implement all interface methods!
@@ -3192,6 +3270,10 @@ Type TDefDataDecl Extends TDecl
 	
 End Type
 
+Type TTryStmtDecl Extends TBlockDecl
+	Field tryStmt:TTryStmt
+End Type
+
 Const MODULE_STRICT:Int=1
 Const MODULE_SUPERSTRICT:Int=2
 Const MODULE_ACTUALMOD:Int=4
@@ -3301,7 +3383,7 @@ Type TModuleDecl Extends TScopeDecl
 		
 			For Local mdecl:TModuleDecl = EachIn _getDeclTreeCache
 
-				If ident = mdecl.ident
+				If ident = mdecl.ident And mdecl <> Self
 					_getDeclCache.Insert(ident, mdecl)
 					Return mdecl
 				End If
@@ -3674,3 +3756,25 @@ Type TStringConst
 	Field count:Int
 
 End Type
+
+Type TTemplateDets
+
+	Field instArgs:TType[]
+	Field args:TTemplateArg[]
+
+	Method Create:TTemplateDets(instArgs:TType[], args:TTemplateArg[])
+		Self.instArgs = instArgs
+		Self.args = args
+		Return Self
+	End Method
+
+End Type
+
+Type TGenProcessor Abstract
+
+	Global processor:TGenProcessor
+
+	Method ParseGeneric:Object(templ:TTemplateRecord, dets:TTemplateDets)
+	End Method
+	
+End Type

+ 75 - 8
expr.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -163,6 +163,16 @@ Type TExpr
 						stmt.exprType = TNewObjectExpr(args[i]).ty
 						args[i] = stmt
 					End If
+
+					' passing a non volatile local as Var from within a Try block?					
+					If TVarExpr(args[i]) Then
+						Local ldecl:TLocalDecl = TLocalDecl(TVarExpr(args[i]).decl)
+						If ldecl Then
+							If Not ldecl.volatile And Not ldecl.declaredInTry And _env.FindTry() Then
+								ldecl.volatile = True
+							End If
+						End If
+					End If
 				End If
 				
 				If (funcDecl.argDecls[i].ty._flags & TType.T_VAR) And Not (funcDecl.argDecls[i].ty.EqualsType(args[i].exprType)) Then
@@ -201,6 +211,7 @@ Type TExpr
 				If TObjectType(rhs) And TObjectType(rhs).classDecl.ident = "Object" Then
 					Return rhs
 				End If
+				Return New TStringType
 			Else
 				Return New TStringType
 			End If
@@ -814,6 +825,8 @@ Type TNewObjectExpr Extends TExpr
 		Local it:TIdentType = TIdentType(ty)
 		Local iArgs:TExpr[] = SemantArgs(CopyArgs(args))
 
+		Local isNewSelf:Int = (it And it.ident = "self")
+		
 		ty=ty.Semant(True)
 		If Not ty Then
 			' maybe it's an instance of a type ?
@@ -850,7 +863,8 @@ Type TNewObjectExpr Extends TExpr
 
 		If Not instanceExpr Then
 			If classDecl.IsInterface() Err "Cannot create instance of an interface."
-			If classDecl.IsAbstract() Err "Cannot create instance of an abstract class."
+			If classDecl.IsAbstract() Err "Cannot create instance of abstract type " + classDecl.ToString() + ..
+				", which is either declared Abstract or has (or inherits) an abstract Method."
 		End If
 		'If classDecl.IsTemplateArg() Err "Cannot create instance of a generic argument."
 		If classDecl.args And Not classDecl.instanceof Err "Cannot create instance of a generic class."
@@ -876,7 +890,11 @@ Type TNewObjectExpr Extends TExpr
 			End If
 		EndIf
 
-		classDecl.attrs:|CLASS_INSTANCED
+		' New Self doesn't necessarily create an instance of ourself - we might be an instance of
+		' a subclass at the time...
+		If Not isNewSelf Then
+			classDecl.attrs:|CLASS_INSTANCED
+		End If
 
 		If TClassType(ty) Then
 			exprType=New TObjectType.Create(TClassType(ty).classDecl)
@@ -1486,6 +1504,11 @@ Type TCastExpr Extends TExpr
 			Return Self
 		End If
 
+		If TObjectType(ty) And TObjectType(src) And TObjectType(ty).classdecl.IsInterface() And flags & CAST_EXPLICIT Then
+			exprType = ty
+			Return Self
+		End If
+
 		If Not exprType
 			Err "Unable to convert from "+src.ToString()+" to "+ty.ToString()+"."
 		EndIf
@@ -1593,6 +1616,25 @@ Type TUnaryExpr Extends TExpr
 	Method Semant:TExpr()
 		If exprType Return Self
 
+		expr = expr.Semant()
+
+		' operator overload?
+		If TObjectType(expr.exprType) And (op = "+" Or op = "-" Or op = "~~") Then
+			'Local args:TExpr[] = [rhs]
+			Try
+				Local decl:TFuncDecl = TFuncDecl(TObjectType(expr.exprType).classDecl.FindFuncDecl(op, Null,,,,True,SCOPE_CLASS_HEIRARCHY))
+				If decl Then
+					Return New TInvokeMemberExpr.Create( expr, decl, Null ).Semant()
+				End If
+			Catch error:String
+				If error.StartsWith("Compile Error") Then
+					Throw error
+				Else
+					Err "Operator " + op + " cannot be used with Objects."
+				End If
+			End Try
+		End If
+
 		Select op
 		Case "+","-"
 			expr=expr.Semant()
@@ -1782,8 +1824,16 @@ Type TBinaryMathExpr Extends TBinaryExpr
 			Select op
 			Case "^" Return x^y
 			Case "*" Return x*y
-			Case "/" Return x/y
-			Case "mod" Return x Mod y
+			Case "/" 
+				If Not y Then
+					Err "Integer division by zero"
+				End If
+				Return x/y
+			Case "mod"
+				If Not y Then
+					Err "Integer division by zero"
+				End If
+				Return x Mod y
 			Case "shl" Return x Shl y
 			Case "shr" Return x Shr y
 			Case "+" Return x + y
@@ -1797,8 +1847,16 @@ Type TBinaryMathExpr Extends TBinaryExpr
 			Select op
 			Case "^" Return x^y
 			Case "*" Return x*y
-			Case "/" Return x/y
-			Case "mod" Return x Mod y
+			Case "/"
+				If Not y Then
+					Err "Integer division by zero"
+				End If
+				Return x/y
+			Case "mod"
+				If Not y Then
+					Err "Integer division by zero"
+				End If
+				Return x Mod y
 			Case "shl" Return x Shl y
 			Case "shr" Return x Shr y
 			Case "+" Return x + y
@@ -2504,8 +2562,16 @@ Type TIdentExpr Extends TExpr
 		End If
 		
 		If vdecl
+		
+			If op And TLocalDecl( vdecl )
+
+				Local ldecl:TLocalDecl = TLocalDecl( vdecl )
+
+				If Not ldecl.volatile And Not ldecl.declaredInTry And scope.FindTry() Then
+					ldecl.volatile = True
+				End If
 
-			If TConstDecl( vdecl )
+			Else If TConstDecl( vdecl )
 '				If rhs Err "Constant '"+ident+"' cannot be modified."
 '				Return New TConstExpr.Create( vdecl.ty,TConstDecl( vdecl ).value ).Semant()
 				If rhs Err "Constant '"+ident+"' cannot be modified."
@@ -2724,6 +2790,7 @@ Type TIdentExpr Extends TExpr
 			If expr And Not static Then
 				Return New TInvokeMemberExpr.Create( expr,fdecl,args ).Semant()
 			Else
+				If fdecl.IsStatic() And fdecl.IsAbstract() Err "Cannot call abstract " + fdecl.ToString()
 				Return New TInvokeExpr.Create( fdecl,args, funcCall ).Semant()
 			End If
 		EndIf

+ 1 - 1
iparser.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 ' 

+ 2 - 2
options.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -25,7 +25,7 @@ SuperStrict
 
 Import "base.configmap.bmx"
 
-Const version:String = "0.92"
+Const version:String = "0.94"
 
 Const BUILDTYPE_APP:Int = 0
 Const BUILDTYPE_MODULE:Int = 1

+ 99 - 24
parser.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -336,7 +336,7 @@ Type TIncbin
 End Type
 
 '***** Parser *****
-Type TParser
+Type TParser Extends TGenProcessor
 
 	Field _toker:TToker
 	Field _toke:String
@@ -514,7 +514,7 @@ Type TParser
 
 	Method ParseIdentType:TIdentType()
 		Local id$=ParseIdent()
-'DebugLog "ParseIdentType : " + id
+
 		If CParse( "." ) id:+"."+ParseIdent()
 		If CParse( "." ) id:+"."+ParseIdent()
 
@@ -522,8 +522,19 @@ Type TParser
 		If CParse( "<" )
 			Local nargs:Int
 			Repeat
-				'Local arg:TIdentType=ParseIdentType()
 				Local arg:TType = ParseType()
+				
+				Repeat
+					If (_toke = "[" Or _toke = "[]") And IsArrayDef()
+						arg = ParseArrayType(arg)
+					Else If _toke = "(" Then
+						Local argDecls:TArgDecl[] = ParseFuncParamDecl()
+						arg = New TFunctionPtrType.Create(New TFuncDecl.CreateF("", arg, argDecls, FUNC_PTR))
+					Else
+						Exit
+					End If
+				Forever
+				
 				If args.Length=nargs args=args+ New TType[10]
 				args[nargs]=arg
 				nargs:+1
@@ -2022,7 +2033,11 @@ End Rem
 	Method ParseTryStmt()
 		Parse "try"
 
-		Local block:TBlockDecl=New TBlockDecl.Create( _block )
+		Local tryStmtDecl:TTryStmtDecl = TTryStmtDecl(New TTryStmtDecl.Create( _block ))
+		
+		PushBlock tryStmtDecl
+
+		Local block:TBlockDecl=New TBlockDecl.Create( tryStmtDecl )
 		Local catches:TList=New TList
 		Local finallyStmt:TFinallyStmt = Null
 
@@ -2068,16 +2083,23 @@ End Rem
 			End If
 		Wend
 
-		PopBlock
-		
 		If catches.Count() = 0 And Not finallyStmt Then Err "Expecting 'Catch' or 'Finally'."
 		
+		PopBlock ' try block
+		
 		If Not CParse("endtry") Then
 			NextToke
 			CParse "try"
 		End If
 
-		_block.AddStmt New TTryStmt.Create( block,TCatchStmt[](catches.ToArray()),finallyStmt )
+		PopBlock ' tryStmtDecl
+		
+		Local tryStmt:TTryStmt = New TTryStmt.Create(block,TCatchStmt[](catches.ToArray()), finallyStmt)
+
+		tryStmtDecl.tryStmt = tryStmt
+
+		_block.AddStmt tryStmt
+		
 	End Method
 
 	Method ParseThrowStmt()
@@ -2666,9 +2688,9 @@ End Rem
 				NextToke
 				
 				Select t
-					Case "*","/","+","-","&","|","~~"
+					Case "*","/","+","-","&","|","~~","^"
 						id = t
-					Case ":*",":/",":+",":-",":&",":|",":~~"
+					Case ":*",":/",":+",":-",":&",":|",":~~",":^"
 						id = t
 					Case "<",">"',"="',"<=",">=","=","<>"
 						If CParse("=") Then
@@ -2724,9 +2746,9 @@ End Rem
 			Local fdecl:TFuncDecl = TFunctionPtrType(ty).func
 			ty = fdecl.retTypeExpr
 			args = fdecl.argDecls
+			attrs :| (fdecl.attrs & DECL_API_FLAGS)
 		End If
 		
-		
 		If CParse( "nodebug" ) Then
 			attrs :| DECL_NODEBUG
 		End If
@@ -2889,13 +2911,14 @@ End Rem
 		Local api:String = ParseStringLit().ToLower()
 		
 		If api = "os" Then
-?win32
-			api = "win32"
-?macos
-			api = "macos"
-?linux
-			api = "linux"
-?
+			Select opt_platform
+				Case "macos", "osx", "ios"
+					api = "macos"
+				Case "linux", "android", "raspberrypi"
+					api = "linux"
+				Case "win32"
+					api = "win32"
+			End Select
 		End If
 
 		Select api
@@ -2944,7 +2967,7 @@ End Rem
 	End Method
 	
 
-	Method ParseClassDecl:TClassDecl( toke$,attrs:Int )
+	Method ParseClassDecl:TClassDecl( toke$,attrs:Int, templateDets:TTemplateDets = Null )
 		SetErr
 
 		Local calculatedStartLine:Int = _toker.Line()
@@ -2986,9 +3009,9 @@ End Rem
 				Local arg:TTemplateArg = New TTemplateArg
 				arg.ident = ParseIdent()
 				
-				If CParse("extends") Then
-					arg.superTy = ParseIdentType()
-				End If
+'				If CParse("extends") Then
+'					arg.superTy = ParseIdentType()
+'				End If
 				
 				args.AddLast arg
 
@@ -2996,6 +3019,34 @@ End Rem
 			'args=args[..nargs]
 
 			Parse ">"
+
+			If CParse( "where" ) Then
+'DebugStop
+				Repeat
+					Local argIdent:String = ParseIdent()
+					
+					Parse("extends")
+					
+					Local found:Int
+					For Local arg:TTemplateArg = EachIn args
+						If arg.ident = argIdent Then
+						
+							Repeat
+							
+								arg.ExtendsType(ParseIdentType())
+							
+							Until Not CParse("and")
+						
+							found = True
+							Exit
+						EndIf
+					Next
+					If Not found Then
+						Err "Use of undeclared type '" + argIdent + "'."
+					End If
+					
+				Until Not CParse(",")
+			End If
 		EndIf
 
 		If CParse( "extends" )
@@ -3234,7 +3285,13 @@ End Rem
 				Local decl:TFuncDecl=ParseFuncDecl( _toke,decl_attrs,classDecl )
 				classDecl.InsertDecl decl
 			Case "type"
-				classDecl.InsertDecl ParseClassDecl( _toke,DECL_NESTED)
+				If templateDets Then
+					Local cdecl:TClassDecl = ParseClassDecl( _toke,DECL_NESTED, templateDets)
+					cdecl = cdecl.GenClassInstance(templateDets.instArgs, False, Null, templateDets)
+					classDecl.InsertDecl cdecl, True
+				Else
+					classDecl.InsertDecl ParseClassDecl( _toke,DECL_NESTED)
+				End If
 			Default
 				Err "Syntax error - expecting class member declaration, not '" + _toke + "'"
 			End Select
@@ -3687,6 +3744,24 @@ End Rem
 		Return attrs
 	End Method
 
+	Method ParseGeneric:Object(templateSource:TTemplateRecord, templateDets:TTemplateDets)
+		Local toker:TToker = New TToker.Create(templateSource.file, templateSource.source, False, templateSource.start)
+		Local parser:TParser = New TParser.Create( toker, _appInstance )
+		
+		Local m:TModuleDecl = New TModuleDecl
+		parser._module = m
+		
+		Local cdecl:TClassDecl = Null
+		
+		Select parser._toke
+		Case "type"
+			cdecl = parser.ParseClassDecl(parser._toke,0, templateDets )
+		Case "interface"
+			cdecl = parser.ParseClassDecl(parser._toke, CLASS_INTERFACE|DECL_ABSTRACT, templateDets )
+		End Select
+		
+		Return cdecl
+	End Method
 
 	Method ParseMain()
 
@@ -3993,7 +4068,7 @@ End Rem
 			con = 0
 			Try
 				If Eval( toker,New TIntType ) = "1" con = 1
-			Catch error:String
+			Catch Error:String
 				con = 0
 			End Try
 

+ 11 - 2
stmt.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -77,6 +77,10 @@ Type TDeclStmt Extends TStmt
 	End Method
 	
 	Method OnSemant()
+		If TLocalDecl(decl) And _env.FindTry() Then
+			TLocalDecl(decl).declaredInTry = True
+		End If
+		
 		decl.Semant
 		' if scope is already set, don't try to add it to the current scope.
 		If Not decl.scope Then
@@ -116,6 +120,7 @@ Type TAssignStmt Extends TStmt
 			TIdentExpr(rhs).isRhs = True
 		End If
 		rhs=rhs.Semant()
+
 		lhs=lhs.SemantSet( op,rhs )
 		If TInvokeExpr( lhs ) Or TInvokeMemberExpr( lhs )
 			rhs=Null
@@ -550,7 +555,11 @@ Type TRepeatStmt Extends TLoopStmt
 	End Method
 
 	Method OnCopy:TStmt( scope:TScopeDecl )
-		Return New TRepeatStmt.Create( block.CopyBlock( scope ),expr.Copy(),TLoopLabelDecl(loopLabel.Copy()) )
+		If loopLabel Then
+			Return New TRepeatStmt.Create( block.CopyBlock( scope ),expr.Copy(),TLoopLabelDecl(loopLabel.Copy()) )
+		Else
+			Return New TRepeatStmt.Create( block.CopyBlock( scope ),expr.Copy(),Null )
+		End If
 	End Method
 	
 	Method OnSemant()

+ 1 - 1
stringbuffer_common.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2016-2017 Bruce A Henderson
+' Copyright (c) 2016-2018 Bruce A Henderson
 ' 
 ' Permission is hereby granted, free of charge, to any person obtaining a copy
 ' of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
stringbuffer_core.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2016-2017 Bruce A Henderson
+' Copyright (c) 2016-2018 Bruce A Henderson
 ' 
 ' Permission is hereby granted, free of charge, to any person obtaining a copy
 ' of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
stringbuffer_glue.c

@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2016-2017 Bruce A Henderson
+  Copyright (c) 2016-2018 Bruce A Henderson
  
   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal

+ 2 - 2
toker.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -49,7 +49,7 @@ Type TToker
 		"and,or,shl,shr,sar,end,if,then,else,elseif,endif,while,wend,repeat,until,forever,for,to,step," + ..
 		"next,return,alias,rem,endrem,throw,assert,try,catch,finally,nodebug,incbin,endselect,endmethod," + ..
 		"endfunction,endtype,endextern,endtry,endwhile,pi,release,defdata,readdata,restoredata,interface," + ..
-		"endinterface,implements,size_t,uint,ulong,struct,endstruct,operator"
+		"endinterface,implements,size_t,uint,ulong,struct,endstruct,operator,where"
 	Global _keywords:TMap
 
 	Field _path$

+ 1 - 1
transform.c

@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017 Bruce A Henderson
+/* Copyright (c) 2014-2018 Bruce A Henderson
 
   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any damages

+ 5 - 1
translator.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -380,6 +380,8 @@ Type TTranslator
 				Return "_or"
 			Case "~~"
 				Return "_xor"
+			Case "^"
+				Return "_pow"
 			Case ":*"
 				Return "_muleq"
 			Case ":/"
@@ -394,6 +396,8 @@ Type TTranslator
 				Return "_oreq"
 			Case ":~~"
 				Return "_xoreq"
+			Case ":^"
+				Return "_poweq"
 			Case "<"
 				Return "_lt"
 			Case "<="

+ 34 - 19
type.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2013-2017 Bruce A Henderson
+' Copyright (c) 2013-2018 Bruce A Henderson
 '
 ' Based on the public domain Monkey "trans" by Mark Sibly
 '
@@ -52,7 +52,7 @@ Type TType
 		Return T_MAX_DISTANCE
 	End Method
 	
-	Method Semant:TType(option:Int = False)
+	Method Semant:TType(option:Int = False, callback:TCallback = Null)
 		Return Self
 	End Method
 
@@ -1352,7 +1352,7 @@ Type TStringType Extends TType
 		Return cdecl
 	End Method
 	
-	Method Semant:TType(option:Int = 0)
+	Method Semant:TType(option:Int = 0, callback:TCallback = Null)
 		GetClass()
 		Return Self
 	End Method
@@ -1378,9 +1378,10 @@ Type TArrayType Extends TType
 	Field elemType:TType
 	Field dims:Int
 	
-	Method Create:TArrayType( elemType:TType, dims:Int = 1 )
+	Method Create:TArrayType( elemType:TType, dims:Int = 1, flags:Int = 0 )
 		Self.elemType=elemType
 		Self.dims = dims
+		Self._flags = flags
 		Return Self
 	End Method
 	
@@ -1400,9 +1401,9 @@ Type TArrayType Extends TType
 		Return (arrayType And dims = arrayType.dims And ( TVoidType( elemType ) Or (TObjectType(elemType) And elemType.EqualsType( arrayType.elemType ) Or elemType.ExtendsType( arrayType.elemType )))) Or IsPointerType(ty, 0, TType.T_POINTER) <> Null Or (TObjectType( ty ) And TObjectType( ty ).classDecl.ident="Object")
 	End Method
 	
-	Method Semant:TType(option:Int = False)
-		Local ty:TType=elemType.Semant()
-		If ty<>elemType Return New TArrayType.Create( ty, dims )
+	Method Semant:TType(option:Int = False, callback:TCallback = Null)
+		Local ty:TType=elemType.Semant(option, callback)
+		If ty<>elemType Return New TArrayType.Create( ty, dims, _flags )
 		Return Self
 	End Method
 	
@@ -1538,13 +1539,13 @@ Type TIdentType Extends TType
 	'End Method
 	
 	
-	Method Semant:TType(ignoreNotFoundError:Int = 0)
+	Method Semant:TType(ignoreNotFoundError:Int = 0, callback:TCallback = Null)
 'If ident="IPair" DebugStop
 		If Not ident Return TType.nullObjectType
 
 		Local targs:TType[args.Length]
 		For Local i:Int=0 Until args.Length
-			targs[i]=args[i].Semant()
+			targs[i]=args[i].Semant(ignoreNotFoundError, callback)
 		Next
 		
 		Local tyid$,ty:TType
@@ -1571,13 +1572,13 @@ Type TIdentType Extends TType
 			End If
 			
 			If Not ty Then
-				ty=_env.FindType( tyid,targs )
+				ty=_env.FindType( tyid,targs, callback )
 			End If
 
 			' finally scan all modules for it
 			If Not ty Then
 				For Local mdecl:TModuleDecl = EachIn _appInstance.globalImports.Values()
-					ty=mdecl.FindType( tyid,targs )
+					ty=mdecl.FindType( tyid,targs, callback )
 					If ty Exit
 				Next
 			End If
@@ -1606,7 +1607,7 @@ Type TIdentType Extends TType
 			End If
 			
 			If Not ty Then
-				ty=_env.FindType( tyid,targs )
+				ty=_env.FindType( tyid,targs, callback )
 			End If
 			
 			If Not ty Then
@@ -1614,7 +1615,7 @@ Type TIdentType Extends TType
 		
 				' try scope search first
 				tyid=id[..i]
-				ty=_env.FindType( tyid,targs )				
+				ty=_env.FindType( tyid,targs, callback )				
 
 				If Not ty Then
 					' no? now try module search
@@ -1622,7 +1623,7 @@ Type TIdentType Extends TType
 					Local mdecl:TModuleDecl=_env.FindModuleDecl( modid )
 					If Not mdecl Err "Module '"+modid+"' not found"
 					tyid=id[i+1..]
-					ty=mdecl.FindType( tyid,targs )
+					ty=mdecl.FindType( tyid,targs, callback )
 				End If
 			End If
 		EndIf
@@ -1650,8 +1651,8 @@ Type TIdentType Extends TType
 		Return ty
 	End Method
 
-	Method SemantClass:TClassDecl()
-		Local ty:TObjectType=TObjectType( Semant() )
+	Method SemantClass:TClassDecl(callback:TCallback = Null)
+		Local ty:TObjectType=TObjectType( Semant(False, callback) )
 		If Not ty Err "Type is not a class"
 		Return ty.classDecl
 	End Method
@@ -1804,7 +1805,7 @@ Type TFunctionPtrType Extends TType
 		Return ty
 	End Method
 
-	Method Semant:TType(option:Int = False)
+	Method Semant:TType(option:Int = False, callback:TCallback = Null)
 		func.Semant()
 		Return Self
 	End Method
@@ -2012,12 +2013,26 @@ End Type
 
 Type TTemplateArg
 	Field ident:String
-	Field superTy:TType
+	Field superTy:TType[]
+	
+	Method ExtendsType(ty:TType)
+		If Not superTy Then
+			superTy = New TType[0]
+		End If
+		
+		superTy :+ [ty]
+	End Method
 	
 	Method ToString:String()
 		Local s:String = ident
 		If superTy Then
-			s :+ " Extends " + superTy.ToString()
+			s :+ " Extends "
+			For Local i:Int = 0 Until superTy.length
+				If i Then
+					s:+ " And "
+				End If
+				s :+ superTy[i].ToString()
+			Next
 		End If
 	End Method
 End Type