BriskVM 2 released

Miscellaneous Forums/Blitz Showcase/BriskVM 2 released

Hello everyone.
I am pleased to announce the release of BriskVM 2.
If you haven't heard of BriskVM yet, this is a scripting engine targetted mainly at (but not limited to) game scripting.
The library is multi-platform (Windows, Linux and MacOs X) and multi-language (currently comes with bindings for BlitzMax, Blitz3D/BlitzPlus, PureBasic and C++).

So what are the specific strength of BriskVM I hear you ask.
If I were to give just two, it would be:
- It greatly simplifies the integration of scripts into an application, by drastically reducing the amount of glue code that one needs to write.
This is achieved in particular through the use of a special file in which you list everything that is to be accessible to the scripts (being functions, global variables or types).
Everything is then generated from this description. With this approach, you can focus on the WHAT, not the HOW (what to expose, not how to do it).
- The script language (BriskSL) is a BASIC dialect that is both easy and powerful.
In fact, the core of the language is BlitzBasic itself, augmented with a set of powerful extensions
such as Object Oriented Programming, reflection, serialization...

I invite anyone with an interest in scripts to give a whirl to the trial version, which will let you try every aspect of BriskVM (it is not feature limited).
Please note that the trial version is currently available for windows only, although the full version already comes for the three mentioned platforms.

For any question, suggestion, or whatever, feel free to come to the BriskVM forum:
http://koriolis-fx.com/forum

As for owners of a BriskVM 1 license (precedently known as "Blitz Virtual Machine"), they will benefit from a free upgrade to the equivalent BriskVM 2 license.
Simply send a mail with your licensing information (key and user name) at my contact address (see my profile) and you will receive a new license key, and
instructions to download BriskVM 2.

Great! :)

You forgot to include resources for B3D sample- Bouncey.bsl

Hi Koriolis, Might I make a friendly suggestion, on your site, where you list the product, http://www.koriolis-fx.com/index.php?id=97, I would suggest putting the prices on the product on there, so potential customers know how much it is, rather than for them to click on the shareit link.

Saying that, looks great and very very reasonable price :)

Good Luck with it, hope it goes well. Will prob get this

Koriolis, please could you explain the difference between independent and commercial version of your licence?

And there is small mistake in "Menu.bsl" in line: Using "Balls.bbm" (.bbm instead of .bvm)

You forgot to include resources for B3D sample- Bouncey.bsl
And there is small mistake in "Menu.bsl" in line: Using "Balls.bbm" (.bbm instead of .bvm)
Thanks, I will correct this.

where you list the product, http://www.koriolis-fx.com/index.php?id=97, I would suggest putting the prices on the product on there
Good suggestion, done. I also detailed the differences between the different licenses (which was already described in the License.txt file which comes with the trial version).

Nice on Koriolis, Thanks for this. Expect payment for me in the next few days, this looks great

Koriolis, just to add, I purchased it, and on full version setup, it incorrectly setup the script editor on desktop to

""C:\Program Files\BriskVM 2\ScriptEditor.exe""

when it should be:

"C:\Program Files\BriskVM 2\bin\win32\ScriptEditor.exe"

So when you launch, you get Windows trying to find the application :)

Oops. I checked the quick launch shortcut and it was OK, I forgot to check the desktop shortcut, silly me.
OK I'll fix that asap too. Thank you.

Sounds cool, if there's static version available?

I tend to buy one soon.

No static version yet, but I might do one in a future release.

this seems really cool. I havent tried this to the fullest extend but I remember coming across a few problems in script languages, wondering if they exist or not in Brisk. Better to ask then endlessly testing it out LOL

1. Is there a line limit per script? Like a max set of instructions?
2. I remember in the previous script engine i was using. Massive condition, loop statements, for loops etc will be very slow and sometimes crashes. Is there such a problem? like for ex.
<presudo code>
for i = 1 to 1000
if i = 500
for loop 1 to 3000
select value:
case 1
do
loop
case 2
end
nxt
endif
next
etc.

3. How many script can be run simulatiously? or is there a limit? lol

4. I think you can tell of what I plan to do with scripts. RPG builder with NPC, events, auto events etc. I presume it safe to say user can wrote scripts for their own events, npc for my engine? Any licensing issue about this part?

5. As for as I understand of the license, if I am an indi developer (one/two man team LOL), the independent lience is rite for me rite? commerical license is for those who have their stuff published or seomthing and sold thru publisher?

now.....back to tryin out ur demo ^^

Nack

1. No
2. Not that I'm aware of. If bugs are discovered, they will be fixed.
3. No limit other than what is sensible given the available CPU power. However you say "simunatenously", so to keep things clear I must say that there is (currenty) no proper support for concurrency. If you want to have different scripts run "simunatenously", you would do just like in standard BlitzMax/Blitz3D (such as having an "Update" function scripts, called each frame). Also in my opinion it is best here to take advantage of the OOP support: rather than creating one script per "entity", write a new type per kind of entity. It's more manageable, and even more resource friendly. But it's just an advice, you may integrate scripts as you see fit.
4. Absolutely. Giving the end user the ability to MOD your game is one the biggest intended use for BriskVM. No licensing problem here. Only the developer(s) integrating BriskVM need a license, the runtime is then free to use and redistribute. As stated in the EULA, you can actually redistribute pretty much everything except the "bvmtools" shared library (which is only needed for the developer integrating BriskVM, and unneeded for cscript compilation nor execution).

5. Exact. One point though: as the license states, established game portals are also interpreted as commercial, so if you get a deal with such a portal you should have a commercial license. If on the other hand you distribute your games via your site you certainly qualify as "indie" :)
In any case, there is an upgrade from "indie" to "commercial", so you don't have to worry about wether you'll go "commercial" or not in the future. You can switch when you want and only pay the difference.

