BriskVM 2 released

Miscellaneous Forums/Blitz Showcase/BriskVM 2 released

Here's a version of fredborg's code that allows drop on builds:
'
' Simple BriskVM Command Set Assistant for BlitzMax
'
' created by Mikkel Fredborg
' use as you please!
'

SuperStrict

Import koriolis.briskvm

'
' Start of Demo
'

Init()

'
' Replace with your own files etc.

Local source:String = AppArgs[1]'AppArgs[0].Replace(".debug","").Replace(".exe",".bmx") ' find the source file :)
ScanSource(source)

Local srcDir:String = ExtractDir(source) + "/"
Local srcName:String = StripAll(source)
'
' You can add as many source files as you need. Imports and Includes are automatically checked as well.
'

' Write all the output
WriteCommandSet(srcDir + srcName + ".bcs")
WriteCSS(srcDir + srcName + ".css")
WriteDocs(srcDir + srcName + "_doc.htm", srcDir + srcName + ".css")
GenerateBMXInvoker(srcDir + srcName + ".bcs", srcDir + srcName + "_invoker.bmx")

Print "DONE"
Input()
End

'
' End of demo
'


Type TType 'expose
	Global _list:TList = New TList

	Field name:String
	Field fields:TList = New TList
	Field methods:TList = New TList
	
	Method New()
		_list.AddLast Self
	EndMethod
	
	Method AddField(s:String) 'expose
		fields.AddLast s
	EndMethod
	
	Method AddMethod(s:String) 'expose
		methods.AddLast s
	EndMethod
EndType

Type TFunc 'expose
	Global _list:TList = New TList
	
	Field name:String 'expose
	
	Method New() 
		_list.AddLast Self
	EndMethod
EndType

Global fun_count:Int
Global met_count:Int
Global typ_count:Int

Global files:TList

Function Init() 'expose
	files = New TList
	fun_count = 0
	met_count = 0
	typ_count = 0
EndFunction

'
' Just for convenience...
Function WriteCSS(output:String) 'expose

	Local out:TStream = WriteStream(output)
	If out = Null Then Throw "Error!"
	
	WriteLine(out,"body {")
	WriteLine(out,"	margin: 0px 0px 0px 0px;")
	WriteLine(out,"	padding: 0px 0px 0px 0px;")
	WriteLine(out,"	background-color: #FFFFFF;")
	WriteLine(out,"	color: #000000;")
	WriteLine(out,"	font-family: verdana, arial, sans-serif;")
	WriteLine(out,"	font-size: 10px;")
	WriteLine(out,"}")

	WriteLine(out,".header {")
	WriteLine(out,"	padding-Left: 10px;")
	WriteLine(out,"	margin-top: 10px;")
	WriteLine(out,"	margin-bottom: 10px;")
	WriteLine(out,"	font-size: 20px;")
	WriteLine(out,"}")

	WriteLine(out,".Type, .Function, .Const, .stat {")
	WriteLine(out,"	padding-Left: 20px;")
	WriteLine(out,"	font-weight: bold;")
	WriteLine(out,"}")

	WriteLine(out,".End {")
	WriteLine(out,"	padding-bottom: 20px;")
	WriteLine(out,"}")

	WriteLine(out,".Method, .Field {")
	WriteLine(out,"	padding-Left: 40px;")
	WriteLine(out,"}")
	
	CloseFile out
	
EndFunction

