How I Ended Up Building a Programming Language While Joking About Something Else

Hello everyone. As August was coming to an end, I was thinking, “I should write one more post, but what am I supposed to write about this time?” Then, while I was looking for a topic, one appeared on its own last night.

Yes, I was talking to AI again.

We were joking about data security, programming languages, and the way people sometimes make technology sound far more mysterious than it actually is. Somehow the conversation reached Terry Davis, HolyC, and TempleOS. That was when an extremely dangerous question came to mind:

“Is building a programming language really that difficult?”

A normal person would probably read a few pages about compiler theory, realize that the subject is complicated, say “Yes, apparently it is,” and continue with their life.

My reaction was slightly different:

“Then we can build a toy language too.”

And that was how it started.

Of course, I was not going to tell an AI, “Build me a programming language,” and then sit back. Before there was even a single line of implementation code, I spent hours deciding how the language should behave.

“Wouldn’t this break something else?”, “What happens to the old behavior if we do this?”, “Why should it be called input? I want to call it take.”, “Does it really have to be try-catch-finally?”, “What happens if this is null?”, “If a collection is constant, should the objects reachable through it also be immutable?” — the conversation kept getting longer.

There Was No Code Yet, But There Were Already Rules

For example, I did not want to use the traditional = operator both for declaring a variable and changing its value. I wanted those two intentions to be clearly separated:

score: Int := 10

If the value changes later:

score = 20

For reading user input, I preferred take instead of input:

name: String := take("Your name: ")
write("Hello " + name)

When we reached error handling, I also wanted the language to have its own vocabulary instead of simply copying the usual names:

attempt {
    value: Int := int(take("Enter a number: "))
}
except error: ValueError {
    write("That is not a number.")
}
ultimately {
    write("Operation completed.")
}

At the beginning, the conversation was mostly, “Wouldn’t it be fun if we did it this way?” After a while, the AI also stopped agreeing with every idea I proposed.

It started answering with things like: “That would break this part of the language,” “I would not recommend that,” or “That introduces semantic ambiguity.”

And I increasingly found myself asking:

“But is it still compatible with the old behavior?”

I think that was the point where the project stopped being just “let's make a toy language.”

Suddenly, There Was a Real Compiler

My original idea was very simple: define a few keywords, write a small parser, and make something appear on the screen. Before long, however, the project had turned into this:

AhdCode Source Code
        ↓
Lexer
        ↓
Parser / AST
        ↓
Semantic Analysis
        ↓
Typed IR
        ↓
Go Backend
        ↓
Native Executable

In other words, an .ahd file was now actually being parsed, type-checked, lowered through an intermediate representation, translated through a Go backend, and compiled into a native executable.

The funny part is that I do not have deep experience with Go.

On one side, the AI was fighting with compiler theory, semantic analysis, IR, and the Go backend. On the other side, I was supervising the system and constantly saying things like, “That should not behave like this,” “That violates the rule we defined,” or “Did this break an old test?”

Making jokes about terminology I did not know was also one of the most entertaining parts of the process.

At one point, the subject of a “panic” came up while discussing tests. My first reaction was:

“What, is the compiler sitting there thinking, ‘How the hell am I supposed to run this?’ and panicking?”

Later I learned that panic in Go means something slightly different. The compiler appears to be psychologically stable for now.

“It Will Take About Two Weeks”

As the project began taking shape, I started increasing the scope. I wanted a command-line interface, native builds, a formatter, a REPL, GitHub releases, and eventually a proper installation process.

At some point I said:

“Eventually, people should not have to download random files from GitHub and manually configure things. It should install like a normal application.”

The estimated development time was roughly two weeks.

Naturally, my response was:

“Oh, really?”

About 24 hours later, there was an actual working programming language.

Obviously, I am not claiming that it suddenly has the ecosystem of Python, Go, or C, which have decades of development behind them. But there was now a real compiler that reads source code, performs type checking, reports errors, and produces native executables.

For example, a basic loop:

i: Int := 0
total: Int := 0

while i < 10 {
    total += i
    i += 1
}

write(total)

Functions:

square: Function := (
    value: Int
) -> Int {
    return value * value
}

write(square(12))

Classes, inheritance, error handling, List and Pair collections, modules, constants, runtime type behavior and many other pieces followed.

My “toy language” was becoming increasingly difficult to describe as a toy.

Then I Made a Logo Too

Once the project became a little too serious, I realized that it already had a name, a GitHub repository, releases, and an editor extension — but it did not have a proper logo.

Well, after going that far, leaving it without a logo did not feel right either.

So I sat down and created the AhdCode logo as well. At that point, the thing I had started as a joke the night before suddenly had branding too.

AhdCode Logo Image
 

I suppose once you start designing a logo for something, the excuse “I am just experimenting” becomes slightly less convincing.

Then the Standard Library Started Growing

Once the language could actually run programs, another question appeared:

“Okay, but what are we going to do with it?”

That was when I started expanding the standard library.

First came Time, DateTime, Duration, and Calendar.

