A programming language implementation turns source text into behavior through a chain of representations. The names and boundaries differ across languages, but separating the stages makes errors and optimizations easier to understand.
Front end: recognize and explain the program
A lexer groups characters into tokens such as identifiers, numbers, and punctuation. A parser turns those tokens into a syntax tree that records grammatical structure. Semantic analysis resolves names, checks types or other language rules, and attaches information later stages need. An abstract syntax tree omits details that matter only to spelling, such as redundant parentheses.
For total = price * count, the syntax tree represents assignment with a multiplication subtree. A type checker may ensure the operands can be multiplied and that the result can be stored in total. A dynamically typed language may defer some of that work until execution instead.
Middle and back end: choose a representation and target
Some compilers lower the tree to an intermediate representation (IR), optimize it, and emit machine instructions. A virtual-machine implementation may emit bytecode interpreted by a runtime. A just-in-time compiler can compile frequently executed code while the program runs. These strategies can coexist in one language implementation; “compiled” and “interpreted” are not mutually exclusive labels for a whole language.
Optimization must preserve observable behavior under the language's rules. Constant folding can evaluate a known expression early; dead-code elimination can remove work whose result cannot affect the program. Undefined behavior, reflection, dynamic loading, or exception semantics can limit which transformations are legal.
Runtime fills in dynamic work
Execution may still need name lookup, object allocation, garbage collection, dynamic dispatch, or calls into libraries. Native code can rely on a runtime too, while bytecode execution still uses machine instructions beneath the interpreter.
Implementing a tiny language is a direct way to learn these boundaries. Crafting Interpreters walks through scanners, parsers, interpreters, bytecode, and virtual machines with runnable examples.