Rho - Expression Builder

David Newberry - Oct 5 '20 - - Dev Community

This blog lags development slightly. If you want to see how Rho is looking, check out the github repository.
https://github.com/paxfeline/rho

The first task I set for myself with Rho was to create an expression composition interface.

Consider how expressions are parsed and evaluated. An expression is converted into a tree structure which reflects the specific order in which each operation should occur. For example, take the expression x = x + 5. It would be converted into the following tree:

   =
 __|__
|     |
x     +
    __|__
   |     |
   x     5
Enter fullscreen mode Exit fullscreen mode

To make things flexible, let's conceptualize each node as a function, and its children as the function's arguments. You'd get something like this:

Set( x, Add( x, 5 ) )

But I want to have functions all the way down. Let's back up.

In this Logo-like language, I want to be able to enter a command like "forward" and then specify an expression for how far forward I want to turtle to move. The simplest kind of expression would simply be a constant value.

There will be objects which I refer to as Rho functions (or just functions -- hopefully the meaning will be clear from the context). Each Rho function will have a factory function that generates instances of that function, so we can get a unique Rho function object for each use of the function in the user's program. For example, each instance of the addition function is unique, which its own set of arguments.

Each Rho function object has several methods, most importantly "render" and "execute". The render method is essentially a React functional component which renders the composition interface for a given function. The execute method actually carries out the function (for example, adding the functions arguments and returning their sum).

Most Rho functions will expect their arguments to also be Rho functions. But there are a few exceptions, which get used a lot. Perhaps most important to start with is the "constant" function. The argument for the constant function is not another Rho function, but the actual constant value. However, the "constant" Rho function itself does behave like a regular function, retuning its value when executed.

In order to implement getting and storing variable values, a simple "runtime" will have to be implemented. This might simply be an object that can act as the namespace for variables in Rho.

Before worrying about the "runtime", however (which I expect to be straightforward), I am going to focus on the interface to construct these nested functions as described above.

The root of the composition interface is a React component which correlates to a single Rho function. We'll call this component RhoExpressionComposer (REC), because essentially the whole expression composition interface will be built from them, often nested several layers deep.

At first, the REC component won't have a function specified. Instead, it will present a popup menu (or some other interface) that allows the user to select a function. Then, the REC will change to now show the composition interface for the selected function. It does this by using the Rho function's "render" method as a React component.

When prototyping this interface, I made the Rho function part of the REC component's state. This made it pretty easy to put it together and see the basic interface in action.

However, there was a huge problem: the information about the expression being created was scattered throughout the states of numerous components. To make it useful, I had to store the entire tree in the state of a single (root) component, and figure out a way for child components to update their leaves.

Tune in next time for that.

. . . . . . . .