Function WriteDocs(output:String,style:String) 'expose
	
	Print "Generating Documentation!"
		
	Local out:TStream = WriteStream(output)
	If out = Null Then Throw "Error!"
	
	WriteLine(out,"<html><head>")
	WriteLine(out,"<title>Script Reference</title>")
	WriteLine(out,"<link href='"+style+"' rel='stylesheet' Type='text/css' />")
	WriteLine(out,"</head><body>")
	WriteLine(out,"<div class='header'>Functions</div>")
	For Local f:TFunc = EachIn TFunc._list
		WriteLine(out,"<div class='function'>Function "+f.name.Replace(" Final","")+"</div>")
	Next

	Local met:Int = 0

	WriteLine(out,"<div class='header'>Types</div>")
	For Local t:TType = EachIn TType._list
	
		If t.fields.IsEmpty() And t.methods.IsEmpty() Then Continue
		
		WriteLine(out,"<div class='type'>Type "+t.name+"</div>")
		For Local f:String = EachIn t.fields
			WriteLine(out,"<div class='field'>"+f+"</div>")
		Next
		WriteLine(out,"")
		For Local m:String = EachIn t.methods
			WriteLine(out,"<div class='method'>"+m.Replace(" Final","")+"</div>")
			met:+1
		Next
		WriteLine(out,"<div class='end type'>EndType</div>")
		WriteLine(out,"")
	Next

	WriteLine(out,"<div class='header'>Constants</div>")
	For Local f:TConstant = EachIn TConstant._list
		WriteLine(out,"<div class='const'>Const "+f.name+":Int = "+f.value+"</div>")
	Next
	
	WriteLine(out,"<div class='header'>Stats</div>")
	WriteLine(out,"<div class='stat'>"+TFunc._list.Count()+" Functions</div>")
	WriteLine(out,"<div class='stat'>"+TType._list.Count()+" Types</div>")
	WriteLine(out,"<div class='stat'>"+met+" Methods</div>")
	WriteLine(out,"<div class='stat'>"+TConstant._list.Count()+" Constants</div>")
	WriteLine(out,"<div class='end'></div>")
	WriteLine(out,"</body></html>")
	CloseFile out
	
EndFunction

Function WriteCommandSet(output:String) 'expose

	Print "Writing Command Set!"
	
	Local out:TStream = WriteStream(output)
	
	If out = Null Then Throw "Error!"
	
	WriteLine(out,".set ~q"+StripAll(output)+"~q")
	WriteLine(out,"")
	WriteLine(out,"!annotations")		' when on, allows to specify meta data in the form of annotations (@MyAnnotation = "SomeValue")
	WriteLine(out,"!explicit")			' when on, forces explicit declaration of variables
	WriteLine(out,"!classes")			' when on, enables objective oriented programming
	WriteLine(out,"!serialization")		' when on, enables serializing (loading/saving) of objects
	WriteLine(out,"!scoping")			' when on, enables to use access specifiers for type fields and methods
	WriteLine(out,"!funcpointers")		' when on, enables to use function pointers
	WriteLine(out,"!reflection")		' when on, enables to use reflection (ie determine at runtime the existing classes, examine their fields and access them)
	WriteLine(out,"!altsyntax")			' when on, scripts use a slightly different syntax
	WriteLine(out,"!properties")
	WriteLine(out,"!assertions")
	WriteLine(out,"!typeInference")
	WriteLine(out,"")
	
	WriteLine(out,"FlushMem<BVM_FlushMem>()")
	WriteLine(out,"FlushDebugLog<BVM_FlushDebugLog>()")
'	WriteLine(out,"DebugLog<BVM_DebugLog>(txt$)")
	WriteLine(out,"")
		
	WriteLine(out,"Abstract Type THostObject<Object>")
	WriteLine(out,"~tMethod ToString:String()")
	WriteLine(out,"End Type")
	WriteLine(out,"")
		
	' The not so obvious part:
	' Let's declare the host TList type, and its accompanying enumerator (will allow us to use EachIn in scripts, on host language lists) 
	WriteLine(out,"Type THostListEnum<TListEnum> Extends THostObject")
	WriteLine(out,"~tMethod MoveNext<HasNext>%()")
	WriteLine(out,"~tMethod GetItem<NextObject>:THostObject()")
	WriteLine(out,"End Type")
	WriteLine(out,"")

	WriteLine(out,"Type THostList<TList> Extends THostObject")
	WriteLine(out,"~tMethod AddLast( value:THostObject )")
	WriteLine(out,"~tMethod AddFirst( value:THostObject )")
	WriteLine(out,"~tMethod First:THostObject()")
	WriteLine(out,"~tMethod Last:THostObject()")
	WriteLine(out,"~tMethod RemoveFirst:THostObject()")
	WriteLine(out,"~tMethod RemoveLast:THostObject()")
	WriteLine(out,"~tMethod GetEnumerator<ObjectEnumerator>:THostListEnum()")
	WriteLine(out,"~tMethod IsEmpty%()")
	WriteLine(out,"~tMethod Clear()")
	WriteLine(out,"~tMethod Contains%( value:THostObject )")
	WriteLine(out,"~tMethod ValueAtIndex:THostObject( index% )")
	WriteLine(out,"~tMethod Count%()")
	WriteLine(out,"~tMethod Remove( value:THostObject )")
	WriteLine(out,"~tMethod Reverse()")
	WriteLine(out,"~tMethod Reversed:THostList()")
	WriteLine(out,"End Type")
	WriteLine(out,"")
			
	For Local f:String = EachIn files
		Local t:String = ExposeSource( f )
		If t
			Local ts:String[] = t.split("~n")
			For Local s:String = EachIn ts
				WriteLine(out,s)
			Next
		EndIf
	Next
	
	CloseStream(out)
