Bladeren bron

Implemented generics import support.

woollybah 8 jaren geleden
bovenliggende
commit
5bce4b7b60
7 gewijzigde bestanden met toevoegingen van 1078 en 508 verwijderingen
  1. 266 0
      base64.bmx
  2. 458 406
      config.bmx
  3. 83 47
      ctranslator.bmx
  4. 14 3
      decl.bmx
  5. 232 46
      iparser.bmx
  6. 12 4
      parser.bmx
  7. 13 2
      toker.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
+

+ 458 - 406
config.bmx

@@ -1,407 +1,459 @@
-' 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 "options.bmx"
-Import "base.stringhelper.bmx"
-
-
-' debugging help
-Const DEBUG:Int = False
-Const ABORT_ON_NULL:Int = True
-Const PROFILER:Int = False
+' 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
+
+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)
+		
+		Local dlen:Int = 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)
+		
+		Local dlen:Int = 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

+ 83 - 47
ctranslator.bmx

@@ -4870,9 +4870,9 @@ End Rem
 
 	Method EmitIfcClassDecl(classDecl:TClassDecl)
 	
-		If classDecl.args Then
-			Return
-		End If
+		'If classDecl.args Then
+		'	Return
+		'End If
 
 		Local head:String = classDecl.ident + "^"
 		If classDecl.superClass Then
@@ -4896,56 +4896,62 @@ End Rem
 		'PushMungScope
 		BeginLocalScope
 
-		' fields, globals and consts
-'		For Local decl:TDecl=EachIn classDecl.Decls()
-
-		' const
-		For Local cDecl:TConstDecl = EachIn classDecl.Decls()
-			cDecl.Semant()
-			
-			EmitIfcConstDecl(cDecl)
-		Next
-
-			' global
-		For Local gDecl:TGlobalDecl = EachIn classDecl.Decls()
-			gDecl.Semant()
 
-			EmitIfcGlobalDecl(gDecl)
-		Next
-
-
-			' field
-		For Local fDecl:TFieldDecl = EachIn classDecl.Decls()
-			fDecl.Semant()
-
-			EmitIfcFieldDecl(fDecl)
-		Next
+		If Not classDecl.templateSource Then
+	
+			' const
+			For Local cDecl:TConstDecl = EachIn classDecl.Decls()
+				cDecl.Semant()
+				
+				EmitIfcConstDecl(cDecl)
+			Next
+	
+				' global
+			For Local gDecl:TGlobalDecl = EachIn classDecl.Decls()
+				gDecl.Semant()
+	
+				EmitIfcGlobalDecl(gDecl)
+			Next
+	
+	
+				' field
+			For Local fDecl:TFieldDecl = EachIn classDecl.Decls()
+				fDecl.Semant()
+	
+				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
-
-			For Local decl:TDecl=EachIn classDecl.Decls()
-
-				Local fdecl:TFuncDecl=TFuncDecl( decl )
-				If fdecl
-					If Not equalsIfcBuiltInFunc(classDecl, fdecl) Then
-						EmitIfcClassFuncDecl fdecl
-					End If
-					Continue
-				EndIf
+				If Not classDecl.attrs & CLASS_INTERFACE Then
+					Emit "-New()=" + Enquote("_" + classDecl.munged + "_New")
+				End If
 
-			Next
+				If classHierarchyHasFunction(classDecl, "Delete") Then
+					Emit "-Delete()=" + Enquote("_" + classDecl.munged + "_Delete")
+				End If
+	
+				For Local decl:TDecl=EachIn classDecl.Decls()
+	
+					Local fdecl:TFuncDecl=TFuncDecl( decl )
+					If fdecl
+						If Not equalsIfcBuiltInFunc(classDecl, fdecl) Then
+							EmitIfcClassFuncDecl fdecl
+						End If
+						Continue
+					EndIf
+	
+				Next
 			
-			Local flags:String
+			End If
 			
+			Local flags:String
+
 			If classDecl.IsAbstract() Then
 				flags :+ "A"
 			End If
@@ -4960,7 +4966,38 @@ 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()
 
@@ -5138,10 +5175,9 @@ 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
 

+ 14 - 3
decl.bmx

@@ -45,6 +45,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
@@ -2079,6 +2080,7 @@ 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 )
 	
@@ -2183,7 +2185,7 @@ Rem
 		Return inst
 	End Method
 End Rem
-	Method GenClassInstance:TClassDecl( instArgs:TType[] )
+	Method GenClassInstance:TClassDecl( instArgs:TType[], declImported:Int = False )
 
 		If instanceof InternalErr
 		
@@ -2211,16 +2213,20 @@ 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
 		
@@ -2239,6 +2245,10 @@ End Rem
 			inst.InsertDecl decl.Copy(), True
 		Next
 
+		If Not declImported Then
+			inst.scope = _env.scope
+		End If
+
 		Return inst
 	End Method
 
@@ -2605,6 +2615,7 @@ End Rem
 		EndIf
 
 		'NOTE: do this AFTER super semant so UpdateAttrs order is cool.
+
 		If AppScope() Then
 			AppScope().semantedClasses.AddLast Self
 		End If
@@ -3033,7 +3044,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

+ 232 - 46
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
 
@@ -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

+ 12 - 4
parser.bmx

@@ -25,7 +25,8 @@ SuperStrict
 
 Import BRL.MaxUtil
 Import "toker.bmx"
-Import "iparser.bmx"
+
+Include "iparser.bmx"
 
 
 Global FILE_EXT$="bmx"
@@ -320,8 +321,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>
@@ -2918,9 +2917,13 @@ 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:TList = New TList
 		Local superTy:TIdentType
 		Local imps:TIdentType[]
@@ -3204,6 +3207,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
 
@@ -3580,7 +3588,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"

+ 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