That looks awesome. I would like to add a parser for your script language to BLIde Plus. Any chance to get a detailed explanation on the language syntax?

Congratulation Koriolis
Have been waiting for its full release after some "tinker tests" with the open beta.
It might not be of much use for my current project but for the future one and I think its definitely worth buying it even when only considering to use it :)

Ziggy: That indeed would be great :)
I am already able to use Blide for nearly anything I need (BMX, XML in most cases) and if BriskVM scripts were possible as well this would round it up to "all you could need" :)

@Ziggy: This would be really great :)
I haven't got a simple grammar to give, but the script language manual ( http://www.koriolis-fx.com/index.php?id=94 ) details the language syntax (though not with a formal BNF specification). Tell me if it's good enough for you to write the syntax highlighter/intellisens/whatever. For any remaining question we can discuss by mail, I am very wlling to help, as you can imagine :)

@Dreamora & others: Thanks for the support.

Also in my opinion it is best here to take advantage of the OOP support: rather than creating one script per "entity", write a new type per kind of entity. It's more manageable, and even more resource friendly. But it's just an advice, you may integrate scripts as you see fit.

Brisk HAVE OOP??? and here i am tryin to fake oop scripts....
Hmm, tell me more about this idea...cus the way I am doing now, as you said, each "npc/event" have its own script file. when user "approach/activite" it, the script will run and auto flush itself afterwards for memory purpose. How will OOP help? I am just tryin to grasp the idea, kinda interesting....and interested if its more manageable

Also, we often include some third party dll (fastlibs, winblitz3d etc). can your script handle it? I tried testing out some examples from fastlibs and it seems to crash. Maybe I am doing something wrong (most likely i am lol)

The blitz3d's famous "INCLUDE" command LOL. Can your script include other script and stuff? Because scripts can get big.....very fast.

Finally, the script editor. How customize can I get with it? Also, does it support unicode? Because the editor will be available to end user. By unicode, the engine will handle displaying stuff, its just the editor need to allow users to input unicode. So its just need to "show" the unicode and nothing else.


Sorry for so many questions, Its just I well into my new engine, and if I want to switch script engine it has to be now before its too intergrated. So got to see if it can do what I want it to do before purchase lol

Nack, You should really read the docs the came with the demo or view them online at http://www.koriolis-fx.com/
All your questions are answered there. There are lot of examples that explain how to bind your application with BVM and detail the power of the scripting language.

Koriolis, for what I've seen, it is very similar to BMX, so maybe the best way to go would be if I could know exactly wich syntax diferences there are and I think a way to implement them. If I could create a parser that inherits the BMX existing one, and just add the changes, this could be done very easily.

@Nack: OOP really shines when it comes to manage concrete entites, such as NPCs adn the like. Just as an example, you can have a base TEntity type, a TEnemy type derived from it, a TPlayer type, a TTroll type derived from Tenemy, end so ona, everything forming a natural hierarchy. Behaviour can then be written in terms of methods of these types. In particular, the "Update" function I was talking about would then become an "Update" *method*, and in your main loop you would loop over each entity and call their "Update" method in turn.

Also, we often include some third party dll (fastlibs, winblitz3d etc). can your script handle it?
If you can call the function from your host application, you can make it available to scripts. In Blitz3D it means that what you have in the .decls file, you can put in the command set file, and that's about it.


The blitz3d's famous "INCLUDE" command LOL. Can your script include other script and stuff?
Yes, it's supported. As GW suggested, you should really have a look at the documentation, there is a manual dedicated entirely to the script language.

How customize can I get with it?
You can add menu entries and the like. When you're ready to customize it, you can ask for precise information and help on the forum.

Also, does it support unicode?
No, the editor doesn't support it (yet). Making it support unicode should be relatively easy for me I guess, but scripts source files don't support Unicode anyway. Note however that it doesn't mean that Unicode is not supported at all: scripts strings are unicode, so you can share unicode strings with the host application. But this, too may change : I'm thinking to support UTF-8 in source files (so that you can directly type unicode strings in your scripts), the priority I give to this will depend on the demand.

oh this script is awesome. yea, sorry, i should really look at the documentation, just that I am on a business trip rite now, not much computer time...got too excited when I see this lol. I will definately purchase this when I return home next week! Expect a payment next week =]

I have been beta testing this, and it is really amazing. The first BVM was pretty good, but BVM2 supports so many new features and is so much easier to integrate with BlitzMax. I am using it in Leadwerks Engine.

I noticed an error in BriskVM Manual -> Linking with the library. The first code example, in blitzmax, says "Using koriolis.briskvm" when it should be "Import koriolis.briskvm".

..im really impressed..I may buy this..

@Ziggy: sorry, I missed your post, maybe you sent it while I was writing my own post.
I'll write a little BMX<->BriskVM language comparison soon, and send it to you by mail.
@Plash: thanks, I will fix the docs.

Great!

Trying to use the demo just through the script editor. Pretty buggy editor, guess that's why I don't use editors that come with stuff most of the time. Anyway, I wanted to try and get familiar with extended methods, so I looked at the documentation, which already confused me because I think it by itself is confused, and doesn't know what to open or close the function/method with:

' Extension method (defined outside TEntity)
Method Move(Self:TEntity, x:Int, y:Int)
    Self.x = x
    Self.y = y
End Function


So I wrote the following using Function first, assuming that End Function probably means 1) it's a function and 2) this is probably just syntactic sugar detected by the compiler/interpreter/what have you (since it doesn't say). This is wrong, it's Method. Confusing. In addition to that, the language documentation does not explain the little !flags at all (note: language, not the other thing, which should be merged so it makes sense), which lead me to believe that you hated explicitly declaring variables and momentarily caused me to shake my fist feebly at the ceiling.