EndFunction

Type TConstant 'expose
	Global _list:TList = New TList
	
	Field name:String 'expose
	Field value:String 'expose
	
	Method New()
		_list.AddLast Self
	EndMethod
	
EndType

Function ExposeSource:String(f:String) 'expose

	Global const_list:TList = New TList

	Local out:String
	
	Local tab:Int = 0
	
	Local expose:Int = 0
	
	Local func_list:TList = New TList
	Local intype:String
	
	Local ty:TType
	
	Local s:TStream = ReadStream(f)
	If s
		While Not Eof(s)
			Local t:String = (ReadLine(s)).Trim()
			t = t.Replace(":Object",":THostObject")
			t = t.Replace(":TList",":THostList")
			
			If t.Contains("'expose")
			If t.Contains("script_")
				Local a:Int = t.find(":")
				Local b:Int = t.find("(")
				Local c:String = ":"
				If b<a And b>-1 Then c = "("
			
				Local bob:String
				bob = (t[..t.find(c)]).Replace("Function","").Trim()
				
				bob =  "<"+bob+">"
				t = t.Replace("script_","")
				Local i:Int = t.find(c)
				t = t[..i]+bob+t[i..]
'					Print t
				EndIf
			EndIf
			
			Local l:String = t.tolower().Replace("end ","end")
	
			If l.Find("endtype") = 0
				tab:-1
				
				ty = Null
				
				If expose
					expose = False
					out :+ (RSet("",tab).Replace(" ","~t"))+"End Type"+"~n"
					out :+ "~n"
				EndIf
	
				For Local f:String = EachIn func_list
					out :+ (RSet("",tab).Replace(" ","~t"))+f+"~n"
				Next
				
				intype = ""
				func_list.Clear()
			EndIf
						
			If t.Contains("'expose")
				t = t.Replace("#",":Float")
				t = t.Replace("%",":Int")
				t = t.Replace("$",":String")
				Local pos:Int = t.find("'expose")
				Local opt:String = (t[pos+7..]).Trim()
									
				t = (t[..pos]).Trim()
				
				If opt
					If opt.contains("params=")
						Local p2:Int = opt.find("params=")
						Local v:Int = Int(opt[p2+7..])
						
						Local a:Int = t.find("(")+1
						While v>0
							a = t.find(",",a+1)
							v:-1
						Wend
						t = t[..a]+")"
