ZE and ZY: Building Interpreted Languages From Tokens to Domain Primitives

By Raymund Zyron Abella /

Inside two interpreters: multilingual syntax, recursive-descent parsing, lexical scope, subnet values, semantic matching, agents, and a tiny web runtime.

In this article

For the current individual implementations, read Ze and Zy. This earlier comparative article records the broader exploration; Zy's AI helper methods remain simulated rather than live model integrations.

I built ZE and ZY to learn what programming languages hide behind familiar syntax. ZE explores readable, multilingual programming through English, Tagalog, and Bisaya keywords. ZY pushes in a more systems-oriented direction with maps, networking values, semantic matching, and first-class AI agents. Both forced me to turn vague language ideas into exact rules a runtime could execute.

Starting With the Surface Language

ZE's central experiment is simple: keyword families can differ while the underlying grammar stays consistent. set, itakda, and ibutang all introduce a binding. show and ipakita both produce output. A single file can mix those families without changing how values or control flow behave.

That choice is more than replacing strings in a keyword table. Diagnostics must recognize the spelling the programmer used, documentation must avoid implying that one family is the canonical form, and the parser must normalize equivalent tokens without losing source positions.

ZY has a different voice. Its braces, let declarations, functions, maps, and method calls are intentionally familiar, creating room for less conventional domain features later. Designing both languages showed me that syntax should establish expectations before adding novelty.

Lexing Without Losing Context

The lexer converts source text into a stream of tokens: identifiers, literals, operators, punctuation, and keywords. It also records line and column information so later errors can point back to the source.

The difficult work lives at boundaries. A minus sign can begin a negative literal or represent subtraction. A slash may start a comment, divide two values, or belong to a subnet literal such as 10.20.0.0/16. Multi-word ZE comparisons like is at least need to become one coherent operation without making ordinary identifiers ambiguous.

I kept tokenization deterministic and pushed structural decisions into the parser. That separation made it possible to test malformed strings, incomplete operators, comments, and numeric edge cases independently from the grammar.

Parsing Into an Abstract Syntax Tree

The parser turns tokens into an Abstract Syntax Tree, or AST. A tree preserves relationships that a flat token list cannot: which expression belongs to a return statement, where a function body ends, and whether multiplication binds more tightly than addition.

ZE uses indentation-based blocks, so the parser has to treat changes in indentation as meaningful structure. ZY uses braces, which simplifies block boundaries but introduces its own ambiguity around map literals and scoped blocks. Recursive descent kept both grammars readable because each parsing function corresponds to a recognizable language rule.

Expression parsing is organized by precedence. Assignment sits below logical operators, comparisons sit below arithmetic, and calls or property access bind tightly. Keeping those levels explicit prevented the parser from becoming one large conditional with accidental precedence rules.

Evaluation, Scope, and Failure

The interpreter walks the AST using an environment chain. A variable lookup starts in the current scope and moves outward; a function call creates a new environment linked to the one where the function was defined. Return values and runtime failures travel through the evaluator as controlled signals rather than abruptly terminating the process.

ZE's try and if it fails syntax made error behavior part of the language instead of a host-runtime detail. Division by zero, invalid calls, and missing values need stable error objects that can either be handled by the program or printed with a useful source location.

This layer revealed how much policy hides inside ordinary operations. Should strings and numbers concatenate automatically? Are maps ordered? Does equality compare identity or structure? A language becomes predictable only when those questions have explicit answers.

Moving From General Syntax to Domain Types

ZY's networking values are parsed and evaluated as real domain primitives. An address such as 10.20.0.0/16 can be split into smaller blocks, expose its mask, and participate in in checks. Treating networks as first-class values is safer and clearer than repeatedly parsing strings in library functions.

Semantic matching and agent declarations extend the same idea. The syntax records model configuration and instructions as part of an agent value, while the runtime controls how a query is executed. Keeping the language boundary explicit makes it possible to validate configuration, simulate responses during development, and isolate external model behavior from the core evaluator.

A Tiny Web Mode

ZE can also run a directory of scripts as web handlers. A script reads a query parameter, constructs a response, and calls respond_html. This feature tested whether the runtime could survive repeated requests without leaking state between executions.

Web mode required a fresh environment per request, clear conversion between HTTP strings and language values, and controlled output. It was a useful pressure test because an interpreter that works once from the command line may still fail when reused continuously inside a server.

Testing the Languages as Products

Parser tests alone were not enough. I added end-to-end programs for multilingual bindings, natural comparisons, lists and loops, functions and handled errors, maps, subnet membership, semantic matching, and web responses. Each example doubles as executable documentation.

The strongest lesson was that language design is interface design. Every shortcut becomes a permanent expectation, every vague diagnostic costs debugging time, and every new feature touches the lexer, parser, AST, evaluator, tests, and documentation. Building ZE and ZY made those dependencies visible in a way that simply using a language never could.

More articles

Browse all engineering articles