Tinkering about

for anyone watching something I’ve been tinkering with

a converter that uses a full grammar for Xojo (that I’ve by hand verified & verified by testing it without about 500 actual code examples from Xojo)

it took this code (in a plain text Xojo text project file)

#tag Class
Protected Class CComputedProperty
Inherits CProperty
#tag Method, Flags = &h0
Sub Constructor(declLine as string, stream as ProperTextInputStream)
// oddly Xojo write a computed wit hte property def LAST not first
// like
// #tag ComputedProperty, Flags = &h0
//   #tag Setter
//   Set
//     // setter
//   End Set
// #tag EndSetter
// SetOnly19 As Integer
// #tag EndComputedProperty
Dim mSourceLines() as string

	  Try
	    ParseDeclaration(declLine)
	    
	    Dim inNote As Boolean
	    
	    Do
	      Dim line As String = stream.readline()
	      
	      If line.Matches("^\s*#tag\s+EndComputedProperty") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+Getter") = StringExtensions.MatchPositions.AtStart Then 
	        ReadGetter(line, stream)
	      Elseif line.Matches("^\s*#tag\s+Setter") = StringExtensions.MatchPositions.AtStart Then 
	        ReadSetter(line, stream)
	      Elseif line.Trim = "#tag Note" Then
	        // properties can contain "notes"
	        inNote = True
	        Continue
	      Elseif line.trim = "#tag EndNote" Then
	        inNote = False
	      Else
	        mSourceLines.Append line
	      End If
	      
	    Loop Until stream.EndOfFile
	    
	    While mSourceLines(0).Trim = ""
	      mSourceLines.RemoveAt 0
	    Wend
	    
	    debug.Assert mSourceLines.Count = 1, CurrentMethodName + " got incorrect # of lines for event definition"
	    
	    Dim scope As String
	    Dim isShared As Boolean
	    Dim propName As String
	    Dim type As String
	    Dim optionalDefault As String
	    
	    If LanguageUtils.CrackPropertyDeclaration(mSourceLines(0), scope, isshared, propName, type, optionalDefault) = False Then
	      Break
	    End If
	    
	    Self.mScope = scope
	    Self.mIsShared = isshared
	    Self.Name = propName
	    Self.mType = type
	    Self.mOptionalDefault = optionalDefault
	    
	  Catch
	    Break
	  End Try
	  
	End Sub
#tag EndMethod

#tag Method, Flags = &h1
	Protected Sub ParseDeclaration(declLine as string)
	  // #tag ComputedProperty, Flags = &h0, CompatibilityFlags = (TargetConsole and (Target32Bit or Target64Bit)) or  (TargetDesktop and (Target32Bit or Target64Bit)) or  (TargetIOS and (Target64Bit))
	  
	  Try
	    Dim tokens() As String = LanguageUtils.TokenizeLine(declLine, False)
	    
	    If tokens.Ubound < 0 Then
	      Return
	    End If
	    
	    If tokens(0) <> "#tag" Then
	      Break
	    End If
	    tokens.RemoveAt 0
	    
	    If tokens(0) <> "ComputedProperty" Then
	      Break
	    End If
	    tokens.RemoveAt 0
	    
	    If tokens.count = 0 Then
	      Return
	    End If
	    
	    If tokens(0) <> "," Then
	      Break
	    End If
	    tokens.RemoveAt 0
	    
	    If tokens.Count <= 0 Then
	      Return
	    End If
	    
	    ParseFlags(tokens)
	    ParseCompatFlags(tokens)
	    ParseDescription(tokens)
	    
	    
	  Catch
	    Break
	  End Try
	  
	  
	End Sub
#tag EndMethod

#tag Method, Flags = &h1
	Protected Sub ParseGetterDeclaration(getterDecl as string)
	  
	  // there actually isnt anything to do here !
	  // Break
	End Sub
#tag EndMethod

#tag Method, Flags = &h1
	Protected Sub ParseSetterDeclaration(setterDecl as string)
	  
	  // there actually isnt anything to do here !
	  // Break
	End Sub
#tag EndMethod

#tag Method, Flags = &h1
	Protected Sub ReadGetter(declLine as string, stream as ProperTextInputStream)
	  Try
	    ParseGetterDeclaration(declLine)
	    
	    Do
	      Dim line As String = stream.readline()
	      
	      If line.Matches("^\s*#tag\s+EndComputedProperty") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+EndGetter") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+Setter") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+EndSetter") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Else
	        mGetterLines.Append line
	      End If
	      
	    Loop Until stream.EndOfFile
	    
	  Catch
	    Break
	  End Try
	  
	End Sub
