DEV Community

Cover image for Ez-Language : An Experiment for multi-language project
Amine Hammami
Amine Hammami

Posted on

Ez-Language : An Experiment for multi-language project

This is a story-like technical article about why and how I made this language experiment.


Context

It was a normal night, drinking my coffee, working on a Minecraft mod that adds a custom OBJ renderer. I was thinking it would be great if I could just have C++ or Rust doing this heavy work instead of Java… and then an idea came to me: surely by now there must be something that could do that exact job:

a coordinator between languages

I found a lot of options — Bazel, FFI, microservices, gRPC, WebAssembly, VM ecosystems, transpilers — but nothing really clicked. I would use FFI for speed, VMs for scalability, transpilers for flexibility, but I couldn't find something I could stick with and use everywhere.


My Take On This Problem

I wanted something where you describe what you want to do, and the language handles the annoying stuff for you.

So after that research, I decided to make my own thing — something I would learn once and use everywhere in my poly-language projects. And also as an excuse to learn C++.

My vision was to make a programming language.

A picture of what I was thinking about:


(I am a full-time artist :P)


Idea / Vision (Now)

  • a system where languages are connected together
  • C++ / Rust / Python are not separated anymore
  • one place to coordinate everything
  • no manual FFI, no messy glue code everywhere
  • focus on “what you want to do”, not “how to connect languages”

Implementation

You and I both know that the first step of every project is giving it the sh**tiest name possible, then calling it a day.


First Steps

That out of the way, I started building a custom lexer with a language I barely used before — fake it till you make it.

I was going hard on the OOP while also trying to keep Python-like simplicity. Somewhere along the way I got lost in parser design, ASTs, interpreters, and runtime systems — and realized I was accidentally building a real programming language instead of solving my original Minecraft mod problem.


Core Idea (How I Started Building It)

After all that mess, I switched to using ANTLR4 because:

  1. the grammar itself doubles as documentation (.g4 files)
  2. it gave me an easier lexer/parser pipeline

ANTLR also made experimenting way faster because I could focus more on the language behavior instead of rewriting parser logic every 5 minutes.

At first, the interpreter directly walked the parse tree itself. Which worked… until it didn't.

I later realized there's a big difference between:

  • “this parses correctly”
  • and
  • “this is architecturally sane”

So now there's a lot of future work involving proper AST lowering and separation between parsing and execution.


Friend Modules Concept

Friend modules are basically a fancy name for the FFI system mixed with automatic build planning.

They are declared like this:

friend math : cpp as mcpp;
Enter fullscreen mode Exit fullscreen mode

This line tells EZ:

  • there is a file called math.cpp
  • compile it as part of the project
  • refer to it in EZ code as mcpp

Then we can use it like this:

print(mcpp.add(3,2));
Enter fullscreen mode Exit fullscreen mode

The goal was to remove all the annoying manual setup:

  • no handwritten headers
  • no manual linking
  • no dlopen
  • no random shell scripts

Just:

"this module exists, use it"

The same idea extends to Python through auto-generated bridge code, though it's rougher around the edges.

Right now the system mostly supports primitive types correctly (int, float, boolean, etc.), but I already know things will get painful once structs, buffers, callbacks, or heap-managed objects enter the picture.

Because at the end of the day:

every abstraction eventually reaches the FFI wall.


Interpreter / Execution Model

EZ currently has two execution paths:

1. Interpreter Mode:

This directly walks the parse tree and executes the program.

It currently supports:

  • variables
  • arithmetic
  • conditions
  • loops
  • functions
  • returns
  • friend module calls

This is the main development path right now because it lets me experiment quickly without rebuilding an entire compiler pipeline every time I change something.


2. Native / C Backend:

The second path translates EZ code into C code, then compiles it using clang.

This started mostly as an experiment to answer the question:

"Can this language actually compile into something native?"

Right now the C backend only supports a subset of the language and intentionally does not support friend calls yet.