bring Time

write(Time.Calendar.isLeapYear(2028))
write(Time.Calendar.daysInMonth(2028, 2))

start: Real := Time.monotonic()

Time.sleep(500)

elapsed: Real := Time.monotonic() - start
write(elapsed)

We also began adding general module aliases so that long module names could be shortened:

bring Time as T

write(T.Calendar.isLeapYear(2028))

A few days earlier, I had been asking whether building a programming language was difficult. Now I was discussing backward compatibility in the import system.

And then, once again, I failed to stop where I probably should have.

As someone who works with mathematics, I asked:

“Wouldn't it be nice if it could generate LaTeX too?”

And that brought us to LaTeX support for v0.1.5. The goal is not merely to generate .tex source files, but eventually to generate PDFs without requiring users to install a massive LaTeX distribution separately.

For this, we started integrating a lightweight Tectonic engine into the AhdCode distribution.

The intended usage is roughly like this:

bring Latex as L

body: String := ""

body += L.section("Introduction")
body += L.escape("This document was generated by AhdCode.")

body += L.equation(
    "\\sum_{k=1}^{n} k = \\frac{n(n+1)}{2}"
)

source: String := L.document(
    body: body,
    title: "AhdCode Document"
)

L.pdf(
    source: source,
    output: "example.pdf"
)

So a conversation that had started one night with:

“Is building a programming language really that difficult?”

soon turned into:

“How can we keep the offline LaTeX engine distribution below 250 MB?”

I can confidently say that the second sentence was not part of my plan the night before.

The AI Wrote Code — So What Did I Do?

There is one point I especially want to make clear. I am not claiming that I personally sat down and wrote every line of AhdCode from scratch.

AI generated a substantial amount of code throughout the project. It handled major parts of the compiler implementation, Go backend, tests, and many technical details that would otherwise have taken considerably longer.

But I was not simply pressing a “continue” button either.

I spent hours defining and debating:

  • how the language should look,
  • which syntax should be accepted,
  • which behaviors should count as errors,
  • how the type system should behave,
  • the rules for null and Constant values,
  • the class and inheritance model,
  • the module system,
  • backward compatibility boundaries,
  • and, perhaps most importantly, which features should deliberately not be added.

Sometimes my most important contribution to the project was simply saying:

“No. Let's not do that. The language will become ridiculous.”

This process also taught me that developing software with AI is not simply about writing prompts and waiting for code.

As the project grows, you have to question the generated implementation, inspect its tests, check whether new decisions contradict old ones, and occasionally reject an entire approach.

AI may be writing code, but you still need to know what you actually want.

I Somehow Built Two Languages This Year

While preparing this article, I realized something else.

I have actually created two different language projects this year.

The first is the artificial AHD language that I have been designing for the fictional world of a book I am writing. That one was intentional and had been one of my long-term creative goals.

The second one is AhdCode.

AhdCode was absolutely not planned.

It started while I was joking about something else.

Then I asked, “How hard can it really be?”

Then it got a GitHub repository.

Then it got releases.

Then it started producing native executables.

Then it got extensions for VS Code and Antigravity.

Then its standard library started growing.

And now we are discussing generating PDFs through LaTeX.

In the next stages, I also want to create proper installation packages for macOS, Windows, and Linux.

What Comes Next?

I still want AhdCode to eventually become a general-purpose language that can also be used for web development.

From this point onward, however, I am going to try to move a little more slowly.

At least that is what I believe while writing this sentence.

If I become impatient again tomorrow, feel free to use this article as evidence against me.

First, I want to make the standard library more complete. File and path handling, Regex, document and spreadsheet generation, JSON, and similar foundations are among the things I want to add over time.

After that, I want to make AhdCode something people can install normally instead of downloading files from GitHub and manually configuring a terminal environment.

The plan is roughly:

macOS   → DMG
Windows → Setup.exe
Linux   → appropriate installation package

After that can come a language server (LSP) providing autocomplete, diagnostics, Go to Definition and similar IDE features; then a debugger, and later the web-oriented libraries.

Once the language is sufficiently mature, I also plan to make AhdCode directly downloadable through ahdakademi.com.

Conclusion

One of the best things this project has already taught me is this:

I did not have a predefined goal of creating a programming language. I was simply complaining about why some existing systems were designed in ways that felt unnecessarily complicated.

Then I took the question:

“How would I design it?”

a little too seriously.

Maybe three people will ever use AhdCode. Maybe I will be the only one. Maybe a few months from now I will look back and laugh again, asking, “Why did we even build this?”

But turning an unplanned idea into a working programming language has already been a valuable experience for me.

I do not know what I will write about in the next article.

I suppose that is exactly what this article demonstrates:

Sometimes life is better when it is not completely planned.

I will leave the project link below for anyone who is curious about AhdCode or wants to try it. Once proper installers are ready, I also plan to make it much easier to access through ahdakademi.com.

AhdCode GitHub:
https://github.com/aliharundaldalli/AhdCode