Why Would Somebody Program Treepython Instead of Python

Treepython is a variation of python which works in my structure editor textended. I am planning to write a computer game using this new language this February. In this post I will be adding new semantic patterns into treepython. It hopefully explains my motivation to break up with plaintext programming.

Here's the samples/clearscreen.t+ from textended-edit. When I run it, it grabs the editor display and colors it black.

clearscreen.t+

The 0.0, 0.0, 0.0, 1.0 represents the black color here. Programmers see their colors like this, which stands to explain where the term "programmer art" is coming from.

We could do a slightly better job by representing the color in the way they appear in an image manipulation software. This is called hexadecimal notation. In python you could create a function which constructs the needed format from a string, and input the color using that function as a helper. It would look like this:

OpenGL.GL.glClearColor(*rgba_to_vec4("#000000"))

The star stands for variable number of arguments. The rgba_to_vec4 produces 4 values in a list, but glClearColor wants 4 values. The star expands the list to fill 4 argument slots.

This is slightly more readable, because you can copy the string into your preferred color chooser to see the color. But it doesn't come even close to what I can do in the treepython:

clearscreen.t+

If you coded in trees, you could represent the color right here. Next I'm going to show how that happens. First we annotate a string with symbol float-rgba. This will represent our hexadecimal colors and should translate to a tuple, each channel represented by a floating point number.

If we try to evaluate the program, it returns us an error. The editor highlights the bad construct and shows the error message on the right.

clearscreen.t+

Extending Treepython with semantics

The error message tells that it cannot be recognised as an expression. Lets extend treepython to recognize strings that are labeled float-rgba.

@semantic(expr, String("float-rgba"))
def float_rgba_expression(env, hexdec):
    channels = [c / 255.0 for c in hex_to_rgb(hexdec)] + [1.0]
    return ast.Tuple(
        [ast.Num(x, lineno=0, col_offset=0) for x in channels[:4]],
        ast.Load(),
        lineno=0, col_offset=0)

def hex_to_rgb(value):
    lv = len(value)
    return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))

Treepython is a translator between textended tree structures and python ast, so the above code resembles a lisp macro. It's up to the language's implementor to decide how his language is extended.

Next if we try to run the program, the program crashes and produces the following error message to the terminal:

Traceback (most recent call last):
  File "main.py", line 550, in <module>
    paint(time.time())
  File "t+", line 0, in paint
TypeError: this function takes at least 4 arguments (1 given)

I intend to get it overlay this error over the file, just like it did small while ago. Anyway you may see why it's happening here. We are passing a tuple to the call.

clearscreen.t+

That there's vararg implemented may give you a false sense of completeness of this project. I just coded in the support for variable argument semantics while writing this blog post.

Extending Treepython's layouter with semantics

Here's we got the code to extend our layouter with the float-rgba string semantics:

@semantic(expr, String("float-rgba"))
def float_rgba_expression(hexdec):
    try:
        channels = [c / 255.0 for c in hex_to_rgb(hexdec[:])] + [1.0]
        rgba = tuple(channels[:4])
    except ValueError as v:
        rgba = 1.0, 1.0, 1.0, 1.0
    prefix = sans(' #', defaultlayout.fontsize, color=defaultlayout.gray)
    text = sans(hexdec, defaultlayout.fontsize, color=defaultlayout.white)
    return hpack([Glue(2), ImageBox(12, 10, 4, None, rgba)] + prefix + text)

def hex_to_rgb(value):
    lv = len(value)
    return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))

clearscreen.t+

Would you have rather wanted a border around it? No problem:

@semantic(expr, String("float-rgba"))
def float_rgba_expression(hexdec):
    try:
        channels = [c / 255.0 for c in hex_to_rgb(hexdec[:])] + [1.0]
        rgba = tuple(channels[:4])
    except ValueError as v:
        rgba = 1.0, 1.0, 1.0, 1.0
    prefix = sans(' #', defaultlayout.fontsize, color=defaultlayout.gray)
    text = sans(hexdec, defaultlayout.fontsize, color=defaultlayout.white)
    yield Padding(
        hpack([ImageBox(12, 10, 4, None, rgba)] + prefix + text),
        (1, 1, 1, 1),
        Patch9('assets/border-1px.png'))
    yield Glue(2)

clearscreen.t+

As you may see now, we've improved readability of our program. Why show the hexadecimal if you can just show the color? That's because the editor needs a visual form to change the color. I think I'll be able to loosen that requirement later on.

I modified an existing language, but all these changes could have been isolated apart and introduced into the file with a directive, like this:

clearscreen.t+

To support some other things, such asadding anonymous functions into a language that doesn't support them would not be as easy. Many languages might still end up to not support extensibility at all. But there are clearly less costs to implementing new semantics in the first place...

...Well you could do this kind of things in lisp of course! But when was the last time your lisp looked like python?

Similar posts