Programming Languages
A programming language is a formal language in exactly the sense the previous topic just defined, and a compiler or interpreter's entire job is bridging that formal language down to the real binary instructions the computer-architecture topic described a CPU actually executing. Parsing is the first step, and it is the theory-of-computation topic's language-recognition problem made concrete: source code is checked against the programming language's formal grammar, and if it matches, parsing builds an abstract syntax tree, a structured representation of the code's actual meaning instead of its raw character-by-character text — the variables, operators, conditions, and functions from the programming-fundamentals topic are exactly the pieces that tree is built out of. A type system is a set of rules about what kinds of values a variable or expression is allowed to hold, and checking those rules before a program ever runs catches an entire category of bugs — adding a number to text, calling a function with the wrong number of arguments — at compile time rather than letting them surface as crashes later, which is the concrete payoff behind the compile-time-versus-run-time tradeoff the programming-fundamentals topic raised without fully explaining. From here, a language implementation takes one of two paths: an interpreter walks the abstract syntax tree directly and executes it step by step as it goes, the same instruction-at-a-time spirit as the CPU's own fetch-decode-execute cycle but running one level higher, in software, which makes interpreted languages flexible and quick to start but adds a layer of translation overhead to every single operation; a compiler instead translates the entire program ahead of time into machine code or a lower-level intermediate form, so by the time the program actually runs there is no translation layer left at all — just the real instructions the CPU topic described executing directly. Code generation is that compiler's final phase, walking the type-checked syntax tree and emitting the literal CPU instructions — the same ADD and LOAD mnemonics the computer-architecture topic introduced — that will eventually be fetched, decoded, and executed on real silicon. Many real languages blend both paths, compiling to an intermediate bytecode that a specialized interpreter then runs, trading some of a compiler's speed for an interpreter's flexibility. This chapter completes the straight line this series has walked since digital logic — gates became a CPU, a CPU runs instructions, and a compiler is what turns human-readable code into those exact instructions — and the next topic, concurrency and parallel computing, is what happens when more than one stream of those instructions needs to run at once.