#tag EndMethod

#tag Method, Flags = &h1
	Protected Sub ReadSetter(declLine as string, stream as ProperTextInputStream)
	  Try
	    ParseGetterDeclaration(declLine)
	    
	    Do
	      Dim line As String = stream.readline()
	      
	      If line.Matches("^\s*#tag\s+EndComputedProperty") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+EndSetter") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+Getter") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Elseif line.Matches("^\s*#tag\s+EndGetter") = StringExtensions.MatchPositions.AtStart Then 
	        Exit
	      Else
	        mSetterLines.Append line
	      End If
	      
	    Loop Until stream.EndOfFile
	    
	  Catch
	    Break
	  End Try
	  
	End Sub
#tag EndMethod

#tag Method, Flags = &h1
	Protected Sub WriteTo(tos as ProperTextOutputStream)
	  Dim parts() As String
	  parts.Append mScope
	  If mIsShared Then
	    parts.Append "Shared"
	  End If
	  
	  parts.append "ComputedProperty"
	  
	  parts.append mName
	  
	  If mType <> "" Then
	    parts.append "As " + mType
	  End If
	  
	  tos.writeline Join(parts," ")
	  
	  tos.Indent 
	  
	  If mGetterLines.Count > 0 Then
	    tos.Writeline "Get() As " + mType
	    tos.indent
	    For Each line As String In mGetterLines
	      tos.Writeline line
	    Next
	    tos.Outdent
	    tos.Writeline "End Get"
	  End If
	  If mSetterLines.Count > 0 Then
	    tos.Writeline "Set(value As " + mType + ")"
	    tos.indent
	    
	    For Each line As String In mSetterLines
	      tos.Writeline line
	    Next
	    tos.Outdent
	    tos.Writeline "End Set"
	  End If
	  
	  tos.Outdent
	  
	  tos.Writeline "End ComputedProperty"
	  
	End Sub
#tag EndMethod


#tag Property, Flags = &h1
	Protected mGetterLines() As string
#tag EndProperty

#tag Property, Flags = &h1
	Protected mSetterLines() As string
#tag EndProperty


#tag ViewBehavior
	#tag ViewProperty
		Name="Type"
		Visible=false
		Group="Behavior"
		InitialValue=""
		Type="String"
		EditorType="MultiLineEditor"
	#tag EndViewProperty
	#tag ViewProperty
		Name="ID"
		Visible=false
		Group="Behavior"
		InitialValue=""
		Type="Uint64"
		EditorType=""
	#tag EndViewProperty
	#tag ViewProperty
		Name="Name"
		Visible=true
		Group="ID"
		InitialValue=""
		Type="String"
		EditorType=""
	#tag EndViewProperty
	#tag ViewProperty
		Name="Index"
		Visible=true
		Group="ID"
		InitialValue="-2147483648"
		Type="Integer"
		EditorType=""
	#tag EndViewProperty
	#tag ViewProperty
		Name="Super"
		Visible=true
		Group="ID"
		InitialValue=""
		Type="String"
		EditorType=""
	#tag EndViewProperty
	#tag ViewProperty
		Name="Left"
		Visible=true
		Group="Position"
		InitialValue="0"
		Type="Integer"
		EditorType=""
	#tag EndViewProperty
	#tag ViewProperty
		Name="Top"
		Visible=true
		Group="Position"
		InitialValue="0"
		Type="Integer"
		EditorType=""
	#tag EndViewProperty
#tag EndViewBehavior

End Class
#tag EndClass

and converted it to

