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:
The way it works is that inside of the code, you place a comment block like this:
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.
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.