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!