浏览代码

Merge branch 'bcc_gen'

woollybah 8 年之前
父节点
当前提交
90edac9889
共有 11 个文件被更改,包括 878 次插入147 次删除
  1. 266 0
      base64.bmx
  2. 65 1
      config.bmx
  3. 61 6
      ctranslator.bmx
  4. 103 30
      decl.bmx
  5. 10 1
      expr.bmx
  6. 233 47
      iparser.bmx
  7. 72 50
      parser.bmx
  8. 29 9
      stmt.bmx
  9. 13 2
      toker.bmx
  10. 12 0
      translator.bmx
  11. 14 1
      type.bmx

+ 266 - 0
base64.bmx

@@ -0,0 +1,266 @@
+' Copyright (c) 2008-2017 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
+' in the Software without restriction, including without limitation the rights
+' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+' copies of the Software, and to permit persons to whom the Software is
+' furnished to do so, subject to the following conditions:
+' 
+' The above copyright notice and this permission notice shall be included in
+' all copies or substantial portions of the Software.
+' 
+' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+' THE SOFTWARE.
+' 
+SuperStrict
+
+Rem
+bbdoc: Encode/Decode Base64 data.
+about:
+To Encode :
+<pre>
+SuperStrict
+Import BaH.Base64
+
+Local someData:String = "Woo! BlitzMax Modules rock!"
+Local encoded:String = TBase64.Encode(someData, someData.length)
+Print "Encoded : " + encoded
+</pre>
+To Decode :
+<pre>
+SuperStrict
+Import BaH.Base64
+
+Local encodedData:String = "V29vISBCbGl0ek1heCBNb2R1bGVzIHJvY2sh"
+Local data:Byte[] = TBase64.Decode(encodedData)
+Local decoded:String = String.FromBytes(data, data.length)
+Print "Decoded : " + decoded
+</pre>
+End Rem
+Type TBase64
+
+	Const DONT_BREAK_LINES:Int = 8
+	
+	' Maximum line length (76) of Base64 output.
+	Const MAX_LINE_LENGTH:Int = 76
+	
+	' The equals sign (=) as int.
+	Const EQUALS_SIGN:Int = Asc("=")
+	
+	' The new line character (\n) as int.
+	Const NEW_LINE:Int = Asc("~n")
+	
+	Const WHITE_SPACE_ENC:Int = -5 ' Indicates white space in encoding
+	Const EQUALS_SIGN_ENC:Int = -1 ' Indicates equals sign in encoding
+	
+	Const _STANDARD_ALPHABET:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+	
+	Global _STANDARD_DECODABET:Int[] = [-9,-9,-9,-9,-9,-9,-9,-9,-9, ..
+						        -5,-5, ..
+						        -9,-9, ..
+						        -5, ..
+						        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, ..
+						        -9,-9,-9,-9,-9, ..
+						        -5, ..
+						        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, ..
+						        62, ..
+						        -9,-9,-9, ..
+						        63, ..
+						        52,53,54,55,56,57,58,59,60,61, ..
+						        -9,-9,-9, ..
+						        -1, ..
+						        -9,-9,-9, ..
+						        0,1,2,3,4,5,6,7,8,9,10,11,12,13, ..
+						        14,15,16,17,18,19,20,21,22,23,24,25, ..    
+						        -9,-9,-9,-9,-9,-9, ..
+						        26,27,28,29,30,31,32,33,34,35,36,37,38, ..
+						        39,40,41,42,43,44,45,46,47,48,49,50,51, ..
+						        -9,-9,-9,-9]
+	
+	Rem
+	bbdoc: Encode byte data to a Base64 encoded String, starting at @offset of @length bytes.
+	End Rem
+	Function Encode:String( source:Byte Ptr, length:Int, offset:Int = 0, options:Int = 0)
+    
+		' Isolate options
+		Local dontBreakLines:Int =  options & DONT_BREAK_LINES 
+		
+		' Convert option To boolean in way that code likes it.
+		Local breakLines:Int = True
+		If dontBreakLines Then
+			breakLines = False
+		End If
+		
+		Local len43:Int = length * 4 / 3
+		Local nl:Int = len43 / MAX_LINE_LENGTH
+		Local pad:Int = 0
+		If length Mod 3 > 0 Then
+			pad = 4
+		End If
+		
+		If Not breakLines Then
+			nl = 0
+		End If
+		
+		Local outBuff:Byte[] = New Byte[ len43 + pad + nl ]     
+		Local d:Int = 0
+		Local e:Int = 0
+		Local len2:Int = length - 2
+		Local lineLength:Int = 0
+		While d < len2
+			encode3to4( source, d + offset, 3, outBuff, e, options )
+			
+			lineLength :+ 4
+			If  breakLines And (lineLength = MAX_LINE_LENGTH) Then
+				outBuff[e+4] = NEW_LINE
+				e:+1
+				lineLength = 0
+			End If
+			d:+3
+			e:+4
+		Wend
+		
+		' pad?
+		If d < Length Then
+			encode3to4( source, d + offset, Length - d, outBuff, e, options )
+			e :+ 4
+		End If
+
+		Return String.FromBytes(outBuff, e)
+	End Function
+	
+	Rem
+	bbdoc: Decode Base64 encoded String to an array of Bytes, starting at @offset.
+	End Rem
+	Function Decode:Byte[]( source:String, offset:Int = 0, options:Int = 0 )
+		
+		Local length:Int = source.length
+		Local len34:Int   = Length * 3 / 4
+		Local outBuff:Byte[] = New Byte[ len34 ]
+		Local outBuffPosn:Int = 0
+		
+		Local b4:Byte[]     = New Byte[4]
+		Local b4Posn:Int    = 0
+		Local sbiCrop:Int   = 0
+		Local sbiDecode:Int = 0
+		For Local i:Int = offset Until offset + length
+		
+			sbiCrop = source[i] & $7f
+			sbiDecode = _STANDARD_DECODABET[ sbiCrop ]
+			
+			If sbiDecode >= WHITE_SPACE_ENC Then
+			
+				If sbiDecode >= EQUALS_SIGN_ENC Then
+				
+					b4[ b4Posn ] = sbiCrop
+					b4Posn:+1
+					If b4Posn > 3 Then
+				
+						outBuffPosn :+ decode4to3( b4, 0, outBuff, outBuffPosn, options )
+						b4Posn = 0
+				
+						' If that was the equals sign, break out of 'for' loop
+						If sbiCrop = EQUALS_SIGN Then
+							Exit
+						End If
+					End If
+			
+				End If
+			
+			Else
+				DebugLog "Bad Base64 input character at " + i + ": " + source[i]
+				Return Null
+			End If
+		Next
+		
+		Return outBuff[0..outBuffPosn]
+	End Function
+
+	Function encode3to4:Byte[](source:Byte Ptr, srcOffset:Int, numSigBytes:Int, destination:Byte[], destOffset:Int, options:Int )
+    
+		Local inBuff:Int
+		If numSigBytes > 0 Then
+			inBuff = (source[ srcOffset     ] Shl 24) Shr 8
+			
+			If numSigBytes > 1 Then
+				inBuff :| (source[ srcOffset + 1 ] Shl 24) Shr 16
+
+				If numSigBytes > 2 Then
+					inBuff :| (source[ srcOffset + 2 ] Shl 24) Shr 24
+				End If
+			End If
+		End If
+		
+		Select numSigBytes
+			Case 3
+				destination[ destOffset     ] = _STANDARD_ALPHABET[ (inBuff Shr 18)       ]
+				destination[ destOffset + 1 ] = _STANDARD_ALPHABET[ (inBuff Shr 12) & $3f ]
+				destination[ destOffset + 2 ] = _STANDARD_ALPHABET[ (inBuff Shr  6) & $3f ]
+				destination[ destOffset + 3 ] = _STANDARD_ALPHABET[ (inBuff       ) & $3f ]
+				Return destination
+			
+			Case 2
+				destination[ destOffset     ] = _STANDARD_ALPHABET[ (inBuff Shr 18)       ]
+				destination[ destOffset + 1 ] = _STANDARD_ALPHABET[ (inBuff Shr 12) & $3f ]
+				destination[ destOffset + 2 ] = _STANDARD_ALPHABET[ (inBuff Shr  6) & $3f ]
+				destination[ destOffset + 3 ] = EQUALS_SIGN
+				Return destination
+			
+			Case 1
+				destination[ destOffset     ] = _STANDARD_ALPHABET[ (inBuff Shr 18)       ]
+				destination[ destOffset + 1 ] = _STANDARD_ALPHABET[ (inBuff Shr 12) & $3f ]
+				destination[ destOffset + 2 ] = EQUALS_SIGN
+				destination[ destOffset + 3 ] = EQUALS_SIGN
+				Return destination
+			
+			Default
+				Return destination
+		End Select
+	End Function 
+	
+	Function decode4to3:Int( source:Byte[] , srcOffset:Int, destination:Byte[] , destOffset:Int, options:Int )
+	
+		' Example: Dk==
+		If source[ srcOffset + 2] = EQUALS_SIGN Then
+		
+			Local outBuff:Int =   ( ( _STANDARD_DECODABET[ source[ srcOffset    ] ] & $FF ) Shl 18 ) ..
+				| ( ( _STANDARD_DECODABET[ source[ srcOffset + 1] ] & $FF ) Shl 12 )
+			
+			destination[ destOffset ] = outBuff Shr 16
+			Return 1
+		
+		' Example: DkL=
+		Else If source[ srcOffset + 3 ] = EQUALS_SIGN Then
+		
+			Local outBuff:Int =   ( ( _STANDARD_DECODABET[ source[ srcOffset     ] ] & $FF ) Shl 18 ) ..
+				| ( ( _STANDARD_DECODABET[ source[ srcOffset + 1 ] ] & $FF ) Shl 12 ) ..
+				| ( ( _STANDARD_DECODABET[ source[ srcOffset + 2 ] ] & $FF ) Shl  6 )
+			
+			destination[ destOffset     ] = outBuff Shr 16
+			destination[ destOffset + 1 ] = outBuff Shr  8
+			Return 2
+			
+		' Example: DkLE
+		Else
+		
+			Local outBuff:Int =   ( ( _STANDARD_DECODABET[ source[ srcOffset     ] ] & $FF ) Shl 18 ) ..
+				| ( ( _STANDARD_DECODABET[ source[ srcOffset + 1 ] ] & $FF ) Shl 12 ) ..
+				| ( ( _STANDARD_DECODABET[ source[ srcOffset + 2 ] ] & $FF ) Shl  6) ..
+				| ( ( _STANDARD_DECODABET[ source[ srcOffset + 3 ] ] & $FF )      )
+		
+			destination[ destOffset     ] =  outBuff Shr 16
+			destination[ destOffset + 1 ] =  outBuff Shr  8
+			destination[ destOffset + 2 ] =  outBuff
+		
+			Return 3
+		End If
+	End Function 
+
+End Type
+

