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