There's also a backend comparison script that runs the same file through:

  • the interpreter
  • the native backend

Then compares the outputs to make sure they behave similarly.


Environment System

The friend modules problem made me think about a bigger issue: environment reproducibility. One drunken night with NixOS made it click.

I loved the small Docker-like environments you could create and how reproducible everything felt. That immediately made me think:

why isn't this feeling part of programming languages themselves?

The classic:

"works on my machine"

Issue becomes an absolute nightmare once multiple languages, compilers, runtimes, and dependencies get involved. Especially in poly-language projects.

So I started experimenting with making environments part of the language itself.

Example:

env native;
Enter fullscreen mode Exit fullscreen mode

The idea is:

  • the environment should be part of the program contract
  • not just hidden setup instructions inside a README nobody reads
  • reproducibility should be built into the workflow from the start

The language uses nix-shell to run its build plan and will eventually use a config file to pin each compiler and interpreter version for the project:

# EZ-Language Configuration
# Place in project root (.ezconfig) or home directory (~/.ezconfig)
# Project-level config overrides user-level config

[python]
# Python interpreter path (default: python3 from PATH)
executable = "python3"

[c]
# C compiler (default: clang)
compiler = "clang"
# Standard version (default: c11)
standard = "c11"

[cpp]
# C++ compiler (default: clang++)
compiler = "clang++"
# Standard version (default: c++17)
standard = "c++17"

[build]
# Output directory for friend modules (default: .ezbuild)
output_dir = ".ezbuild"
# Verbose build output (default: false)
verbose = false
# skip Nix env activation when set (default: false)
no_env = false
Enter fullscreen mode Exit fullscreen mode

Right now the config system is still rough and partially ignored by the Nix environment — but the goal is clear:

"clone project → run project"

Without spending 3 hours fixing dependencies, compiler versions, or missing libraries.


Problems I Ran Into

A lot.

Some of the biggest ones:

  • the interpreter directly walking the CST instead of a proper AST
  • type marshalling for friend calls becoming ugly very quickly
  • more segfaults and core dumps than I'd like to admit
  • trying to keep the language simple while the backend complexity explodes
  • parser/runtime/features growing way faster than the architecture itself
  • adding grammar features before implementing them properly
  • realizing “easy syntax” does NOT mean “easy implementation”

Also: FFI is terrifying.

The deeper I went into cross-language interaction, the more I understood why every existing solution feels complicated.

Languages fundamentally disagree on:

  • memory ownership
  • calling conventions
  • threading
  • exceptions
  • object lifetimes
  • type systems

So making them “just work together” cleanly is much harder than I originally thought — but doable.


Current State Of The Project

The project is still extremely experimental.

There's a lot of:

  • messy code
  • technical debt
  • unfinished systems
  • AI-assisted scaffolding
  • “future me will fix this” moments

I'm also starting to hate the name — thinking of gluCore as a replacement.

But overall, the core idea works:

  • friend modules work
  • the interpreter works
  • the native backend exists
  • multi-language execution is happening

And honestly, that's already farther than I expected when this whole thing started from a late-night Minecraft modding session.


Conclusion

Somewhere between the Minecraft mod and the segfaults, this stopped being a language and started being something else — more of a coordination layer, a system that lets multiple languages work together without you managing the glue.

I don't even think this is a programming language in any traditional sense anymore. It still has functions and variable declarations, but it's become more and more like a runtime that manages and connects multiple languages. I'm not sure what to call that. Maybe that's the point.

Whether it's useful to anyone else, I genuinely don't know. But the core idea works, and that's already further than a late-night coffee session had any right to get.

If you want to dig into the mess — or help make it less of one — the repo is linked at the top.


Also: I got accused of writing AI slop when I tried more structured technical articles on Reddit, so I figured — why not just embrace the chaos? Hopefully it made this more fun to read, and hopefully I don't get accused of being a robot this time.

Top comments (0)