'						Print "Modified: "+t
'						Print "Options: Parameters = "+v
					EndIf
				EndIf
				
				If l.Find("type") = 0
					expose = True
			
					If l.contains("extends") = False
						t :+ " Extends THostObject"
					EndIf
					Local a:Int = l.find("'")
					Local b:Int = l.find("extends")
					If b<a And b>-1 Then a = b
					intype = (t[t.find("Type")+4..a]).Trim()
					
					ty = New TType
					ty.name = intype.Replace("Abstract","")
					
					typ_count:+1
				EndIf
				
				If l.Find("const") = 0
					Local c:TConstant = New TConstant
					c.name = t.Replace("Const","")
					c.name = c.name[..c.name.find("=")]
					c.name = c.name.Replace(":Int","")
					c.name = c.name.Replace(":Float","")
					c.name = c.name.Replace(":Byte","")
					c.name = c.name.Replace(":Long","")
					c.name = c.name.Replace(":String","")

					c.name = c.name.Replace("%","")
					c.name = c.name.Replace("#","")
					c.name = c.name.Replace("$","")
					c.name = c.name.Replace("!","")
					c.name = c.name.Replace("&","")
					
					c.name = c.name.Trim()
					c.value = (t[ t.find("=")+1 .. ]).Trim()
					t = t.Replace(c.value,Int(c.value))
					c.value = Int(c.value)
					const_list.AddLast c
				Else
					For Local c:TConstant = EachIn const_list
						t = t.Replace(c.name,c.value)
					Next
					
					'
					' Replace | variable stuff
					While t.contains("|")
						Local a:Int = t.find("=")
						Local b:Int = t.find("|",a)
						Local c:Int = t.find("|",b+1)
						Local d:Int = t.find(",",b+1)
						Local e:Int = t.find(")",b+1)
						If c<0 Then c = d
						If d<0 Then d = e
						If d<0 Then d = c
						If e<0 Then d = c
						c = Min(c,Min(d,e))
						Local vala:Int = Int(t[a+1..b])
						Local valb:Int = Int(t[b+1..c])
						t = t.Replace( t[a+1..c], (vala|valb))
						
						't = ""
					Wend
				EndIf
				
				If l.find("function") = 0
					t = t.Replace("#",":Float")
					t = t.Replace("%",":Int")
					
					t = t.Replace("Function","").Trim()
					t = t[..1].toupper()+t[1..]
					If intype
						Local s:String = t
						Local a:Int = t.Find(":")
						Local b:Int = t.Find("(")
						Local c:Int = a
						If b<a And b>-1 Then c = b
						Local funcname:String = t[..c]
						' Print intype+","+funcname
						
						Local fu:TFunc = New TFunc
						fu.name = intype[1..]+t
						
						s = intype[1..]+t[..c]+"<"+intype+"."+funcname+">"+t[c..]
						func_list.AddLast s
					Else
					
						Local fu:TFunc = New TFunc
						fu.name = t
						
						out :+ (RSet("",tab).Replace(" ","~t"))+t+"~n"
					EndIf
					
					fun_count :+ 1
				EndIf
				
				If l.find("method") = 0
					met_count :+ 1
					ty.AddMethod(t)
				EndIf
				
				If l.find("field") = 0
					ty.AddField(t)
				EndIf
				
				'
									
				If l.Find("type") = 0 ..
					Or l.find("global") = 0 .. 
					Or l.find("const") = 0 ..
					Or l.find("method") = 0 ..
					Or l.find("field") = 0
					
					If t.Find("Abstract")>-1
						t = t.Replace("Abstract","")
						If t.Find("Type")>-1
							t = "Abstract "+t
						EndIf
					EndIf
					
					t = t.Trim()
					
					out :+ (RSet("",tab).Replace(" ","~t"))+t+"~n"
				EndIf
			EndIf

			If l.Find("type") = 0
				tab:+1
			EndIf
		Wend
		CloseStream s
	EndIf
	
	Return out

EndFunction
	
Function ScanSource(m:String,trace:Int=True) 'expose

	For Local f:String = EachIn files
		If StripAll(f).ToLower() = StripAll(m).ToLower() 
		'	Print "File: "+m+" ALREADY PROCESSED!"
			Return
		EndIf
	Next

	files.AddLast m

	Local comment:Int = 0

	Local s:TStream = ReadStream(m)
	If s
		Print "Scanning: "+m
		While Not Eof(s)
			Local t:String = (ReadLine(s)).ToLower().Trim().Replace("end ","end")
			
			If t.Find("'") = 0 Then comment = 1
			If t.find("rem") = 0 Then comment = 2
			If t.find("endrem") = 0 Then comment = 0
			
			If comment = False
				If trace 
					If t.Find("import") = 0 Or t.Find("include") = 0
								
						Local a:Int = t.find("~q")
						Local b:Int = t.find("~q",a+1)

						If b>a 
							ScanSource(ExtractDir(m)+"/"+t[a+1..b])
						EndIf
					EndIf
				EndIf
			EndIf
			
			If comment = 1 Then comment = 0
		Wend
		CloseStream s
	Else
		Print "Couldn't open: "+m
	EndIf

EndFunction



Function GenerateBMXInvoker(bcsPath_$, invokerPath_$) 
	
	Print "Generating invoker!"
	Local invCount% = BVM_CountInvokerGenerators()	
	For Local i:Int = 0 Until invCount
		Local extCount% = BVM_GetInvokerGeneratorFileExtCount(i)
		For Local j:Int = 0 Until extCount
			If BVM_GetInvokerGeneratorFileExt(i, j).ToLower() = "bmx" Then
				Local hCmdSet% = BVM_LoadCommandSet(bcsPath_)
				If hCmdSet = BVM_INVALID_CMD_SET Then
					RuntimeError(BVM_GetLastErrorMsg())
				EndIf
				If Not BVM_GenerateInvoker(hCmdSet, invokerPath_, i) Then
					RuntimeError(BVM_GetLastErrorMsg())
				EndIf
				Return
			EndIf
		Next		
	Next
	RuntimeError("Invoker Generator not found")
End Function