DEV Community

Cover image for What I got wrong building my own programming language in Python
Divyansh Garg
Divyansh Garg

Posted on

What I got wrong building my own programming language in Python

While recording a demo video for GravLang v1.1.0, I pasted the example from my own README into my own interpreter. It didn't run.

The example used new Student(...), super.init(...) and an arrow lambda, (a, b) => a + b. GravLang supports none of those. It also used // comments (GravLang uses #), and the README promised six IDE themes when the IDE ships four. I hadn't noticed any of it.

What GravLang is

GravLang is a dynamically typed language written entirely in Python 3.10+. It has classes with single inheritance, closures, lambdas (func(x) { return x * 2; }), variadic parameters, f-strings, dictionaries, array slicing, try / catch / finally and a built-in formatter. The pipeline is the classic one: lexer, parser, AST, interpreter, environment.

The part I like most is the IDE. Press Stages and it shows each step for the code you just wrote. For let total = 2 + 3; the lexer produces:

LET  ID(total)  ASSIGN  INT(2)  PLUS  INT(3)  SEMI
Enter fullscreen mode Exit fullscreen mode

The parser builds:

VarDecl(name='total', value=BinOp(Literal(2) + Literal(3)))
Enter fullscreen mode Exit fullscreen mode

and the interpreter logs each step as it runs. There is also a debugger with breakpoints, single-stepping and a live variable inspector, plus linting, autocomplete and four themes: Catppuccin Mocha, GitHub Dark, Solarized Dark and Catppuccin Latte. Watching a program move through those three stages taught me more than writing them did.

The bugs v1.1.0 fixed

None of them were parser bugs.

Files with a byte-order mark wouldn't load. Some Windows editors save UTF-8 files with a BOM, and others leave non-breaking spaces in the source. The lexer rejected both, and now it accepts them.

The standard library was wrong in boring ways. sqrt, capitalize and padding had bugs, and the iterative factorial and fib gave wrong answers. Those are the first functions anyone tries.

The config file lived in the wrong place. I stored IDE settings next to the code. That works from source, but a one-file .exe unpacks into a temp folder, so the settings are lost, and it fails outright on read-only installs. Settings now live in a per-user file, and the GRAVLANG_CONFIG environment variable overrides the path.

Theme switching only half worked. It updated the editor but not hover handlers, ttk styles or scrollbars, so parts of the IDE kept the old colours.

Formatting also couldn't be undone. The formatter rewrites your code from the AST, which is fine until it changes something you wanted to keep. A format can now be undone.

What I would do differently

If I started over, I would run every README example in CI, test with files saved by other people's editors, and decide where settings live before packaging anything. The code I wrote for the language was the part I tested. The files around it caused most of the bugs.

Try it

GravLang is MIT licensed. Four demos are written in GravLang itself: Snake, a todo app, a Brainfuck interpreter and a grade analyzer.

git clone https://github.com/Divyansh3105/GravLang.git
cd GravLang
pip install -e .
python main.py
Enter fullscreen mode Exit fullscreen mode

Repo: https://github.com/Divyansh3105/GravLang

It is a learning project and a tree-walking interpreter in Python, so speed isn't the goal. If you have built a language or an interpreter, what would you add next?

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more