Adding Roo to a Xojo app

The prerequisites to using Roo as a scripting language for you own Xojo app are the same as for building the Roo command line tool from source, namely:

  1. The source code. Available on the GitHub releases page
  2. A valid Xojo console license
  3. A valid Monkeybread Software MBS Xojo plugin license

Copy the Roo module from the repo and paste it into your Xojo project. Roo has been tested on macOS, 64-bit Windows, 64-bit Linux and the Raspberry Pi (running Raspbian). I haven’t tested it in a Xojo Web project but it should work just fine. Roo will not work in iOS projects as it relies on the String class and the MonkeyBread plugin (neither of which are compatible with iOS at present).

Remember that somewhere in your project you will need to register the MBS plugin otherwise Roo won’t compile.

Required functions

The Roo standard library provides print() and input() global functions. You will need to provide an implementation for each of these in your app.

The print() function

The print() function takes a String. Depending on your app you may wish to print this value to the console, display it in a message window or assign it to a variable within your Xojo app.

Create a method in your app (let’s say MyPrintMethod()) and use AddHandler to link MyPrintMethod() with a Roo.Interpreter instance’s Print() method:

' Assuming myInterpreter is an instance of Roo.Interpreter
AddHandler myInterpreter.Print, AddressOf MyPrintMethod

The input() function

The input() function takes a String (which may be empty) and returns a String. The String parameter is the optional prompt to display to the user. The String returned by the function will be handed back to the running Roo script as a native Roo Text object.

' Again assuming myInterpreter is an instance of Roo.Interpreter
AddHandler myInterpreter.Input, AddressOf MyInputMethod

Running Roo code within your app

Executing Roo code is reasonably straightforwards. Below is a comprehensive example of setting up a parser, resolver and interpreter including delegating custom error handling methods.

' We will assume `source` is a String containing the source code to interpret.

' Setup a parser.
dim myParser as new Roo.Parser
' Tell our Parser to call our app's custom `ScanningError()` method 
' if a scanning error occurs (optional).
AddHandler myParser.ScanningError, AddressOf ScanningError

' Tell our Parser to call our app's custom `ParsingError()` method 
' if a parsing error occurs (optional).
AddHandler myParser.ParsingError, AddressOf ParsingError

' Parse the source code.
dim ast() as Roo.Stmt = myParser.Parse(source)
if self.parser.hasError then return ' Bail.

' Create a new interpreter and delegate the `Print()` and `Input()` methods.
dim myInterpreter as new Roo.Interpreter
AddHandler myInterpreter.Print, AddressOf MyPrintMethod
AddHandler myInterpreter.Input, AddressOf MyInputMethod

' Perform static analysis and symbol resolution on the AST.
dim myResolver as new Roo.Resolver(myInterpreter)
if myResolver.hasError then return ' Don't run if the resolver failed.

' Run the code.

' Catch any errors. Remember that scanning and parsing errors will 
' already call this app's custom ScanningError() or ParsingError() methods 
' assigned above if they occur.
exception err as Roo.ResolverError
  ' Handle this...
exception err as Roo.RuntimeError
  ' Handle this...
exception err as Roo.QuitReturn
  ' The user wants to quit the script. Handle this...

Although the above looks a little verbose, a lot of it only requires doing once per interpreter. Assuming you’ve called the code above, subsequent calls to run Roo code can be cut down to the following:

' Parse the source code.
ast() = myParser.Parse(source)
if not myParser.hasError then
    if not myResolver.hasError then
    end if
end if