BriskVM 2 released

Miscellaneous Forums/Blitz Showcase/BriskVM 2 released

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

I've made a simple command set generator (like noels I assume) which runs through selected files and spits out the nescessary invoker + command set along with some very basic documentation.

Tag functions, types, fields, or methods with 'expose to let the script access them.

Here's the source along with a simple example which uses the source code itself as a demonstration...

'
' 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[0].Replace(".debug","").Replace(".exe",".bmx") ' find the source file :)
ScanSource(source)

'
' You can add as many source files as you need. Imports and Includes are automatically checked as well.
'

' Write all the output
WriteCommandSet("example.bcs")
WriteCSS("example.css")
WriteDocs("example.htm","example.css")
GenerateBMXInvoker("example.bcs", "example_invoker.bmx")

Print "DONE"
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


Remember to copy bvmtools.dll and briskvm.dll to the same folder as the source code, otherwise it won't run...

BriskVM2 is superb!