Type CColor
	Field r,g,b
End Type

Method SetColor(Self:CColor, nr,ng,nb)
	Self.r = nr
	Self.g = ng
	Self.b = nb
End Method

Local c:CColor = New CColor

c.SetColor( 5, 10, 15 )

Print c.r
Print c.g
Print c.b


The editor gives me this:
Error: Script file 'C:/Users/Noel/Documents/woop.bsl', not found

I assure you, editor, this file does exist. You happen to be modifying it! O_o But fine, I can accept that, default editors hate me, fine, be that way.

So I went to the command line, and thankfully after about 40 minutes of fiddling with your bloody evil bvmcmd.exe (which, by the way, refers me to bvmcc.exe at times, which clearly does not exist) and its habit of not accepting absolute paths with -m ("C:\...\woop.bsl" isn't accepted, and apparently will only allow me to use "woop.bsl," which leads me to believe that your editor does not know how to use the compiler or your compiler does not know how to determine whether or not a path is relative).

That aside, wonderful language and I'll probably buy it. But good god man, the editor!

Note: Editing this post as I go.

Reading the documentation off your site is painful. Extremely painful.

Do you have a grammar for the language? (Just curious. Wouldn't be of any actual use.)

Pretty buggy editor
The editor is pretty much a bonus, and a work in progress. It is maybe maybe not good enough yet for using beyond simple source code edition (what I mean is that it certainly can't be seen yet as a full blwon IDE, but is already much better than using notepad, or any editor that has no syntax highlighting for BriskSL). Other than that, if you find sever bugs I'd be thankfull for you to report them on the BriskVM forum.
By the way, at one point I may open source the editor, so that if anyone feels like he could enhance it further.

which already confused me because I think it by itself is confused
In all honesty I don't think the script language manual is that bad. There are necessarily some typos left here and there, and I correct them as they are reported. Thanks to report the error about the extension method, I fixed the docs, it will be in the next release (ad is already fixed on the online help). If you have other complaints I invite you to detail how you think I can enhance it, this would very much help me.
(note: language, not the other thing, which should be merged so it makes sense)
Here I have to disagree. I separated the language manual and the library manual on purpose, because the angle is not the same, and so that you can redistribute the script language manual along with your briskvm-powered application (a modder doesn't care about the brisvm library, all he cares about is the script language).
And the "!flag" thing (that is, "command set options") are something a modder as no way to modify, it's totally in the handds of the devloper that integrates BriskVM, hence why it's explained in the BriskVM manual, not in the script language manual. That said, I alsa have included notes here and there in the language manual to warn that feature X is only available if option Y is specified in the command set.

I think your problem may be due to the fact that you specified repository directories inside the script editor. When repositories are specified, *only* scripts (or command sets, or modules) within the repository are accepted. This is for security reasons (this is a bit like in Java where classes must all be within the classpath). Your problem with bvmcmd may be the same : if you specified a repository earlier on the command line, no script outside this repository will be accepted.

Reading the documentation off your site is painful. Extremely painful.
I agree it could be more comfortable. But come on, *all* the docs comme with the trial version, so it's seriously no big deal. Thanks to report though, it confirms my own thoughts.

For the grammar: no, I have no formal grammar for the language (well I have one, but it's not the "clean grammar", as it's the grammar I actually use for the parsing and compiling, which is unnecessarily complex for mere reading).

I think your problem may be due to the fact that you specified repository directories inside the script editor.
Looking at the options for that, it appears that it is set by default to have the repository path as ".". You can't set it to nothing in the editor, and it appears your default is the bin\win32 folder. I didn't set it, so I'm blaming you for that.

One question I have for you is in regards to handling multiple script files (because, obviously, packing everything into one gigantic script is stupid). Can the compiler be set up such that if a script includes a BSL file, it searches for an existing BVM. If the BVM's timestamp is older than the BSL's timestamp (or the BVM doesn't exist), the BSL is recompiled on the spot and the new BVM is included.

That would require the BSL to exist, so it could be handled such that in the case the BSL doesn't exist, the BVM is just used as-is. (And if neither exist, the compiler shakes its head and asks if you're insane.)

The way it is now (as far as I've seen, since I haven't gone through the docs entirely), it seems that you have to have the host application check all the files and recompile them if necessary before actually loading the scripts. This isn't really convenient because it means that, as a solution, I still have to spend a lot of time including functionality that I think should be part of the compiler. It'd be like getting a vacuum cleaner, except it has no handle, so you tape a broom handle to it. Should have come with a handle, but instead you're forced into adding functionality to the vacuum in a way.

For example, BlitzMax will recompile imported source files if it has to, or just link to the objects that were already compiled (or compile them if the objects simply don't exist). What I'm more or less hoping for is something similar to this, if the above didn't make much sense.

And it looks like the end of the month timestamp-screwy just happened...

One question I have for you is in regards to handling multiple script files (because, obviously, packing everything into one gigantic script is stupid). Can the compiler be set up such that if a script includes a BSL file, it searches for an existing BVM. If the BVM's timestamp is older than the BSL's timestamp (or the BVM doesn't exist), the BSL is recompiled on the spot and the new BVM is included.

Thats just being lazy.

You could
1) just use the BVM panel to compile your scripts.
2) have your own code in your host application compile your scripts on startup.
3) Export the BVM compiler command to BVM and write a script to compile your scripts, then just call that script at runtime.

Another thing: why is there no support for 64-bit data types? This is kind of a big problem for me.

1) just use the BVM panel to compile your scripts.
I would prefer not to have my users have to compile their scripts manually.
2) have your own code in your host application compile your scripts on startup.
I'm saying there should be an option so that I don't have to do this. If it's going to look like BASIC, you may as well include that simplicity in its use as well.

No limit other than what is sensible given the available CPU power. However you say "simunatenously", so to keep things clear I must say that there is (currenty) no proper support for concurrency. If you want to have different scripts run "simunatenously", you would do just like in standard BlitzMax/Blitz3D (such as having an "Update" function scripts, called each frame).

Hmm just hit me, I want to make sure our defination of "simunatenoously" is the same. By simuatenously, I mean Multi-Threading. In my Nream Engine, one "event" constantly having 2+ script running at the same time. One mite be for movement, and another for acuatl events etc. I want to make sure Brisk can Multi-Thread. Because so far, from what I seen in the demo, both (rain, bouncy) are ran one after the other. And if one script have like "repeat; forever" loop in it, the program will run/stuck in that script forever. I read over the manual multiple times, and cant seem to find such answer/solution. I was wondering, does it support multi-thread?

@Noel/Dingbat Blump:
One question I have for you is in regards to handling multiple script files (because, obviously, packing everything into one gigantic script is stupid). Can the compiler be set up such that if a script includes a BSL file, it searches for an existing BVM. If the BVM's timestamp is older than the BSL's timestamp (or the BVM doesn't exist), the BSL is recompiled on the spot and the new BVM is included.
Actually I was already thinking about doing this. See my Public TODO list
However, please note that comparing this with BlitzMax is unfair, because as GW hinted BriskVM *already* lets you compile a whole repository recursively, just use the BriskVM control panel. So typically you would just have to run this "repository recompiling" before recompiling your application. And because this operation is also available through the command line front end, you can even integrate this as a thrid party tool in most editors.
As for the BriskVM script Editor, I'm going to add a menu entry for this operation (I just added this to my TODO list)

I'm saying there should be an option so that I don't have to do this. If it's going to look like BASIC, you may as well include that simplicity in its use as well.
I actually agree, entirely. This is why it's in my TODO-list.

@Nack:
There is no proper support for multithreading yet. This is mostly because I'm still wondering what is the best in terms of API for this, rather than hard technical problems.
Indeed, BriskVM already supports the bases for multithreading: you can schedule the timed execution of a function (I call this "tickers", you may know this better as "timers").
In addition, you can also execute scripts with a timeout, so with short timeouts you can simulate multithreading.
However this is not very user firendly, hence why I certainly don't consider it a true feature I can bragg about.

So, in the current state of BriskVM, the best thing to do is probably to do just like in BlitzMax of BlitzBasic, as I said.

However, since I'm very interested in adding some form of concurrency, I'd really like to discuss with you (and others) about what would be the best API for this.
----------------------------------------------------
(...going into the details...)
As I hinted in my public TODO-list, I'm thinking about a dual solution:
- concurrency at the level of a whole execution context, that is the host could say something like "these three contexts", I'd like you to run them concurrently. This part is technically pretty easy for me as I already have everything to handle the timeouts. If I can settle on the best API quickly, this can really be added very soon (no kidding). An additional related question is: should I provide some kind of mutex? I tend to think it's less of an issue in our case, as scripts will never run in parallel with the host (even if they run in parallel with one another), so a mutex is less of a need tha for say a C++ application.
- a more fine grained concurrency, probably synchronous (most people would not call this "true" concurrency, but this is still really usefull). I'm still very unsure about this one, and it will probably take quite some tries and discussions with BriskVM users before I come up with something I'm pleased with (or before I toss it entirely).

I'll stop here with this, as I'm going to frighten potential users who may be made to believe that BriskVM is awfuly complex (when I'm merely digressing).
If you want to discuss about the issue (and possibly have an influence on the final API for concurrency), you can come on the forum and give me your view on this.

Ziggy, I sent the BriskVM-BlitzMax syntax comparison that you asked for.

Bought after fiddling around with it most of the night. Obviously there are some weak points, but I can still make use of it, and I think the language features make up for a lot.

Definitely. Its far simpler to integrate than anything I tried given the features it brings in with OO and the like. And the IDE + especially debugger are definitely features that you won't find that often on other script languages.

Wrote a program to take my source code, read off various special comments within it, and generate the command set for the code afterward. Rather happy with how that turned out. Smooth sailing so far.

If anyone is interested in using such a thing, I'll see about modifying it to not rely on my speshul code and post it somewhere.

Dingbat - sounds useful.

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!

He he, this is great. Thanks to both of you to share this.

You did close to the exact same thing as me. Creepy.

Latest version of my BCS thingamajigger. The code is terrible, I won't deny that, but then it wasn't really intended for use by others:
Strict

Import "parse.bmx"

Global elements:TMap = New TMap
Global aliases:TMap = New TMap
Global doc:IPNode
Global docStream:TBankStream = TBankStream.Create(TBank.Create(0))

Local setName$
If AppArgs.Length = 1 Then
    setName = "exposed"
Else
    setName = StripExt(AppArgs[1])
EndIf

Local output:TStream = OpenFile( setName+".bcs", 0, 1 )

Local otype:IType = New IType
        otype.Name("Object")
        otype.ExposeAs("HostObject")
        otype.IsAbstract(True)

        Local meth:IMethod = New IMethod
            meth.Name("ToString")
            meth.TypeName("$")
            meth._fromType = True
        otype.Fields().Insert( meth.Name().ToLower(), meth )

        meth = New IMethod
            meth.Name("Compare")
            meth.TypeName("%")
            meth._fromType = True
            Local v:IVariable = New IVariable
                v.Name("other")
                v.TypeName("Object")
            meth.Arguments().AddLast( v )
        otype.Fields().Insert( meth.Name().ToLower(), meth )

        otype.Exposed( True, True )

output.WriteString( ".set ~q"+setName+"~q~n~n"+..
                    "!altsyntax~n"+..
                    "!classes~n"+..
                    "!explicit~n"+..
                    "!funcpointers~n"+..
                    "!properties~n"+..
                    "!scoping~n"+..
                    "!serialization~n"+..
                    "!reflection~n~n" )

Local docDir$
If AppArgs.Length >= 3 Then
    docDir = AppArgs[2]
Else
    docDir = StripSlash(AppDir)+"/"
EndIf
Global docName$
For docName = EachIn LoadDir(docDir)
    If docName[0..1] = "." Or ExtractExt( docName ).ToLower() <> "bmx" Then
        Continue
    EndIf

    ReadExpose( docDir+docName )

    GCCollect()
Next

doc = LoadParseDoc( docStream )
ProcessDocument( doc )
WriteBCSData( output )

docStream.Close()
docStream = Null
output.Close()

Function ReadExpose:IPNode( docName$ )
    Local stream:TStream = ReadFile( docName )
    Local l$

    If stream = Null Then
        Return Null
    EndIf

    While Not stream.Eof()
        l = stream.ReadLine()

        If l.Trim().ToLower() <> "'expose" Then
            Continue
        EndIf

        While Not stream.Eof()
            l = stream.ReadLine()

            If l.Trim().ToLower() = "'endexpose" Then
                Exit
            EndIf

            docStream.WriteLine(l)
        Wend
    Wend

    stream.Seek(0)
    ProcessSource( stream )

    stream.Close()
    stream = Null

End Function

' Rem

Function StripComment$( s$ )
    Local sopen% = 0
    Local c% = -1
    Local nw$

    For Local i% = 0 To s.Length-1
        If sopen = 0 And s[i] = 39 Then
            c = i
            Exit
        ElseIf s[i] = 34 Then
            sopen = Not sopen
        EndIf
    Next
    If c > -1 Then
        Return s[0..c]
    Else
        Return s
    EndIf
End Function

Function WriteBCSData( output:TStream )
    For Local i:IType = EachIn elements.Values()
        If i.Exposed() Then i.WriteData( output )
    Next

    Local c% = 0
    For Local i:IMethod = EachIn elements.Values()
        If i.Exposed() Then i.WriteData( output ) c = 1
    Next

    If c Then output.WriteString("~n")
    c = 0
    For Local i:IVariable = EachIn elements.Values()
        If i.Exposed() Then i.WriteData( output ) c = 1
    Next

    If c Then output.WriteString("~n")
    c = 0
    For Local i:IEntryPoint = EachIn elements.Values()
        i.WriteData( output )
        c = 1
    Next
    If c Then output.WriteString("~n")
End Function

Function ProcessDocument( doc:IPNode )
    Global ecount% = 0
    For Local attr:IPAttr = EachIn doc.Attrs
        Select attr.Name.ToLower()
            Case "exposeall"
                For Local i:IExposable = EachIn elements.Values()
                    i.Exposed(True, True)
                Next
            Case "hideall"
                For Local i:IExposable = EachIn elements.Values()
                    i.Exposed(False, True)
                Next
            Case "hidemethods"
                For Local i:IMethod = EachIn elements.Values()
                    i.Exposed(False)
                Next
            Case "hidetypes"
                For Local i:IType = EachIn elements.Values()
                    i.Exposed(False, True)
                Next
            Case "hideglobals"
                For Local i:IVariable = EachIn elements.Values()
                    i.Exposed(False)
                Next
            Case "exposemethods"
                For Local i:IMethod = EachIn elements.Values()
                    i.Exposed(True)
                Next
            Case "exposetypes"
                For Local i:IType = EachIn elements.Values()
                    i.Exposed(True, True)
                Next
            Case "exposeglobals"
                For Local i:IVariable = EachIn elements.Values()
                    i.Exposed(True)
                Next
            Case "expose"
                Local obj:IExposable = IExposable(elements.ValueForKey( attr.Content.ToLower() ))
                If obj Then
                    obj.Exposed( True )
                Else
                    Print "Exposable ["+attr.Content+"] does not exist : "+docName
                EndIf
            Case "hide"
                Local obj:IExposable = IExposable(elements.ValueForKey( attr.Content.ToLower() ))
                If obj Then
                    obj.Exposed( False )
                Else
                    Print "Exposable ["+attr.Content+"] does not exist : "+docName
                EndIf
            Case "entry"
                Local ent:IEntryPoint = New IEntryPoint
                ent.Entry( attr.Content )
                elements.Insert( "entrypoint:"+ecount, ent )
                ecount :+ 1
            Default
        End Select
    Next

    For Local node:IPNode = EachIn doc.Children
        If node.Name.ToLower() = "type" Then
            Local attr:IPAttr = node.FindAttr("name")
            Local t:IType = IType(elements.ValueForKey(attr.Content.ToLower()))
            If Not t Then Print "Exposable ["+attr.Content+"] does not exist : "+docName Continue
            t.Exposed(True)
            For Local attr:IPAttr = EachIn node.Attrs
                Select attr.Name.ToLower()
                    Case "as"
                        t.ExposeAs( attr.Content )
                    Case "expose"
                        If attr.Content = "" Then
                            t.Exposed(True)
                        Else
                            Local obj:IExposable = IExposable(t.Fields().ValueForKey(attr.Content.ToLower()))
                            If obj Then obj.Exposed(True) Else Print "Exposable ["+attr.Content+"] does not exist : "+docName
                        EndIf
                    Case "hide"
                        If attr.Content = "" Then
                            t.Exposed(False)
                        Else
                            Local obj:IExposable = IExposable(t.Fields().ValueForKey(attr.Content.ToLower()))
                            If obj Then obj.Exposed(False) Else Print "Exposable ["+attr.Content+"] does not exist : "+docName
                        EndIf
                    Case "exposeall"
                        For Local i:IExposable = EachIn t.Fields().Values()
                            i.Exposed(True)
                        Next
                    Case "exposefields"
                        For Local i:IVariable = EachIn t.Fields().Values()
                            i.Exposed(True)
                        Next
                    Case "exposemethods"
                        For Local i:IMethod = EachIn t.Fields().Values()
                            i.Exposed(True)
                        Next
                    Case "hideall"
                        For Local i:IExposable = EachIn t.Fields().Values()
                            i.Exposed(False)
                        Next
                    Case "hidefields"
                        For Local i:IVariable = EachIn t.Fields().Values()
                            i.Exposed(False)
                        Next
                    Case "hidemethods"
                        For Local i:IMethod = EachIn elements.Values()
                            i.Exposed(False)
                        Next
                End Select
            Next
        ElseIf node.Name.ToLower() = "func"
            Local attr:IPAttr = node.FindAttr("name")
            Local obj:IMethod = IMethod(elements.ValueForKey(attr.Content.ToLower()))
            If Not obj Then Print "Exposable ["+attr.Content+"] does not exist : "+docName Continue
            obj.Exposed(True)
            For Local attr:IPAttr = EachIn node.Attrs
                Select attr.Name.ToLower()
                    Case "expose"
                        obj.Exposed(True)
                    Case "hide"
                        obj.Exposed(False)
                    Case "as"
                        obj.ExposeAs( attr.Content )
                End Select
            Next
        ElseIf node.Name.ToLower() = "alias" Then
            Local orig$ = node.GetAttr("name")
            Local as$ = node.GetAttr("as")
            If orig.Length = 0 Or as.Length = 0 Then Continue
            aliases.Insert( orig.ToLower(), as )
        EndIf
    Next
End Function

Function ProcessSource( source:TStream )
'    DebugStop
    Local l$, la$[]
    While Not source.Eof()
        l = ReadSourceLine(source)

        If l = Null Or l.Length = 0 Then
            Continue
        EndIf

        la = l.Split(" ")

        If (Not la) Or la.Length = 0 Then
            Continue
        EndIf

        If la[0].Trim().ToLower() = "type" Then
            Local _type:IType = ProcessType( l, source )
            If _type Then
                elements.Insert( _type.Name().ToLower(), _type )
            EndIf
        ElseIf la[0].Trim().ToLower() = "global" Then
            Local _variable:IVariable = ProcessVariable( l, False, elements )
        ElseIf la[0].Trim().ToLower() = "function" Or la[0].Trim().ToLower() = "method" Then
            Local _method:IMethod = ProcessMethod( l, False, elements )
        EndIf
    Wend
End Function

Function ReadWord$( s$, from% Var )
    If from >= s.Length-1 Then
        Return Null
    EndIf
    Local sopen = False

    Local nw$
	Local i:Int
    For i = from To s.Length-1
        Local c$ = s[i..i+1]
        If sopen = 0 And c = "'" Then
            from = s.Length-1
            Exit
        ElseIf c = "~q" Then
            sopen = Not sopen
        ElseIf sopen = 0 And "~t ;[]{}?():\/%~~',.<>!@...) > -1 Then
            If nw <> "" Then
                from = i
                Exit
            EndIf
        ElseIf sopen = 0 Then
            nw = nw + c
        EndIf
    Next

	from = i

    If nw = "" Then
        from = s.Length-1
        Return Null
    EndIf

    Return nw
End Function

Global remopen% = False
Function ReadSourceLine$( source:TStream )
    Local sopen% = 0, nw$ = "",from%=0, l$

    If remopen Then
        l = StripComment(ReadUntilEndRem(source)).Trim()
        remopen = False
    Else
        l = StripComment(source.ReadLine()).Trim()
    EndIf

    While l[l.Length-2..] = ".." And Not source.Eof()
        Local lb$ = StripComment(source.ReadLine()).Trim()
        l = l[..l.Length-2] + " " + lb
    Wend

    nw = ReadWord( l, from )
    While nw
        If nw.ToLower() = "rem" Then
            remopen = True
            l = l[..from-3].Trim()
        ElseIf nw.ToLower() = "endrem" Then
            l = l[from..].Trim()
            remopen = False
        EndIf

        nw = ReadWord( l, from )
    Wend

    Return l
End Function

' I probably don't need this, but what the hey
Function ReadUntilEndRem$( source:TStream )
    While Not source.Eof()
        Local l$ = source.ReadLine().Trim()
        Local f% = l.ToLower().Find("endrem")
        If f > -1 Then
            remopen = False
            l = l[f+6..]
            Return l
        EndIf
    Wend
End Function

Function ProcessType:IType( line$, source:TStream )
    Local t:IType = New IType
    
    Local from% = 0

    ReadWord( line, from )
    Local name$ = ReadWord(line, from)
    Local extend$ = ReadWord(line, from)
    Local extender$ = ReadWord(line, from)
    Local ab$ = ReadWord(line, from)

    t.Name( name )
    
    If extend.ToLower() = "extends" Then
        t.ExtendsFrom(extender)
    ElseIf extend.ToLower() = "abstract" Then
        t.IsAbstract(True)
    EndIf
    If ab.ToLower() = "abstract" Then
        t.IsAbstract(True)
    EndIf

    Local fields:TMap = t.Fields()
    Local l$, la$[]
    While Not source.Eof()
        from = 0
        l = ReadSourceLine(source)

        If l.Length = 0 Then Continue

        If l.ToLower().Find("end type") > -1 Or l.ToLower().Find("endtype") > -1 And l.ToLower().Find("~qend type") = -1 And l.ToLower().Find("~qendtype") = -1 Then
            Exit
        EndIf

        la = l.Split(" ")

        If (Not la) Or la.Length = 0 Then
            Continue
        EndIf

        If la[0].Trim().ToLower() = "function" Or la[0].Trim().ToLower() = "method" Then
            ProcessMethod( l, True, fields )
        ElseIf la[0].Trim().ToLower() = "global" Or la[0].Trim().ToLower() = "field" Then
            ProcessVariable( l, True, fields )
        EndIf
    Wend
    Return t
End Function

Function ProcessMethod:IMethod( line$, typeChild%, addmap:TMap )
    Local m:IMethod = New IMethod
    Local from%=0
    Local scope$ = ReadWord(line, from).ToLower()
    If scope = "function" Then
        m.IsGlobal( True )
    Else
        m.IsGlobal( False )
    EndIf
    Local name$ = ReadWord(line, from)
    m.Name name
    m._fromType = typeChild

    m.TypeName line[from..line.Find("(")]
    Local args$ = line[line.Find("(")+1..line.Find(")")]

    For Local i$ = EachIn args.Split(",")
        i = i.Trim()
        If "1234567890!@...]) > -1 Or i.Length = 0 Then
            Continue
        EndIf
        If i.Find("=") > -1 Then i = i[..i.Find("=")]
        from = 0
        Local aname$ = ReadWord( i, from )
        Local atype$ = i[from..].Replace(":","")
        Local v:IVariable = New IVariable
        v.Name( aname )
        v.TypeName( atype )
        m.Arguments().AddLast( v )
    Next

    If addmap Then addmap.Insert( name.ToLower(), m )

    Return m
End Function

Function ProcessVariable:IVariable( line$, _fromType%, addmap:TMap=Null, addlist:TList=Null )
    Local v:IVariable
    Local from%=0, scope%, name$

    scope% = ReadWord( line, from ).ToLower()="global"

    Local vars$[] = line[from..].Trim().Split(",")
    For Local i$ = EachIn vars
        If i.Find("=") > -1 Then i = i[..i.Find("=")-1]
        from = 0
        Local name$ = ReadWord( i, from )
        If name = "" Or "1234567890!@... Then Continue
        Local vt$ = i[from..].Replace(":","")
        v = New IVariable
        v.IsGlobal( scope )
        v.Name( name )
        v.TypeName( vt )
        v._fromType = _fromType
        If addmap Then
            addmap.Insert( v.Name().ToLower(), v )
        EndIf
        
        If addlist Then
            addlist.AddLast( v )
        EndIf
    Next

    Return v
End Function

Type IExposable
    Field _name$
    Field _expose

    Method Name$( n$=Null )
        If n = Null Then
            Return _name
        EndIf
        _name = n
        Return n
    End Method

    Method Exposed%(ex%=-1, recurse%=0)
        If ex = -1 Then Return _expose
        _expose = ex
        Return ex
    End Method

    Method ExposeAs( a$=Null )
        If a = Null Then
            aliases.Remove(_name.ToLower())
        Else
            aliases.Insert(_name.ToLower(), a)
        EndIf
    End Method

    Method ExposedAs$()
        Return String(aliases.ValueForKey(_name.ToLower()))
    End Method

    Method WriteData( out:TStream ) Abstract
End Type

Type IEntryPoint Extends IExposable
    Field _entry$

    Method Exposed(ex%=-1, recurse%=0)
        Return True
    End Method
    
    Method WriteData( out:TStream )
        out.WriteString( "EntryPoint "+_entry+"~n" )
    End Method

    Method Entry$( en$=Null )
        If en = Null Then Return _entry
        _entry = en
        Return en
    End Method
End Type

Type IVariable Extends IExposable
    Field _global%
    Field _type$
    Field _mode$=""
    Field _fromType% = 0

    Method IsGlobal( g% = -1 )
        If g = -1 Then Return _global
        _global = g
        Return g
    End Method

    Method TypeName$( n$ = Null )
        If n = Null And _type Then
            Local t$ = String(aliases.ValueForKey(_type.ToLower()))
            If t Then Return t+_mode
            Return _type+_mode
        EndIf
        If n = Null And _type = Null Then Return "Int"
        _type = n
        If _type.Find(";")>-1 Then _type = _type[.._type.Find(";")]
        If _type.Find("{")>-1 Then _type = _type[.._type.Find("{")]
        _type = _type.Replace("@@","Short").Replace("@...()
        If _type="" Or _type = Null Then Return "Int"
        If _type.ToLower().Find(" var")>-1 Then
            _mode = " Var"
            _type.Replace(" Var","")
        EndIf
        Return n
    End Method

    Method ToString:String()
        Return "Variable "+Name()+" > global:"+IsGlobal()+"  type:"+TypeName()
    End Method

    Method WriteData( out:TStream )
        If _fromType Then out.WriteString("    ")
        If IsGlobal() Then
            out.WriteString( "Global "+Name()+":"+TypeName()+"~n" )
        Else
            out.WriteString( "Field "+Name()+":"+TypeName()+"~n" )
        EndIf
    End Method
End Type

Type IMethod Extends IExposable
    Field _global%
    Field _type$
    Field _fromType%
    Field _mode$
    Field _arguments:TList = New TList  ' Container: IVariable

    Method IsGlobal( g% = -1 )
        If g = -1 Then Return _global
        _global = g
        Return g
    End Method

    Method TypeName$( n$ = Null )
        If n = Null And _type Then
            Local t$ = String(aliases.ValueForKey(_type.ToLower()))
            If t Then Return t+_mode
            Return _type+_mode
        EndIf
        If n = Null And _type = Null Then Return "Int"
        _type = n
        If _type.Find(";")>-1 Then _type = _type[.._type.Find(";")]
        If _type.Find("{")>-1 Then _type = _type[.._type.Find("{")]
        _type = _type.Replace("@@","Short").Replace("@...()
        If _type="" Or _type = Null Then Return "Int"
        If _type.Find(" Var")>-1 Then
            _mode = " Var"
            _type.Replace(" Var","")
        EndIf
        Return n
    End Method

    Method Arguments:TList()
        Return _arguments
    End Method

    Method ToString:String()
        Local s$ = "Method "+Name()+" > global:"+IsGlobal()+"  type:"+TypeName()+"  args{ "
        Local c%=0
        For Local i:IVariable = EachIn _arguments
            s :+ i.Name()+" : "+i.TypeName()+i._mode+", "
            c=1
        Next
        If c Then s = s[..s.Length-2]
        s:+"}"
        Return s
    End Method

    Method WriteData( out:TStream )
        If TypeName().Find("[") > -1 Or TypeName().Find("]") > -1 Then Return
        Local s$ = ":"+TypeName()+"("
        If ExposedAs() Then
            s = ExposedAs()+"<"+Name()+">"+s
        Else
            s = Name()+s
        EndIf
        Local c% = 0
        For Local i:IVariable = EachIn Arguments()
            c = 1
            If i.TypeName().Find("[") > -1 Or i.TypeName().Find("]") > -1 Then Return
            s :+ " "+i.Name()+":"+i.TypeName()+","
        Next
        If c Then s = s[..s.Length-1]
        s = s + " )"
        If IsGlobal() Then
            s = "Function "+s
        Else
            s = "Method "+s
        EndIf
        If _fromType Then
            s = "    "+s
        EndIf
        out.WriteString( s+"~n" )
    End Method
End Type

Type IType Extends IExposable
    Field _fields:TMap ' Container: IVariable, IMethod
    Field _extends$ = Null
    Field _abstract%=0

    Method New()
        _fields = New TMap
    End Method

    Method Fields:TMap()
        Return _fields
    End Method
    
    Method ExtendsFrom$(ext$=Null)
        If ext = Null Then Return _extends
        _extends = ext
        Return ext
    End Method

    Method IsAbstract%(ab%=-1)
        If ab = -1 Then Return _abstract
        _abstract = ab
        Return ab
    End Method

    Method ToString:String()
        Local r$ = "Type "+Name()+"~n{~n"
        For Local i:Object = EachIn _fields.Values()
            r :+ "    "+i.ToString()+"~n"
        Next
        r :+ "}"
        Return r.Trim()
    End Method

    Method WriteData( out:TStream )
        Local s$
        If ExposedAs() Then
            s = "Type "+ExposedAs()+"<"+Name()+">"
        Else
            s = "Type "+Name()
        EndIf
        If IsAbstract() Then
            s = "Abstract "+s
        EndIf
        If ExtendsFrom() <> Null Then
            s = s + " Extends "+ExtendsFrom()
        ElseIf Name().ToLower() <> "object" Then
            s = s + " Extends "
            Local sob$ = String(aliases.ValueForKey("object"))
            If sob = Null Then sob = "Object"
            s :+ sob
        EndIf
        out.WriteString( s + "~n" )

        For Local i:IVariable = EachIn _fields.Values()
            If i.Exposed() Then i.WriteData( out )
        Next

        For Local i:IMethod = EachIn _fields.Values()
            If i.Exposed() Then i.WriteData( out )
        Next

        out.WriteString( "End Type~n~n" )
    End Method

    Method Exposed%( ex%=-1, recurse%=0 )
        If ex = -1 Then
            Return Super.Exposed( ex, recurse )
        EndIf

        If recurse Then
            For Local i:IExposable = EachIn Fields().Values()
                i.Exposed( ex, recurse )
            Next
        EndIf

        Return Super.Exposed( ex, recurse )
    End Method
End Type


The way it works is that inside of the code, you place a comment block like this:
Rem
'expose

expose Identifier
hide Identifier
# ^ be it a type, function, or global variable- methods in types have to be handled via type{..}

exposeAll!
hideAll!
exposeTypes!
exposeFunctions!
exposeGlobals!
#expose*! and hide*! attributes will expose/hide all of whatever across /all/ files, not the active one- I was too lazy to fix this, since I do not actually use these and they were mostly added for my own amusement
# there are equivalent hide*s for the above as well.


alias {
    name Identifier
    as NewName
}

func {
    name Identifier # required
    expose!  # unless specified otherwise, it is assumed that the function is to be exposed
    hide!
    as NewName # alias- synonym for the above alias entry
}

type {
    name Identifier
    as newName
    expose!
    hide! # <- or this
    exposeMethods!
    exposeFields!
    exposeAll! # also hide* equivalents
    expose Identifier # can be a method or field
    hide Identifier #can be a method or field
}

'endexpose
EndRem

You can have as many blocks of those in a file as you want, although it probably makes the most sense to stick to one and keep its contents relevant to the file (not that this is enforced, just a good idea).

You will need to have SParse: http://www.blitzbasic.com/codearcs/codearcs.php?code=2217

Frankly, I'm probably going to swap this out for fredborg's now.

Edit: And mine has no support for arrays, since I'm not sure that they're accepted as arguments to host functions (or script functions yet, even- haven't tried it yet, guess I will).
Edit 2: And consider this public domain, obviously.

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