BriskVM 2 released

Miscellaneous Forums/Blitz Showcase/BriskVM 2 released

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.