Code Story

Chapter #13 – Projects

ten imperative programming projects - instructive, useful, buildable

13.0  Prologue

The Code Story chapters survey what each imperative language feature is and how it works across languages. These projects take the next step: applying those features to build something complete and worth keeping. Each project targets one or two chapters directly - closures and error handling, generics and memory, concurrency and synchronization - so the connection between concept and code stays explicit. The projects are stated as language-independent specifications. Implement in whichever language sharpens the skill you are currently developing. Rust makes ownership and generics unavoidable; C++ exposes the full cost of manual memory management; C# or Python offer a faster path to a working result when the goal is the architecture rather than the low-level mechanics.

13.1  Project Table

Project Specification Teaches Output
Expression Evaluator (REPL) Lex and parse arithmetic expressions into an AST using recursive descent. Evaluate by tree traversal. Add variables, assignment, and a read-eval-print loop. Composite data types (AST nodes), recursive functions, control flow, variables and binding. A working command-line calculator with named variables and operator precedence.
Generic Container Library Implement a stack, a queue, and a ring buffer as generic types. Enforce capacity limits, iterator support, and correct behavior on empty and full states. Generic types and trait/interface bounds, memory layout (heap vs inline storage), operator overloading for iterators. A drop-in collection library usable wherever the standard containers are too heavy or too general.
Thread Pool with Work Queue Spin up N worker threads on construction. Accept tasks as closures via a channel or mutex-protected queue. Support graceful shutdown and a join that waits for all pending work to drain. Concurrency, synchronization primitives, closures as first-class task representations. A reusable thread pool suitable for CPU-bound parallel workloads.
Result-Chain Error Handling Library Build a Result<T, E> type (if the language lacks one) with map, and_then, or_else, map_err, and unwrap_or. Add an error-accumulating collect for validating a list of inputs. Error handling strategies, higher-order functions, lambdas and closures. A composable error-handling library that eliminates nested if-error checks.
Finite State Machine Library Define states and transitions as data (enums or tagged unions). Drive the machine with an event loop; support entry/exit actions per state and guard conditions on transitions. Enumerations and pattern matching, control flow, generic types parameterized over state and event. A reusable FSM library applicable to protocol parsers, UI workflows, and game logic.
Operator-Overloaded Linear Algebra Types Implement Vec2, Vec3, and Mat3 with arithmetic operators (+, -, *, dot product, cross product, matrix multiply). Use generics to support both f32 and f64. Operator overloading, generic numeric types, composite data, and operator semantics (value vs reference). A small math library usable in graphics, physics simulation, or data processing.
Module Dependency Resolver Parse a dependency manifest (JSON or custom DSL) into a directed graph of module nodes. Detect cycles and produce a valid build order via topological sort (Kahn’s algorithm or DFS post-order). Modules and namespaces, graph data structures, control flow for cycle detection, error reporting. A standalone dependency resolver embeddable in any build tool or package manager.
Memory Pool Allocator Pre-allocate a fixed-size slab of memory. Serve same-size object requests from the pool with O(1) alloc and free using a free-list. Track utilization; detect double-free in debug builds. Stack vs heap, manual memory management, pointer arithmetic, unsafe code discipline. A pool allocator that eliminates per-object heap overhead for hot allocation paths.
Closure-Based Event Emitter Support named events; let callers subscribe with a closure and unsubscribe by handle. Emit an event by calling all registered closures in subscription order. Handle re-entrant emit safely. Lambdas and closures, environment capture, function pointers vs trait objects, lifetime management. A lightweight event emitter library with no external dependencies.
Derive Macro / Code Generator Write a procedural macro (Rust) or source generator (C#) that inspects a struct and emits: a Display/ToString impl, a builder with per-field setters, and a deep-clone. Drive it with a single #[derive] or [Generator] attribute. Metaprogramming, code generation, reflection and syntax tree manipulation, attributes and decorators. A derive macro that eliminates boilerplate for any struct that opts in.

13.2  Epilogue

These ten projects cover the full chapter sequence: data types and recursion (expression evaluator), generics and memory (container library, pool allocator), concurrency (thread pool), closures and error handling (result-chain library, event emitter), control flow and modules (FSM, dependency resolver), operators (linear algebra types), and metaprogramming (derive macro). Each is small enough to finish in a weekend but rich enough to reveal the trade-offs the chapter descriptions can only hint at. Start with whichever project overlaps most with work you are already doing. The expression evaluator is the most self-contained first project; the thread pool is the highest-payoff if concurrent programming is the goal. Build, then revisit the relevant chapters - the concepts land differently once you have written the code.