+ 65 - 1
config.bmx

@@ -26,10 +26,11 @@ 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
@@ -405,3 +406,66 @@ 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

+ 61 - 6
ctranslator.bmx

@@ -3240,6 +3240,10 @@ End Rem
 
 	Method EmitClassProto( classDecl:TClassDecl )
 	
+		If classDecl.args Then
+			Return
+		End If
+
 		Local classid$=classDecl.munged
 		Local superid$
 		If classDecl.superClass Then
@@ -3814,6 +3818,10 @@ End Rem
 
 	Method EmitClassDecl( classDecl:TClassDecl )
 	
+		If classDecl.args Then
+			Return
+		End If
+
 		PushEnv classDecl
 		'If classDecl.IsTemplateInst()
 		'	Return
@@ -3996,6 +4004,8 @@ End Rem
 					For Local ifc:TClassDecl = EachIn implementedInterfaces.Values()
 						Emit ".interface_" + ifc.ident + "={"
 
+						Local dups:TMap = New TMap
+						
 						For Local func:TFuncDecl = EachIn ifc.GetImplementedFuncs()
 						
 							If func.IsMethod() Then
@@ -4004,7 +4014,14 @@ End Rem
 
 									Mungdecl f
 									If f.ident = func.ident And f.EqualsFunc(func) Then
+
+										If Not dups.ValueForKey(f.ident) Then
+									
 											Emit "_" + f.munged + ","
+										
+											dups.Insert(f.ident, "")
+										End If
+
 										Exit
 									End If
 								Next
@@ -4909,8 +4926,7 @@ End Rem
 		'PushMungScope
 		BeginLocalScope
 
-		' fields, globals and consts
-'		For Local decl:TDecl=EachIn classDecl.Decls()
+		If Not classDecl.templateSource Then
 	
 			' const
 			For Local cDecl:TConstDecl = EachIn classDecl.Decls()
@@ -4934,13 +4950,17 @@ End Rem
 				EmitIfcFieldDecl(fDecl)
 			Next
 
+		End If
 
 		' functions
 		If Not classDecl.IsExtern() Then
 		
+			If Not classDecl.templateSource Then
+
 				If Not classDecl.attrs & CLASS_INTERFACE Then
 					Emit "-New()=" + Enquote("_" + classDecl.munged + "_New")
 				End If
+
 				If classHierarchyHasFunction(classDecl, "Delete") Then
 					Emit "-Delete()=" + Enquote("_" + classDecl.munged + "_Delete")
 				End If
@@ -4957,6 +4977,8 @@ End Rem
 	
 				Next
 			
+			End If
+			
 			Local flags:String
 
 			If classDecl.IsAbstract() Then
@@ -4973,7 +4995,39 @@ End Rem
 				flags :+ "S"
 			End If
 			
-			Emit "}" + flags + "=" + Enquote(classDecl.munged), False
+			If classDecl.templateSource Then
+				flags :+ "G"
+			End If
+			
+			Local t:String = "}" + flags + "="
+			
+			
+			
+			If classDecl.templateSource Then
+				t :+ Enquote(classDecl.scope.munged)
+			
+				t :+ ",<"
+
+				Local s:String
+				
+				If classDecl.instArgs Then
+					For Local ty:TType = EachIn classDecl.instArgs
+						If s Then
+							s :+ ","
+						End If
+						s :+ ty.ToString()
+					Next
+				Else
+					s = "?"
+				End If
+				
+				t :+ s + ">" + classDecl.templateSource.ToString()
+			Else
+				t :+ Enquote(classDecl.munged)	
+			End If
+			
+			Emit t, False
+
 		Else
 			For Local decl:TDecl=EachIn classDecl.Decls()
 
@@ -5151,10 +5205,10 @@ End Rem
 		Next
 
 		Emit "int " + app.munged + "();"
-		
+
 		For Local decl:TDecl=EachIn app.Semanted()
 