protected class CComputedProperty : CProperty
{
public void Constructor(string declLine, ProperTextInputStream stream)
{
string
 mSourceLines;
try
{
bool inNote;
do
{
string line = stream.readline();
if (line.Matches(“^\s*#tag\s+EndComputedProperty”)=StringExtensions.MatchPositions.AtStart)
{
return;
}
else if (line.Matches(“^\s*#tag\s+Getter”)=StringExtensions.MatchPositions.AtStart)
{
}
else if (line.Matches(“^\s*#tag\s+Setter”)=StringExtensions.MatchPositions.AtStart)
{
}
else if (line.Trim = “#tag Note”)
{
inNote = true;
continue;
}
else if (line.trim = “#tag EndNote”)
{
inNote = false;
}
else
{
}
} while (!(stream.EndOfFile));
while (mSourceLines(0).Trim=“”)
{
}
string scope;
bool isShared;
string propName;
string type;
string optionalDefault;
if (LanguageUtils.CrackPropertyDeclaration(mSourceLines(0),scope,isshared,propName,type,optionalDefault)=False)
{
System.Diagnostics.Debugger.Break();
}
Self.mScope = scope;
Self.mIsShared = isshared;
Self.Name = propName;
Self.mType = type;
Self.mOptionalDefault = optionalDefault;
}
catch
{
System.Diagnostics.Debugger.Break();
}
}

protected void ParseDeclaration(string declLine)
{
    try
    {
        string[] tokens = LanguageUtils.TokenizeLine(declLine,False);
        if (tokens.Ubound < 0)
        {
            return;
        }
        if (tokens(0)<>"#tag")
        {
            System.Diagnostics.Debugger.Break();
        }
        if (tokens(0)<>"ComputedProperty")
        {
            System.Diagnostics.Debugger.Break();
        }
        if (tokens.count = 0)
        {
            return;
        }
        if (tokens(0)<>",")
        {
            System.Diagnostics.Debugger.Break();
        }
        if (tokens.Count <= 0)
        {
            return;
        }
    }
    catch
    {
        System.Diagnostics.Debugger.Break();
    }
}

protected void ParseGetterDeclaration(string getterDecl)
{
}

protected void ParseSetterDeclaration(string setterDecl)
{
}

protected void ReadGetter(string declLine, ProperTextInputStream stream)
{
    try
    {
        do
        {
            string line = stream.readline();
            if (line.Matches("^\s*#tag\s+EndComputedProperty")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else if (line.Matches("^\s*#tag\s+EndGetter")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else if (line.Matches("^\s*#tag\s+Setter")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else if (line.Matches("^\s*#tag\s+EndSetter")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else
            {
            }
        } while (!(stream.EndOfFile));
    }
    catch
    {
        System.Diagnostics.Debugger.Break();
    }
}

protected void ReadSetter(string declLine, ProperTextInputStream stream)
{
    try
    {
        do
        {
            string line = stream.readline();
            if (line.Matches("^\s*#tag\s+EndComputedProperty")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else if (line.Matches("^\s*#tag\s+EndSetter")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else if (line.Matches("^\s*#tag\s+Getter")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else if (line.Matches("^\s*#tag\s+EndGetter")=StringExtensions.MatchPositions.AtStart)
            {
                return;
            }
            else
            {
            }
        } while (!(stream.EndOfFile));
    }
    catch
    {
        System.Diagnostics.Debugger.Break();
    }
}

protected void WriteTo(ProperTextOutputStream tos)
{
    string[] parts;
    if (mIsShared)
    {
    }
    if (mType != "")
    {
    }
    if (mGetterLines.Count > 0)
    {
        foreach (string line in mGetterLines)
        {
        }
    }
    if (mSetterLines.Count > 0)
    {
        foreach (string line in mSetterLines)
        {
        }
    }
}

}

Oh and yes its plausible to have this converter emit other languages like Swift, Java, C++ etc

When/if I get further along with it I’ll see about making an entire repo with it

What DOESNT exist ?

  • conversion of UI
  • emitting a VS Solution for use in VS, Rider, VS Mac etc

Conversion of UI is the biggest problem cause in C# you have declarative UI. For the most XoJo users this is a big problem to realize their projects cause they can’t find an entrance. But on the other side: there is no entrance for them in Java or JavaFX . And there they can have their drag and drop UI design. May be they anyhow don’t wanna change the language.

yeah I could maybe make it spit out Java & a javaFX based UI

javafx fxml is not so hard

One could convert to WinForms and get drag & drop designer support. And there’s a ton of 3rd party support. Even, via WiseJ.NET, deploying to the web. That comes a lot closer to one code base for all platforms than Xojo ever will.

Very interesting - and could provide a solution to the burgeoning costs of Xojo.

WinForms has no support outside of Windows though ?
At least none that I’m aware of

The one which only wants to program for windows. With JavaFX I have Windows, Linux, macOS, web, iOS and Android as platform. So it is not so that I have no choices like with Winform. That’s only helping for Windows not for other operating systems.

TBH it would be really interesting to have an example of “Hello World”, how to create it, the project, and how to do all the layout, compiling etc so the end result is the multiple outputs you say you get

Its one thing to say “Hey I can do this”
And a whole other one to say “Here‘s how” with an example

Note this isnt to say you CANT just tbh I have no clue how, what the toolchain might be, costs, etc

Being able to point people to this sort of thing would be useful
Something in the Java channel maybe

That*s simple: open IntelliJ, select maven project, external maven servers, fxml JavaFX project. Than you will have an example within 10 seconds

If MSFT maintains support for the tooling then vendors like WiseJ.NET can take that to other platforms. WiseJ ( Roadmap – Wisej.NET ) can deploy on Windows, MacOS, Android, iOS and of course web as of v 3.5. In part they do this by completely replacing the Windows-specific rendering engine. It is a serious engineering effort.

I haven’t had to use it yet – I have just kicked the tires off & on. My main concern is that they rely on .NET MAUI. Reports surfaced last year of key personnel departures from the MAUI team and slowing of velocity, etc., but officially MSFT is maintaining they are fully behind it. More broadly I’ve learned over the years that MSFT isn’t queasy about multiple false starts / resets on various technologies, without regard for dev communities that spring up around it.

Of course WiseJ could respond to bad .NET MAUI developments by pivoting to Uno or Avalonia. In theory.

So in practical terms I’m glad I probably don’t have to worry about cross platform for whatever is left of my active working life, but if I had to, I’d take a serious look.

Thanks for the info

Moved a lot of posts that were drifting WAY outside of my “tinkering” to another thread

Well moving things along

Have x-platform code for DesktopApplications, ConsoleApplications, and stubs for web(with a built in web server) & mobile (iOS & android)

The sample app I created by hand opens & creates a window with a menubar and populates with with a button, checkbox, and label
Each of the controls works as expected

ALL of this has the same api x-platform for the desktop app
But is backed by actual native windows, menubar, & controls (partial classes are amazingly useful here)

Folderitem, SpecialFolder TextInput & TextOutputStreams exist as well

I’m hoping to have a small example where give the converter a Xojo text project & it spits out a VS project you can open in VS, or Rider in the not too distant future

Onwards to make more controls !

Added Timers & Binary Streams

Might need to come up with a name for this too

Maybe the NoJoFramework ?

I very much doubt that. I absolutely love declarative syntax, the bigger problem for me with Swift were that it mostly uses structs instead of classes, and concepts that do not exist in Xojo.

Yeah this “framework” mimics a lot of what Xojo would write under the hood

For instance this is the constructor from a custom subclass of Window for some quick tests
it creates the window and adda a button, checkbox, label & timer to the window

And it sets their handlers (much like Xojo’s addHandler)

public MyWindow() {

    // two ways to set up a button
    //     long form - call constructor then set each property
    // _btnClickMe = new PushButton(this);
    // _btnClickMe.Left   = 20;
    // _btnClickMe.Top    = 40;
    // _btnClickMe.Width  = 120;
    // _btnClickMe.Height = 32;
    // _btnClickMe.Text   = "Click Me";

    int nextTop = 40;
    // short form - call constructor then set each property in one shot
    _btnClickMe = new PushButton(parent: this) { Left   = 20, Height = 32, Top    = nextTop, Width  = 120, Text   = "Click Me"} ;

   // add an event handler
    // and yes this make it super easy to have multiple buttons with the same event handler  !
    _btnClickMe.Pressed += Button1Pressed ;
    nextTop += _btnClickMe.Height + 8;

    _btnChkMe = new CheckBox(parent: this) { Left   = 20, Height = 32, Top    = nextTop, Width  = 120, Text   = "Check check", AllowsMixed = true} ;

    _btnChkMe.ValueChanged += CheckBoxPressed;

    nextTop += _btnChkMe.Height + 8;

    _lblUntitled = new Label(parent: this) { Left = 20, Height = 32, Top = nextTop, Width = 120, Text = "Untitled" };

    _lblUntitled.Pressed += Label1Pressed ;

    _timer = new Timer();
    _timer.Action += HandleTimerAction;
    _timer.Period = 2500;
    _timer.RunMode = Timer.RunModes.Multiple;

}

edited : instructor changed to constructor
I hate autodestroy