I built Dotal because programming languages had always felt like finished objects. I wanted to see one as a pipeline of decisions instead: how source text becomes structure, how structure becomes behavior, and where useful error messages come from.
Using Albanian syntax made the project personal, but it also created a serious design constraint. Translating keywords is easy. Designing a language that still feels consistent is harder.
From characters to meaning
The lexer is the first boundary. It reads characters and produces tokens: identifiers, numbers, operators, punctuation, and reserved words. This stage taught me that small rules have large consequences. Handling whitespace, comments, malformed numbers, and unexpected characters cleanly determines how understandable every later error will be.
The parser then turns tokens into an abstract syntax tree. Expressions need precedence. Statements need clear termination rules. Blocks and control flow need structure that can be represented without ambiguity.
Once I could print the syntax tree and compare it with the source, debugging became much faster. The tree is the bridge between what the programmer wrote and what the runtime can execute.
The interpreter
Dotal walks the syntax tree and evaluates nodes. Variables require an environment. Functions require scope and argument binding. Conditionals and loops require controlled execution. Errors need source context instead of a generic failure.
The most valuable part was discovering how features interact. A function is not just a syntax rule. It affects parsing, scope, returns, errors, and runtime values. Language design forces you to think across the complete system.
Dotal is still an experiment, but it changed how I write software. Frameworks and libraries now feel less opaque because I am more comfortable tracing behavior down through layers. Building a language is an extreme version of a useful engineering habit: when an abstraction feels magical, follow it until the magic becomes a mechanism.