-			If decl.declImported Continue
+			If decl.declImported And decl.munged Continue
 
 			MungDecl decl
 
@@ -5508,7 +5562,8 @@ End If
 			If decl.declImported Continue
 
 			Local cdecl:TClassDecl=TClassDecl( decl )
-			If cdecl And Not cdecl.IsExtern()
+
+			If cdecl And Not cdecl.IsExtern() And Not cdecl.args
 				If Not cdecl.IsInterface() Then
 					If Not cdecl.IsStruct() Then
 						Emit "bbObjectRegisterType((BBCLASS)&" + cdecl.munged + ");"

+ 103 - 30
decl.bmx

@@ -29,6 +29,7 @@ Const DECL_FINAL:Int=         $080000
 
 Const DECL_SEMANTED:Int=      $100000
 Const DECL_SEMANTING:Int=     $200000
+Const DECL_CYCLIC:Int=       $8000000
 
 Const DECL_POINTER:Int=       $400000
 
@@ -45,6 +46,7 @@ Const DECL_API_DEFAULT:Int=DECL_API_CDECL
 Const CLASS_INTERFACE:Int=    $002000
 Const CLASS_THROWABLE:Int=    $004000
 Const CLASS_STRUCT:Int=       $008000
+Const CLASS_GENERIC:Int=      $001000
 
 Const SCOPE_FUNC:Int = 0
 Const SCOPE_CLASS_LOCAL:Int = 1
@@ -225,7 +227,12 @@ Type TDecl
 
 		If IsSemanted() Return
 
-		If IsSemanting() Err "Cyclic declaration of '"+ident+"'."
+		If IsSemanting() Then
+			If attrs & DECL_CYCLIC Then
+				Return
+			End If
+			Err "Cyclic declaration of '"+ident+"'."
+		End If
 		
 		If actual<>Self
 			actual.Semant
@@ -336,7 +343,7 @@ Type TValDecl Extends TDecl
 	End Method
 	
 	Method OnSemant()
-	
+'DebugStop	
 		If declTy
 
 			Local at:TType = TArrayType(declTy)
@@ -562,8 +569,10 @@ Type TLocalDecl Extends TVarDecl
 	End Method
 	
 	Method OnCopy:TDecl(deep:Int = True)
-		Local decl:TLocalDecl = New TLocalDecl.Create( ident,ty,CopyInit(),attrs &~ DECL_SEMANTED, generated, volatile )
+		Local decl:TLocalDecl = New TLocalDecl.Create( ident,declTy,declInit,attrs &~ DECL_SEMANTED, generated, volatile )
 		decl.scope = scope
+		decl.ty = ty
+		decl.init = init
 		Return decl
 	End Method
 
@@ -604,9 +613,9 @@ Type TArgDecl Extends TLocalDecl
 	End Method
 	
 	Method OnCopy:TDecl(deep:Int = True)
-		Local d:TArgDecl = New TArgDecl.Create( ident,ty,CopyInit(),attrs,generated,volatile )
-		d.ty = d.declTy
-		d.init = d.declInit
+		Local d:TArgDecl = New TArgDecl.Create( ident,declTy,declInit,attrs,generated,volatile )
+		d.ty = ty
+		d.init = init
 		Return d
 	End Method
 
@@ -636,7 +645,10 @@ Type TGlobalDecl Extends TVarDecl
 	End Method
 
 	Method OnCopy:TDecl(deep:Int = True)
-		Return New TGlobalDecl.Create( ident,ty,CopyInit(),attrs,funcGlobal )
+		Local g:TGlobalDecl = New TGlobalDecl.Create( ident,declTy,declInit,attrs,funcGlobal )
+		g.ty = ty
+		g.init = init
+		Return g
 	End Method
 	
 	Method ToString$()
@@ -683,7 +695,9 @@ Type TFieldDecl Extends TVarDecl
 	End Method
 
 	Method OnCopy:TDecl(deep:Int = True)
-		Local f:TFieldDecl = New TFieldDecl.Create( ident,ty,CopyInit(),attrs )
+		Local f:TFieldDecl = New TFieldDecl.Create( ident,declTy,declInit,attrs )
+		f.ty = ty
+		f.init = init
 		f.metadata = metadata
 		Return f
 	End Method
@@ -803,14 +817,14 @@ Type TScopeDecl Extends TDecl
 		Return fdecls
 	End Method
 	
-	Method InsertDecl( decl:TDecl )
+	Method InsertDecl( decl:TDecl, isCopy:Int = False )
 
-		If decl.scope And Not (decl.attrs & DECL_INITONLY) InternalErr
+		If decl.scope And Not (decl.attrs & DECL_INITONLY) And Not isCopy InternalErr
 		
 		'Local ident$=decl.ident
 		If Not decl.ident Return
 		
-		If Not decl.scope Then
+		If Not decl.scope Or isCopy Then
 			decl.scope=Self
 		End If
 		_decls.AddLast decl
@@ -949,7 +963,7 @@ Type TScopeDecl Extends TDecl
 			End If
 
 			' if scope is an interface, also check implemented/extended interfaces?
-			If TClassDecl(tscope) And TClassDecl(tscope).IsInterface() Then
+			If TClassDecl(tscope) Then'And TClassDecl(tscope).IsInterface() Then
 				If TClassDecl(tscope).implments Then
 					For Local idecl:TScopeDecl = EachIn TClassDecl(tscope).implments
 						Local decl:Object=idecl.GetDeclList( ident, declList, maxSearchDepth )
@@ -1059,8 +1073,10 @@ Type TScopeDecl Extends TDecl
 			Local cdecl:TClassDecl=TClassDecl( decl )
 			If cdecl
 				cdecl.AssertAccess
-				cdecl=cdecl.GenClassInstance( args )
-				cdecl.Semant
+				If Not cdecl.instanceof Then
+					cdecl=cdecl.GenClassInstance( args )
+					cdecl.Semant
+				End If
 				Return cdecl.objectType
 			EndIf
 		EndIf
@@ -1630,6 +1646,7 @@ Type TFuncDecl Extends TBlockDecl
 			Next
 		End If
 		t.retType = retType
+		t.retTypeExpr = retTypeExpr
 		t.scope = scope
 		t.overrides = overrides
 		t.superCtor = superCtor
@@ -2016,7 +2033,7 @@ Type TNewDecl Extends TFuncDecl
 		Local t:TNewDecl = TNewDecl(New TNewDecl.CreateF( ident,retType,args,attrs &~DECL_SEMANTED ))
 		If deep Then
 			For Local stmt:TStmt=EachIn stmts
-				t.AddStmt stmt.Copy( t )
+				t.AddStmt stmt.Copy(Null)
 			Next
 		End If
 		t.retType = retType
@@ -2054,7 +2071,7 @@ Type TClassDecl Extends TScopeDecl
 
 	Field lastOffset:Int
 
-	Field args:String[]
+	Field args:TTemplateArg[]
 	Field superTy:TIdentType
 	Field impltys:TIdentType[]
 
@@ -2069,10 +2086,11 @@ Type TClassDecl Extends TScopeDecl
 
 	Field objectType:TObjectType '"canned" objectType
 	Field globInit:Int
+	Field templateSource:TTemplateRecord
 
 	'Global nullObjectClass:TClassDecl=New TNullDecl.Create( "{NULL}",Null,Null,Null,DECL_ABSTRACT|DECL_EXTERN )
 	
