A Logo-to-Python Transpiler
I remember very fondly some classes I had in elementary school that taught us Logo. Some days ago I got melancholic while learning of turtledemo and decided to create this toy project with no practical applications whatsoever, but one that I had a lot of fun with – a transpiler called chelodina1.
At the moment it can turn Logo code like this:
TO triangle :length
REPEAT 3 [ FORWARD :length RIGHT 120 ]
END
TO flower :length :count
REPEAT 150 [
triangle :length
RIGHT 360 / :count
]
END
TO web
REPEAT 6 [ flower 150 18 ]
END
web
Into Python like this:
import turtle
def triangle(p_length):
for _ in range(3):
turtle.forward(p_length)
turtle.right(120.0)
def flower(p_length, p_count):
for _ in range(150):
triangle(p_length)
turtle.right(360.0 / p_count)
def web():
for _ in range(6):
flower(150.0, 18.0)
web()
turtle.done()
And it can compile the python code directly with chelodina --input file --run
to look at the result:
You will find more details about its usage on Github.
Notes on its development and PLY
For chelodina I wanted to use a Yacc-like parser in Python, so I went with the sure-fire solution that is PLY – David Beazley has been maintaining this project since 20012.
Positive notes:
- PLY is as tested as they come, once you get the hang of it, the lexer and parser error handling is very helpful along the way.
- PLYs documentation is amazing – it's so detailed it feels like someone is walking you through it.
- Python's documentation for the ast module is not particularly helpful, thankfully Green Tree Snakes more than makes up for it.
Things I didn't enjoy:
- PLY behaves more like a framework than a library, I disliked some assumptions that it makes. It is just a hassle to try and figure out what magic is going under the hood.
- Looking for variables and methods prefixed
t_
in the lexer andp_
in the parser. - Uses docstrings for regex definitions in the lexer and BNF grammar in the parser.
- Not following conventions for the slicing operator on special vars, in some cases like
[-1]
behaves differently. - Reserved words.
- Looking for variables and methods prefixed
- I'd rather use EBNF than BNF, but that's a nitpick.
For a future project, I would probably look into a more pythonic library like Lark. It's harder to develop when the library is not being explicit, as you're always second-guessing if it was a problem with your code or you just broke another magic rule you didn't know about.
This is the current BNF grammar. I have only focused on making the examples work so it still has ways to go for a complete implementation.
program : statements
statements : statements statement
| statement
statement : expression
| funcdef
| repeat
expression : NAME terms
| NAME
terms : terms PLUS terms
| terms MINUS terms
| terms MULT terms
| terms DIV terms
| terms NUMBER
| terms PARAM
| NUMBER
| PARAM
funcdef : TO NAME terms statements END
| TO NAME statements END
repeat : REPEAT NUMBER LBRACKET expression RBRACKET
I plan to work on it sparingly –with UCBLogo as the target implementation– since it's been a fun project.
Footnotes
-
That's funny because snake-necked turtles! ↩
-
And actively maintaining it, not just letting it sit pretty. ↩