A Logo-to-Python Transpiler


I'm not one of those who started coding super early, I didn't really start until college. However, I do remember fondly some classes I had in elementary school that taught us Logo.

Some days ago I got melancholic while using turtledemo and decided to create a project that has no practical applications whatsoever – 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()

Or compile the python code directly with chelodina --input file --run to look at the result:

Notes on PLY and the development

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.

    • Looking for variables and methods prefixed t_ in the lexer and p_ 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.
  • 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.