-	Method Create:TClassDecl( ident$,args:String[],superTy:TIdentType,impls:TIdentType[],attrs:Int )
+	Method Create:TClassDecl( ident$,args:TTemplateArg[],superTy:TIdentType,impls:TIdentType[],attrs:Int )
 		Self.ident=ident
 		Self.args=args
 		Self.superTy=superTy
@@ -2081,7 +2099,6 @@ Type TClassDecl Extends TScopeDecl
 		Self.objectType=New TObjectType.Create( Self )
 		If args
 			instances=New TList
-			instances.AddLast Self
 		EndIf
 		Return Self
 	End Method
@@ -2092,19 +2109,28 @@ Type TClassDecl Extends TScopeDecl
 	
 	Method ToString$()
 		Local t$
+
 		If args Then
-				For Local i:Int=0 Until args.Length
-				If i t:+","
+			For Local i:Int=0 Until args.Length
+				If i Then
+					t :+ ","
+				End If
 				t:+args[i].ToString()
 			Next
 		ElseIf instargs
+			For Local i:Int=0 Until instargs.Length
+				If i Then
+					t :+ ","
+				End If
+				t :+ instargs[i].ToString()
+			Next
 		End If
 		If t t="<"+t+">"
 		Return ident+t
 	End Method
 
 	Method ToTypeString:String()
-		Return ident
+		Return ToString()
 	End Method
 Rem
 	Method GenClassInstance:TClassDecl( instArgs:TClassDecl[] )
@@ -2165,7 +2191,7 @@ Rem
 		Return inst
 	End Method
 End Rem
-	Method GenClassInstance:TClassDecl( instArgs:TType[] )
+	Method GenClassInstance:TClassDecl( instArgs:TType[], declImported:Int = False )
 
 		If instanceof InternalErr
 		
@@ -2193,25 +2219,42 @@ End Rem
 			Next
 			If equal Return inst
 		Next
-		
+
 		Local inst:TClassDecl=New TClassDecl.Create( ident,Null,superTy,impltys, attrs )
 
 		inst.attrs:&~DECL_SEMANTED
+
 		inst.munged=munged
 		inst.errInfo=errInfo
 		inst.scope=scope
 		inst.instanceof=Self
 		inst.instArgs=instArgs
+		inst.templateSource = templateSource
 		instances.AddLast inst
 		
+		inst.declImported = declImported
+
 		For Local i:Int=0 Until args.Length
-			inst.InsertDecl New TAliasDecl.Create( args[i].ToString(),instArgs[i],0 )
-		Next
 		
+			' 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() + "'"
+				End If
+			End If
+		
+			inst.InsertDecl New TAliasDecl.Create( args[i].ident,instArgs[i],0 )
+		Next
+
 		For Local decl:TDecl=EachIn _decls
-			inst.InsertDecl decl.Copy()
+			inst.InsertDecl decl.Copy(), True
 		Next
 
+		If Not declImported Then
+			inst.scope = _env.ModuleScope()
+		End If
+
 		Return inst
 	End Method
 
@@ -2456,11 +2499,12 @@ End Rem
 			If func.IdentLower() = fdecl.IdentLower() And func.EqualsArgs(fdecl) Then
 				Return func
 			End If
-			
+		
 		Next
 		
 		Return fdecl
 	End Method
+
 	
 	Method ExtendsClass:Int( cdecl:TClassDecl )
 		'If Self=nullObjectClass Return True
@@ -2484,6 +2528,10 @@ End Rem
 	End Method
 	
 	Method OnSemant()
+	
+		If args Then
+			Return
+		End If
 
 		PushEnv Self
 
@@ -2509,7 +2557,9 @@ End Rem
 		Local impls:TClassDecl[]=New TClassDecl[impltys.Length]
 		Local implsall:TStack=New TStack
 		For Local i:Int=0 Until impltys.Length
+			attrs :| DECL_CYCLIC
 			Local cdecl:TClassDecl=impltys[i].SemantClass()
+			attrs :~ DECL_CYCLIC
 			If Not cdecl.IsInterface()
 				Err cdecl.ToString()+" is a type, not an interface."
 			EndIf
@@ -2597,6 +2647,7 @@ End Rem
 		EndIf
 
 		'NOTE: do this AFTER super semant so UpdateAttrs order is cool.
+
 		If AppScope() Then
 			AppScope().semantedClasses.AddLast Self
 		End If
@@ -2606,6 +2657,9 @@ End Rem
 '		If IsSemanted() Return
 		
 '		Super.Semant()
+		If args Then
+			Return
+		End If
 		
 		For Local decl:TConstDecl = EachIn Decls()
 			decl.Semant()
@@ -2792,7 +2846,7 @@ End Rem
 				Local found:Int
 				For Local decl2:TFuncDecl=EachIn impls
 					If decl.IdentLower() = decl2.IdentLower()
-						If Not decl2.EqualsFunc( decl )
+						If decl2.argDecls.Length = decl.argDecls.Length And Not decl2.EqualsFunc( decl )
 							Err "Cannot mix incompatible method signatures." + decl2.ToString() + " vs " + decl.ToString() + "."
 						Else
 							found = True
@@ -2840,6 +2894,16 @@ End Rem
 		lastOffset :+ modifier
 	End Method
 	
+	Method ImplementsInterface:Int(ident:String)
+		ident = ident.ToLower()
+		For Local iface:TClassDecl = EachIn implmentsAll
+			If iface.IdentLower() = ident Then
+				Return True
+			End If
+		Next
+		Return False
+	End Method
+	
 	' returns a map of all interfaces implemented in this hierarchy
 	Method GetInterfaces:TMap(map:TMap = Null)
 		If Not map Then
@@ -3022,7 +3086,7 @@ Type TModuleDecl Extends TScopeDecl
 		Self.filepath=filepath
 		Self.attrs=attrs
 
-		If ident.Find(".") <> -1 Then
+		If ident.Find(".") <> -1 And ident.find("/") = -1 And ident.find("\") = -1 Then
 			Local m:String = ident[..ident.Find(".")]
 			Local ns:TNamespaceDecl = TNamespaceDecl(_appInstance.GetDecl(m.ToLower()))
 			If Not ns Then
@@ -3239,7 +3303,16 @@ Type TModuleDecl Extends TScopeDecl
 		Next
 
 		For Local cdecl:TClassDecl=EachIn _decls
-			cdecl.Semant
+			If cdecl.args Then
+				For Local inst:TClassDecl = EachIn cdecl.instances
+					For Local idecl:TDecl = EachIn inst.Decls()
+						If TAliasDecl( idecl ) Continue
+						idecl.Semant()
+					Next
+				Next
+			Else
+				cdecl.Semant
+			End If
 		Next
 
 		For Local fdecl:TFuncDecl=EachIn _decls

+ 10 - 1
expr.bmx

@@ -2384,7 +2384,12 @@ Type TIdentExpr Extends TExpr
 	End Method
 
 	Method Copy:TExpr()
-		Return New TIdentExpr.Create( ident,CopyExpr(expr), _identLower )
+		Local i:TIdentExpr = New TIdentExpr.Create( ident,CopyExpr(expr), _identLower )
+		i.static = static
+		i.isArg = isArg
+		i.isRhs = isRhs
+		i.fixedScope = fixedScope
+		Return i
 	End Method
 
 	Method ToString$()
@@ -3163,6 +3168,10 @@ Type TNewExpr Extends TExpr
 		Return Self
 	End Method
 
+	Method Copy:TExpr()
+		Return New TNewExpr.Create(CopyArgs(args), isSuper)
+	End Method
+
 	Method Semant:TExpr()
 
 		Local fdecl:TFuncDecl = _env.FuncScope()

+ 233 - 47
iparser.bmx

@@ -21,11 +21,11 @@
 '    3. This notice may not be removed or altered from any source
 '    distribution.
 ' 
-SuperStrict
+'SuperStrict
 
-Import BRL.MaxUtil
+'Import BRL.MaxUtil
 
-Import "toker.bmx"
+'Import "parser_factory.bmx"
 
 
 Const DECL_GLOBAL:Int = $10
@@ -262,59 +262,175 @@ Type TIParser
 						' class decl
 						class = ParseClassDecl( stm,0 )
 						class.declImported = True
-						_mod.InsertDecl(class)
 
-						If CParse("F")
-							class.attrs :| DECL_FINAL
+						Local parsed:Int
+						For Local i:Int = 0 Until _toke.Length
+							Select _toke[i]
+								Case Asc("F")
+									class.attrs :| DECL_FINAL
+									parsed = True
+								Case Asc("A")
+									class.attrs :| DECL_ABSTRACT
+									'ApplyFunctionAttributes(class, DECL_ABSTRACT)
+									parsed = True
+								Case Asc("S")
+									class.attrs :| CLASS_STRUCT
+									parsed = True
+								Case Asc("E")
+									class.attrs :| DECL_EXTERN
+									ApplyFunctionAttributes(class, DECL_EXTERN)
+									parsed = True
+								Case Asc("W")
+									class.attrs :| DECL_API_STDCALL
+									ApplyFunctionAttributes(class, DECL_API_STDCALL)
+									parsed = True
+								Case Asc("I")
+									class.attrs :| CLASS_INTERFACE
+									ApplyFunctionAttributes(class, DECL_ABSTRACT)
+									parsed = True
+								Case Asc("G")
+									class.attrs :| CLASS_GENERIC
+									parsed = True
+							End Select
+						Next
+
+						If parsed Then
+							NextToke
+						End If
 
-						Else If CParse("A")
-							class.attrs :| DECL_ABSTRACT
+						If CParse( "=" )
 
-						Else If CParse("S")
-							class.attrs :| CLASS_STRUCT
+							If Not class.IsExtern() Then
+								class.munged=ParseStringLit()
 
-						Else If CParse("AF")
-							class.attrs :| DECL_ABSTRACT | DECL_FINAL
+								If class.ident <> "String" Then
+									For Local fdecl:TFieldDecl = EachIn class._decls
+										fdecl.munged = "_" + class.munged + "_" + fdecl.ident
+										fdecl.munged = fdecl.munged.ToLower()
+									Next
+								End If
+								
+								' process generic details
+								If class.attrs & CLASS_GENERIC Then
 
-						Else If CParse("E")
-							class.attrs :| DECL_EXTERN
-							ApplyFunctionAttributes(class, DECL_EXTERN)
+									If _toke <> "," Then
+										Err "Syntax error - unexpected token '" + _toke + "'"
+									End If
+									
+									NextToke
+
+									Parse "<"
+									Local args:TType[]
+									Local nargs:Int
+									Repeat
+										Local arg:TType = ParseType()
+										If args.Length=nargs args=args+ New TType[10]
+										args[nargs]=arg
+										nargs:+1
+									Until Not CParse(",")
+									args=args[..nargs]
+									Parse ">"
+
+									Parse "{"
+									' line no
+									
+									If _tokeType <> TOKE_INTLIT Then
+										Err "Syntax error - unexpected token '" + _toke + "'"
+									End If
+									
+									Local line:Int = _toke.ToInt()
+									
+									NextToke
+									Parse ","
+									
+									' source size
+									If _tokeType <> TOKE_INTLIT Then
+										Err "Syntax error - unexpected token '" + _toke + "'"
+									End If
+									
+									Local size:Int = _toke.ToInt()
 
-						Else If CParse("EW")
-							class.attrs :| DECL_EXTERN | DECL_API_STDCALL
-							ApplyFunctionAttributes(class, DECL_EXTERN | DECL_API_STDCALL)
-						
-						Else If CParse("AI")
-							class.attrs :| CLASS_INTERFACE | DECL_ABSTRACT
-							ApplyFunctionAttributes(class, DECL_ABSTRACT)
+									NextToke
+									Parse ","
+									
+									' path
+									If _tokeType <> TOKE_STRINGLIT Then
+										Err "Syntax error - unexpected token '" + _toke + "'"
+									End If
+									
+									Local path:String = BmxUnquote(_toke)
+									
+									NextToke
+									Parse ","
 
-						Else If CParse("EI")
-							class.attrs :| DECL_EXTERN | CLASS_INTERFACE
-							ApplyFunctionAttributes(class, DECL_EXTERN | DECL_ABSTRACT)
 
-						Else If CParse("EIW")
-							class.attrs :| DECL_EXTERN | CLASS_INTERFACE | DECL_API_STDCALL
-							ApplyFunctionAttributes(class, DECL_EXTERN | DECL_ABSTRACT | DECL_API_STDCALL)
+									' source
+									If _tokeType <> TOKE_STRINGLIT Then
+										Err "Syntax error - unexpected token '" + _toke + "'"
+									End If
+									
+									Local source:String = BmxUnquote(_toke)
+									
+									NextToke
 
-						Else If CParse("ES")
-							class.attrs :| DECL_EXTERN | CLASS_STRUCT
+									Parse "}"
 
-						Else If CParse("ESW")
-							class.attrs :| DECL_EXTERN | CLASS_STRUCT | DECL_API_STDCALL
+									class.templateSource = TTemplateRecord.Load(line, path, size, source)
 
-						End If
-'DebugStop
-						If CParse( "=" )
-'DebugStop
-							If Not class.IsExtern() Then
-								class.munged=ParseStringLit()
+									Local toker:TToker = New TToker.Create(path, class.templateSource.source, False, line)
+									Local parser:TParser = New TParser.Create( toker, Null )
+									
+									Local m:TModuleDecl = New TModuleDecl
+									parser._module = m
+									
+									Local cdecl:TClassDecl = Null
+									
+									Select parser._toke
+										Case "type"
+											cdecl = parser.ParseClassDecl(parser._toke,0)
+										Case "interface"
+											cdecl = parser.ParseClassDecl(parser._toke, CLASS_INTERFACE|DECL_ABSTRACT )
+									End Select
 
-								If class.ident <> "String" Then
-									For Local fdecl:TFieldDecl = EachIn class._decls
-										fdecl.munged = "_" + class.munged + "_" + fdecl.ident
-										fdecl.munged = fdecl.munged.ToLower()
-									Next
+									Local ty:TType = args[0]
+
+									Local genDecl:TClassDecl = TClassDecl(_mod.GetDecl(cdecl.IdentLower()))
+
+									If Not genDecl Then
+										genDecl = TClassDecl(pmod.GetDecl(cdecl.identLower()))
+									End If
+									
+									If genDecl Then
+									
+										If Not TIdentType(ty) Or (TIdentType(ty) And TIdentType(ty).ident <> "?") Then
+										
+											cdecl = genDecl.GenClassInstance(args, True)
+
+											cdecl.scope.munged = class.munged
+											cdecl.scope.scope = _appInstance
+										
+										End If
+									
+										' don't add to module
+										class = Null
+									
+									Else
+
+										class = cdecl
+										class.declImported = True
+										
+										If Not TIdentType(ty) Or (TIdentType(ty) And TIdentType(ty).ident <> "?") Then
+										
+											cdecl = class.GenClassInstance(args)
+											cdecl.declImported = True
+											
+											_mod.munged = class.munged
+										End If
+										
+									End If
+									
 								End If
+								
 							Else
 								Parse "0"
 								If Not class.munged Then
@@ -324,12 +440,15 @@ Type TIParser
 							End If
 						EndIf
 
+						If class Then
+							_mod.InsertDecl(class)
+						End If
+
 						'state = STATE_CLASS
 						'Exit
 				'	Case "%"
 				Default
 					If toker._tokeType = TOKE_EOF
-'DebugStop
 						Exit
 					End If
 
@@ -508,7 +627,7 @@ Type TIParser
 		'If toke Parse toke
 		
 		Local id$=ParseIdent()
-		Local args:String[]
+		Local args:TTemplateArg[]
 		Local superTy:TIdentType
 		Local imps:TIdentType[]
 
@@ -643,6 +762,7 @@ Type TIParser
 		Select _toker._toke.tolower()
 		Case "@" _toker.NextToke
 		Case "string","___array","object"
+		Case "?"
 		Default	
 			If _toker._tokeType<>TOKE_IDENT Err "Syntax error - expecting identifier."
 		End Select
@@ -658,7 +778,20 @@ Type TIParser
 		While CParse( "." )
 			id:+"."+ParseIdent()
 		Wend
-		Local args:TIdentType[]
+		
+		Local args:TType[]
+		If CParse( "<" )
+			Local nargs:Int
+			Repeat
+				Local arg:TType = ParseType()
+				If args.Length=nargs args=args+ New TType[10]
+				args[nargs]=arg
+				nargs:+1
+			Until Not CParse(",")
+			args=args[..nargs]
+			Parse ">"
+		EndIf
+		
 		Return New TIdentType.Create( id,args )
 	End Method
 
@@ -1599,6 +1732,59 @@ End Rem
 		EndIf
 	End Method
 
-End Type
+	Method ParseType:TType()
+		Local ty:TType=CParsePrimitiveType()
+		If ty Return ty
+		Return ParseIdentType()
+	End Method
+
+	Method CParsePrimitiveType:TType()
+		If CParse( "string" ) Return TType.stringType
+		If CParse( "object" ) Return New TIdentType.Create( "brl.classes.object" )
+
+		Local ty:TType
+		If CParse( "short" )
+			ty = New TShortType
+		Else If CParse( "byte" )
+			ty = New TByteType
+		Else If CParse( "int" )
+			ty = New TIntType
+		Else If CParse( "uint" )
+			ty = New TUIntType
+		Else If CParse( "float" )
+			ty = New TFloatType
+		Else If CParse( "long" )
+			ty = New TLongType
+		Else If CParse( "ulong" )
+			ty = New TULongType
+		Else If CParse( "double" )
+			ty = New TDoubleType
+		Else If CParse( "size_t" )
+			ty = New TSizeTType
+		Else If CParse( "int128" ) Then
+			If opt_arch <> "x64" Err "Intrinsic types only available on x64"
+			ty = New TInt128Type
+		Else If CParse( "float128" ) Then
+			If opt_arch <> "x64" Err "Intrinsic types only available on x64"
+			ty = New TFloat128Type
+		Else If CParse( "double128" ) Then
+			If opt_arch <> "x64" Err "Intrinsic types only available on x64"
+			ty = New TDouble128Type
+		Else If CParse( "float64" ) Then
+			If opt_arch <> "x64" Err "Intrinsic types only available on x64"
+			ty = New TFloat64Type
+		Else If CParse( "wparam" ) Then
+			If opt_platform <> "win32" Err "WParam types only available on Win32"
+			ty = New TWParamType
+		Else If CParse( "lparam" ) Then
+			If opt_platform <> "win32" Err "LParam types only available on Win32"
+			ty = New TLParamType
+		End If
 
+		While CParse("ptr")
+			ty = TType.MapToPointerType(ty)
+		Wend
+		Return ty
+	End	Method
 
+End Type

+ 72 - 50
parser.bmx

@@ -25,7 +25,8 @@ SuperStrict
 
 Import BRL.MaxUtil
 Import "toker.bmx"
-Import "iparser.bmx"
+
+Include "iparser.bmx"
 
 
 Global FILE_EXT$="bmx"
@@ -52,7 +53,7 @@ Type TForEachinStmt Extends TLoopStmt
 	Method OnCopy:TStmt( scope:TScopeDecl )
 		If loopLabel Then
 			If varExpr Then
-		Return New TForEachinStmt.Create( varid,varty,varlocal,expr.Copy(),block.CopyBlock( scope ),TLoopLabelDecl(loopLabel.Copy()), varExpr.Copy() )
+				Return New TForEachinStmt.Create( varid,varty,varlocal,expr.Copy(),block.CopyBlock( scope ),TLoopLabelDecl(loopLabel.Copy()), varExpr.Copy() )
 			Else
 				Return New TForEachinStmt.Create( varid,varty,varlocal,expr.Copy(),block.CopyBlock( scope ),TLoopLabelDecl(loopLabel.Copy()), Null )
 			End If
@@ -66,6 +67,7 @@ Type TForEachinStmt Extends TLoopStmt
 	End Method
 
 	Method OnSemant()
+
 		expr=expr.Semant()
 
 		If TArrayType( expr.exprType ) Or TStringType( expr.exprType )
@@ -174,6 +176,10 @@ Type TForEachinStmt Extends TLoopStmt
 
 		Else If TObjectType( expr.exprType )
 			Local tmpDecl:TDeclStmt
+			Local iterable:Int
+			If TObjectType(expr.exprType).classDecl.ImplementsInterface("iiterable") Then
+				iterable = True
+			End If
 
 			If TInvokeExpr(expr) Or TInvokeMemberExpr(expr) Then
 				Local tmpVar:TLocalDecl=New TLocalDecl.Create( "",expr.exprType,expr,,True )
@@ -182,11 +188,22 @@ Type TForEachinStmt Extends TLoopStmt
 				expr = New TVarExpr.Create( tmpVar )
 			End If
 
-			Local enumerInit:TExpr=New TFuncCallExpr.Create( New TIdentExpr.Create( "ObjectEnumerator",expr ) )
+			Local enumerInit:TExpr
+			If iterable Then
+				enumerInit = New TFuncCallExpr.Create( New TIdentExpr.Create( "Iterator",expr ) )
+			Else
+				enumerInit = New TFuncCallExpr.Create( New TIdentExpr.Create( "ObjectEnumerator",expr ) )
+			End If
 			Local enumerTmp:TLocalDecl=New TLocalDecl.Create( "",Null,enumerInit,,True )
 
 			Local hasNextExpr:TExpr=New TFuncCallExpr.Create( New TIdentExpr.Create( "HasNext",New TVarExpr.Create( enumerTmp ) ) )
-			Local nextObjExpr:TExpr=New TFuncCallExpr.Create( New TIdentExpr.Create( "NextObject",New TVarExpr.Create( enumerTmp ) ) )
+			
+			Local nextObjExpr:TExpr
+			If iterable Then
+				nextObjExpr = New TFuncCallExpr.Create( New TIdentExpr.Create( "NextElement",New TVarExpr.Create( enumerTmp ) ) )
+			Else
+				nextObjExpr = New TFuncCallExpr.Create( New TIdentExpr.Create( "NextObject",New TVarExpr.Create( enumerTmp ) ) )
+			End If
 
 			Local cont:TContinueStmt
 			
@@ -196,7 +213,7 @@ Type TForEachinStmt Extends TLoopStmt
 
 				Local cExpr:TExpr
 				
-				If TIdentType(varty) And TIdentType(varty).ident = "Object" Then
+				If iterable Or (TIdentType(varty) And TIdentType(varty).ident = "Object") Then
 					cExpr = nextObjExpr
 				Else
 					cExpr = New TCastExpr.Create( varty, nextObjExpr,CAST_EXPLICIT )
@@ -320,8 +337,6 @@ Type TParser
 	Field _toker:TToker
 	Field _toke:String
 	Field _tokeType:Int
-	'Ronny: _tokerStack is unused
-	'Field _tokerStack:TList=New TList'<TToker>
 
 	Field _block:TBlockDecl
 	Field _blockStack:TList=New TList'<TBlockDecl>
@@ -499,12 +514,13 @@ Type TParser
 		If CParse( "." ) id:+"."+ParseIdent()
 		If CParse( "." ) id:+"."+ParseIdent()
 
-		Local args:TIdentType[]
+		Local args:TType[]
 		If CParse( "<" )
 			Local nargs:Int
 			Repeat
-				Local arg:TIdentType=ParseIdentType()
-				If args.Length=nargs args=args+ New TIdentType[10]
+				'Local arg:TIdentType=ParseIdentType()
+				Local arg:TType = ParseType()
+				If args.Length=nargs args=args+ New TType[10]
 				args[nargs]=arg
 				nargs:+1
 			Until Not CParse(",")
@@ -1828,34 +1844,22 @@ End Rem
 		If CParse( "local" )
 			varlocal=True
 			varid=ParseIdent()
-			'If Not CParse( ":=" )
-				varty=ParseDeclType()
-				If varty._flags & (TType.T_CHAR_PTR | TType.T_SHORT_PTR) Then
-					DoErr "Illegal variable type"
-				End If
-				
-				Parse( "=" )
-			'EndIf
-		Else
-			varlocal=False
-			
-			varExpr=ParsePrimaryExpr( False )
-			
-			'varExpr = New TIdentExpr.Create( ParseIdent(),varExpr )
 
-			'ParseConstNumberType()
+			varty=ParseDeclType()
+			If varty._flags & (TType.T_CHAR_PTR | TType.T_SHORT_PTR) Then
+				DoErr "Illegal variable type"
+			End If
+			
+			Parse( "=" )
 			
-'			varid=ParseIdent()
+			' use an ident expr to pass the variable to different parts of the statement.
+			' the original implementation passed decl references, which cause problems if we wanted to
+			' copy the statement later.
+			varExpr = New TIdentExpr.Create(varid)
+		Else
+			varlocal=False
 
-			'While Cparse(".")
-				'NextToke
-				
-			'	varExpr = New TIdentExpr.Create( ParseIdent(),varExpr )
-				
-			'	ParseConstNumberType()
-			'Wend
-			' eat any type stuff
-'			ParseConstNumberType()
+			varExpr=ParsePrimaryExpr( False )
 
 			Parse "="
 		EndIf
@@ -1866,10 +1870,6 @@ End Rem
 
 			PushBlock block
 			While Not CParse( "next" )
-				'If CParse( "end" )
-				'	CParse "for"
-				'	Exit
-				'EndIf
 				ParseStmt
 			Wend
 			PopBlock
@@ -1907,8 +1907,10 @@ End Rem
 		If varlocal
 			Local indexVar:TLocalDecl=New TLocalDecl.Create( varid,varty,New TCastExpr.Create( varty,from,1 ),0 )
 			init=New TDeclStmt.Create( indexVar )
-			expr=New TBinaryCompareExpr.Create( op,New TVarExpr.Create( indexVar ),New TCastExpr.Create( varty,term,1 ) )
-			incr=New TAssignStmt.Create( "=",New TVarExpr.Create( indexVar ),New TBinaryMathExpr.Create( "+",New TVarExpr.Create( indexVar ),New TCastExpr.Create( varty,stp,1 ) ) )
+'			expr=New TBinaryCompareExpr.Create( op,New TVarExpr.Create( indexVar ),New TCastExpr.Create( varty,term,1 ) )
+'			incr=New TAssignStmt.Create( "=",New TVarExpr.Create( indexVar ),New TBinaryMathExpr.Create( "+",New TVarExpr.Create( indexVar ),New TCastExpr.Create( varty,stp,1 ) ) )
+			expr=New TBinaryCompareExpr.Create( op, varExpr,New TCastExpr.Create( varty,term,1 ) )
+			incr=New TAssignStmt.Create( "=",varExpr,New TBinaryMathExpr.Create( "+",varExpr,New TCastExpr.Create( varty,stp,1 ) ) )
 		Else
 			' varty is NULL here for the casts. We will back-populate it later.
 '			init=New TAssignStmt.Create( "=",New TIdentExpr.Create( varid ),from )
@@ -1923,10 +1925,6 @@ End Rem
 
 		PushBlock block
 		While Not CParse( "next" )
-			'If CParse( "end" )
-			'	CParse "for"
-			'	Exit
-			'EndIf
 			ParseStmt
 		Wend
 		PopBlock
@@ -2935,10 +2933,14 @@ End Rem
 	Method ParseClassDecl:TClassDecl( toke$,attrs:Int )
 		SetErr
 
+		Local calculatedStartLine:Int = _toker.Line()
+		Local startLine:Int = _toker._line
+
 		If toke Parse toke
 
 		Local id$=ParseIdent()
-		Local args:TStack = New TStack
+
+		Local args:TList = New TList
 		Local superTy:TIdentType
 		Local imps:TIdentType[]
 		Local meta:TMetadata
@@ -2967,7 +2969,15 @@ End Rem
 				'If args.Length=nargs args=args + New TClassDecl[10]
 				'args[nargs]=decl
 				'nargs:+1
-				args.Push ParseIdent()
+				Local arg:TTemplateArg = New TTemplateArg
+				arg.ident = ParseIdent()
+				
+				If CParse("extends") Then
+					arg.superTy = ParseIdentType()
+				End If
+				
+				args.AddLast arg
+
 			Until Not CParse(",")
 			'args=args[..nargs]
 
@@ -3086,7 +3096,15 @@ End Rem
 		'check for metadata
 		meta = ParseMetaData()
 
-		Local classDecl:TClassDecl=New TClassDecl.Create( id,String[](args.ToArray()),superTy,imps,attrs )
+		
+		Local sargs:TTemplateArg[] = New TTemplateArg[args.Count()]
+		Local i:Int = 0
+		For Local arg:TTemplateArg = EachIn args
+			sargs[i] = arg
+			i :+ 1
+		Next
+
+		Local classDecl:TClassDecl=New TClassDecl.Create( id,sargs,superTy,imps,attrs )
 		
 		If meta Then
 			If attrs & CLASS_STRUCT
@@ -3205,6 +3223,11 @@ End Rem
 				Err "Syntax error - expecting class member declaration, not '" + _toke + "'"
 			End Select
 		Forever
+		
+		If Not args.IsEmpty() Then
+			Local endline:Int = _toker._line
+			classDecl.templateSource = New TTemplateRecord.Create(calculatedStartLine - 1, _toker._path, _toker.Join(startLine, endLine, "~n"))
+		End If
 
 		If toke Parse toke
 
@@ -3581,7 +3604,7 @@ End Rem
 			Case "struct"
 				_module.InsertDecl ParseClassDecl( _toke,attrs | CLASS_STRUCT )
 			Case "type"
-				_module.InsertDecl ParseClassDecl( _toke,attrs )
+				_module.InsertDecl ParseClassDecl( _toke,attrs)
 			Case "interface"
 				_module.InsertDecl ParseClassDecl( _toke,attrs|CLASS_INTERFACE|DECL_ABSTRACT )
 			Case "function"
@@ -4215,4 +4238,3 @@ Type TCastDets
 	Field api:String
 	
 End Type
-

+ 29 - 9
stmt.bmx

@@ -69,10 +69,11 @@ Type TDeclStmt Extends TStmt
 	End Method
 
 	Method OnCopy:TStmt( scope:TScopeDecl )
-		If Not decl.scope Then
-			decl.scope = scope
-		End If
-		Return New TDeclStmt.Create( decl.Copy(), generated )
+		Local d:TDecl = decl.Copy()
+		'If Not d.scope Then
+		d.scope = Null
+		'End If
+		Return New TDeclStmt.Create( d, generated )
 	End Method
 	
 	Method OnSemant()
@@ -223,8 +224,14 @@ Type TReturnStmt Extends TStmt
 	End Method
 
 	Method OnCopy:TStmt( scope:TScopeDecl )
-		If expr Return New TReturnStmt.Create( expr.Copy() )
-		Return New TReturnStmt.Create( Null )
+		Local r:TReturnStmt
+		If expr Then
+			r = New TReturnStmt.Create( expr.Copy() )
+		Else
+			r = New TReturnStmt.Create( Null )
+		End If
+		r.fRetType = fRetType
+		Return r
 	End Method
 	
 	Method OnSemant()
@@ -539,7 +546,14 @@ Type TForStmt Extends TLoopStmt
 	End Method
 
 	Method OnCopy:TStmt( scope:TScopeDecl )
-		Return New TForStmt.Create( init.Copy( scope ),expr.Copy(),incr.Copy( scope ),block.CopyBlock( scope ),TLoopLabelDecl(loopLabel.Copy()) )
+	
+		Local b:TBlockDecl = block.CopyBlock( scope )
+	
+		If loopLabel Then
+			Return New TForStmt.Create( init.Copy( Null ),expr.Copy(),incr.Copy( Null ),b,TLoopLabelDecl(loopLabel.Copy()) )
+		Else
+			Return New TForStmt.Create( init.Copy( Null ),expr.Copy(),incr.Copy(Null),b,Null )
+		End If
 	End Method
 	
 	Method OnSemant()
@@ -552,8 +566,6 @@ Type TForStmt Extends TLoopStmt
 		End If
 		init.Semant
 
-		PopEnv
-
 		If updateCastTypes Then
 			' ty in the casts are currently Null - we didn't know at the time of creating the statement, what the variable type was.
 			' Now we do, so we'll fill in the gaps.
@@ -561,8 +573,11 @@ Type TForStmt Extends TLoopStmt
 			TCastExpr(TBinaryMathExpr(TAssignStmt(incr).rhs).rhs).ty = TAssignStmt(init).lhs.exprType.Copy()
 		End If
 
+		' scope for expression part should be block-scope
 		expr=expr.Semant()
 
+		PopEnv
+
 		' for anything other than a const value, use a new local variable
 		If Not TConstExpr(TBinaryCompareExpr(expr).rhs) Then
 			Local tmp:TLocalDecl=New TLocalDecl.Create( "", TBinaryCompareExpr(expr).rhs.exprType,TBinaryCompareExpr(expr).rhs,, True )
@@ -575,7 +590,12 @@ Type TForStmt Extends TLoopStmt
 		block.Semant
 		_loopnest:-1
 
+		' scope for increment part is also block-scope
+		PushEnv block
+
 		incr.Semant
+		
+		PopEnv
 
 		'dodgy as hell! Reverse comparison for backward loops!
 		Local assop:TAssignStmt=TAssignStmt( incr )

+ 13 - 2
toker.bmx

@@ -63,8 +63,9 @@ Type TToker
 	
 	Field _lookingForEndRem:Int
 	Field _preprocess:Int
+	Field _lineOffset:Int
 	
-	Method Create:TToker( path$,source$, preprocess:Int = False )
+	Method Create:TToker( path$,source$, preprocess:Int = False, lineOffset:Int = 0 )
 		_path=path
 		_line=1
 		_source=source
@@ -73,6 +74,7 @@ Type TToker
 		_tokePos=0
 		_lines = source.split("~n")
 		_preprocess = preprocess
+		_lineOffset = lineOffset
 		If Not _keywords Then
 			initKeywords()
 		End If
@@ -112,7 +114,7 @@ Type TToker
 	End Method
 	
 	Method Line:Int()
-		Return _line
+		Return _line + _lineOffset
 	End Method
 	
 	Method NextToke$()
@@ -399,6 +401,15 @@ Type TToker
 		Return _lastTSTR
 	End Method
 	
+	Method Join:String(startLine:Int, endLine:Int, s:String)
+		Local sb:TStringBuffer = New TStringBuffer
+		For Local i:Int = startLine - 1 To endLine
+			sb.Append(_lines[i])
+			sb.Append(s)
+		Next
+		Return sb.ToString()
+	End Method
+	
 	Field _lastTSTR:String
 	
 End Type

+ 12 - 0
translator.bmx

@@ -443,6 +443,12 @@ Type TTranslator
 '		Case "cpp"
 			If TModuleDecl( decl.scope )
 				munged=decl.ModuleScope().munged+"_"+id
+				
+				If TClassDecl(decl) And TClassDecl(decl).instArgs Then
+					For Local ty:TType = EachIn TClassDecl(decl).instArgs
+						munged :+ TransMangleType(ty)
+					Next
+				End If
 			EndIf
 
 			If TModuleDecl( decl )
@@ -459,6 +465,12 @@ Type TTranslator
 				If decl.scope Then
 					munged = decl.scope.munged + "_" + id
 					
+					If TClassDecl(decl) And TClassDecl(decl).instArgs Then
+						For Local ty:TType = EachIn TClassDecl(decl).instArgs
+							munged :+ TransMangleType(ty)
+						Next
+					End If
+					
 					' fields are lowercase with underscore prefix.
 					' a function pointer with FUNC_METHOD is a field function pointer.
 					If TFieldDecl(decl) Or (TFuncDecl(decl) And (decl.attrs & FUNC_METHOD) And (decl.attrs & FUNC_PTR)) Then

+ 14 - 1
type.bmx

@@ -1539,7 +1539,7 @@ Type TIdentType Extends TType
 	
 	
 	Method Semant:TType(ignoreNotFoundError:Int = 0)
-'If ident="obj" DebugStop
+'If ident="IPair" DebugStop
 		If Not ident Return TType.nullObjectType
 
 		Local targs:TType[args.Length]
@@ -2009,3 +2009,16 @@ Type TLParamType Extends TParamType
 	End Method
 
 End Type
+
+Type TTemplateArg
+	Field ident:String
+	Field superTy:TType
+	
+	Method ToString:String()
+		Local s:String = ident
+		If superTy Then
+			s :+ " Extends " + superTy.ToString()
+		End If
+	End Method
+End Type
+