Rule: Mark a feature
[x]only after the user confirms they're happy with it. Never auto-check. Rule: Ensure after a task is checked off the relevant ADR coverage table is updated as well.
| ADR | Title | Status | Covered by | % Done |
|---|---|---|---|---|
| 0001 | Symbol tracking | Proposed | 5b.7, 5b.8, 5b.10 | 100% |
| 0002 | Type system & inference | Proposed | 6.1–6.3, 6.11–6.13, 11.1 | 50% |
| 0003 | Async/green threads | Proposed | 12.1 | 0% |
| 0004 | Memory ownership | Proposed | 8.1 | 0% |
| 0005 | Resource management | Proposed | 9.1 | 0% |
| 0006 | Allocation strategy | Proposed | 8.1 | 0% |
| 0007 | String/collection types | Proposed | 10.1 | 0% |
| 0008 | Module system | Accepted | 5b.1–5b.16 | 94% |
| 0009 | Monadic AST parsing | Accepted | 5c.1–5c.10 | 100% |
- 1.1 CMakeLists.txt — C++23, find_package, format/lint targets, GTest discovery
- 1.2 flake.nix — multi-shell (frontend, backend, default)
- 1.3 .clang-format config
- 1.4 .clang-tidy config
- 1.5 Skeleton src/ and tests/ — backend.h/.cpp stub, smoke GTest, prove full build works
- 2.1 Error types — structured error enum used across reader and emitter
- 2.2 Internal C++ types — structs mirroring proto (FunctionDef, Expression, etc.)
- 2.3 FsoReader — read .fso, validate 8-byte header, deserialize, map to internal types
- 2.4 Fixture .fso files — generated from real Furst source via frontend tests
- 2.5 Tests — roundtrip .fso → internal types, invalid file/header/proto errors
- 3.1 Module + function scaffolding — emit empty
mainreturning 0, verify valid IR - 3.2 Integer literals + arithmetic — i32 constants, add/subtract/multiply, identifier lookup
- 3.3 Variable bindings —
let x = expras alloca/store/load - 3.4 Function definitions — typed params, return type, body
- 3.5 Function calls — direct calls with args
- 3.6 Lambda-lifted functions —
outer$innernaming, captured params as extra args (frontend call rewrite fixed) - 3.7 Tests — 28 tests, fixture .fso → emitted IR, tested as each feature landed
- 4.1 Wire
compile()entrypoint — .fso → LLVM module → output .ll - 4.2 Object code emission — TargetMachine::emit, in-process, configurable target triple
- 4.3 Linking — .o → executable via system linker (cc)
- 4.4 CompileOptions — output format (.ll/.o/exe), optimization level (O0-O3)
- 4.5 Integration test — .fso → executable → run → check exit code (42!)
- 4.6 Debug info — DICompileUnit, DISubprogram, DILocation on instructions for gdb/lldb source mapping
- 4.7 Hook into frontend
buildcommand —furstcCLI +furstc-backendin build/
- 5.1
furst new -n MyApi -o ./myapi— scaffold project dir withfurst.yaml,src/main.fu,.gitignore - 5.2
furst.yamlschema — name, version, type (executable/library), entry, targets - 5.3 YAML parser in frontend — add YamlDotNet, parse
furst.yamlinto project config - 5.4
furst buildwithout args — find nearestfurst.yaml, build the project - 5.5 Target triple construction — yaml
arch+os→ triple string, pass to backend - 5.6 Output conventions —
bin/for final output,build/for intermediates (.fso, .o, .ll) - 5.7
furst run— build + execute, pass through command-line args - 5.8 Library projects —
type: libraryproduces.aviaar,.fsimanifest,exportkeyword - 5.9 Project references —
dependencies:in yaml, manifest-baseddeclare, linked against.a - 5.10 Multi-file compilation —
sources:list in yaml, merged in order, single compilation unit - 5.11 Workspace support —
furst-workspace.yamllisting projects, builds all in order
ADR: ADR-0008, ADR-0001 | Discussion: modules-namespaces-bcl
ADR-0001 (symbol tracking) is implemented by the scoped symbol table (5b.7–5b.10) — two-pass collection + validation approach. ADR-0008 (module system) is the primary driver for all tasks in this epic.
- 5b.1
modkeyword parsing — parsemod Nameblocks with indentation-scoped body, dotted names (e.g.mod Api.Types) build hierarchy structurally - 5b.2 Implicit mod from filesystem — filename becomes mod name, directories extend lib path (e.g.
src/collections/list.fuwith yaml rootFurst→ libFurst.Collections, modList) - 5b.3
libkeyword parsing — parselib Pathin library projects only (compile error in executables), relative to yaml root name, overrides filesystem-derived lib path - 5b.4 Yaml schema update — add
library: name:field for root lib name (library projects only). Top-levelname:remains as project name for all project types - 5b.5 Visibility flip — remove
exportkeyword, switch to public-by-default. RemoveExportedFuncDef/TopExportedFunctionfrom AST and lowering, update parser and all tests - 5b.6
privatekeyword — private-to-mod scoping for functions and types, replacesexportas the visibility modifier - 5b.7 Scoped symbol table — symbols keyed by fully qualified path (
lib.mod.namefor libraries,mod.namefor executables), enforced declaration order (F#-style) - 5b.8 No-shadowing enforcement — compile error on duplicate symbol at same fully qualified path, language-wide
- 5b.9
openkeyword — brings direct mods under specified lib path into scope, shallow only (not sub-paths) - 5b.10 Qualified access — resolve
List.map,Furst.Collections.List.mapetc. through scoped symbol table - 5b.11 Additive mod merging — multiple files contribute to same mod; second file must use explicit
mod(filesystem convention would give wrong name) - 5b.12 Proto/fso update — add module path and
is_privatevisibility flag to .fso format (replaces lost export status) - 5b.13 Manifest update —
.fsicarries fully qualifiedlib.mod.functionpaths, not just flat function names - 5b.14 Backend module-aware codegen — namespaced symbol names in LLVM IR
- 5b.15 Entry point convention — compiler finds function named
mainin last source file's implicit mod, executable projects only - 5b.16 Tests — mod scoping, lib paths, open resolution, qualified access, shadowing errors, implicit mods, private visibility, additive merging, entry point
Deferred:
extend modfor cross-package module augmentation- Nested mods via
=syntax internalvisibility (package-private)
ADR: ADR-0009 | Discussion: monadic-ast-parsing
Replaces
AstBuilder.fswith composable FParsec-style combinators over token streams. Preserves two-phase architecture (lex → Row trees → AST), adds operator precedence, threads symbol table through parse state.
- 5c.1
TokenCombinators.fs— core combinator library:ParseState,TParser<'a>, operators (>>=,|>>,>>.,.>>,.>>.,<|>), helpers (preturn,pfail,optional,many,many1,choice,chainl1), primitives (expect,expectName,expectNumber,expectType,peek), state combinators (registerSymbol,registerType) - 5c.2
ExpressionParser.fs— precedence-based expression parsing: atom → application → unary → multiply → add, withNegateExpressionsupport - 5c.3
StatementParser.fs— typed/untyped params, let bindings, lib/open declarations, struct field/body parsers - 5c.4
RowParser.fs— row-level dispatch, let/func detection, function definition, mod/mod-body, struct chains, body recursion, file entry point - 5c.5
NegateExpressionAST variant — added to Expression union, handled in TypeInference, LambdaLifting, FsoWriter - 5c.6 Wire
Compiler.fs— replaceAstBuilder.rowToExpressionwithRowParser.parseFile, update CLI commands - 5c.7 Update all tests — switched from
AstBuildertoRowParser/TokenCombinatorsentry points - 5c.8 Delete
AstBuilder.fs— removed from fsproj, file deleted, dead code cleaned fromAst.fs - 5c.9 Folder restructure —
Tokenise/(Parsers, StructParser, Lexer) andParse/(combinators, parsers) as sibling dirs - 5c.10 Naming cleanup —
createAST→tokenise,rowReader→printRow,fakeTokenListOption→alwaysSome,isIndentifierChar→isIdentifierChar
ADR: ADR-0002 (phases 1–4: primitives, inference, unification, typed AST)
ADR-0002 phases 5–7 (sum types, pattern matching, generics) deferred to Epic 11.
- 6.1 Hindley-Milner type inference — Algorithm W in the frontend, infer types for all expressions
- 6.2 Type unification — unify type variables, detect mismatches, produce clear errors with source locations
- 6.3 Typed AST — replace
Inferredwith concrete types after inference, flow through to .fso - 6.4 Operator definition —
let (+) a b = ...syntax for user-defined operators, infix usage desugars to function call - 6.5 Precedence and associativity — builtin precedence table, user operators default to lowest precedence
- 6.6 Backend type-aware codegen — emit
fadd/fsubfor floats,add/subfor ints based on inferred types - 6.7 Type errors in backend — reject type mismatches with source locations from .fso
- 6.8 Struct type registration — register struct types in type environment, fields as typed records
- 6.9 Struct construction expression —
Point { x = 1, y = 2 }syntax, type-checked against definition - 6.10 Field access expression —
p.xsyntax, resolves to field index + type - 6.11 Backend struct codegen — bare minimum: LLVM named struct types, stack alloca, GEP for construction and field access. No heap/arena — just enough to prove structs work before memory model ADR
- 6.12 Tests — type inference roundtrips, mixed-type errors, struct construction and field access
Unresolved questions:
- user-declarable precedence (
infixl 6 +) or fixed table? - what operator symbols allowed? just math, or custom like
<|>,>>=?
- 7.1 Design syntax for type contracts (not "class" — explore: trait, protocol, contract, shape)
- 7.2 Design syntax for implementing contracts on types (explore: impl, extend, for, instance)
- 7.3 Standard contracts — Functor (<$>/map), Applicative (<*>), Monad (>>=)
- 7.4 Compiler resolves contract implementations at compile time (zero runtime cost)
- 7.5 Allow adding contracts to existing/third-party types after the fact
- 7.6 Operator symbols — any combination of symbols allowed, standard set predefined
- 7.7 Tests — contract resolution, operator dispatch, type errors
Unresolved questions:
- keyword for defining a contract? (trait, protocol, contract, shape, ...)
- keyword for implementing? (impl, extend, for, ...)
- should contracts support default implementations?
- associated types?
ADR-0004 defines immutable-first ownership, refcounting, reuse analysis, Ptr, Slice. ADR-0006 defines stack-first placement, heap promotion, arena allocators, struct layout. Both depend on a working type system (Epic 6) — the compiler needs concrete types to decide placement. Epic 6.14 (struct codegen) deliberately stops at stack alloca; this epic picks up from there.
- 8.1 Refine tasks from ADR-0004 and ADR-0006 — break down ownership semantics, allocation strategy, reuse analysis, Ptr/Slice into implementable tasks
ADR: ADR-0005
ADR-0005 defines Drop trait,
usebinding, auto-generated drop for structs, loan pattern. Depends on Epic 8 (memory model) — Drop triggers on refcount zero, needs ownership semantics in place. Depends on Epic 7 (type contracts) — Drop is itself a contract/trait.
- 9.1 Refine tasks from ADR-0005 — break down Drop trait,
usebinding, auto-drop generation, loan pattern into implementable tasks
ADR: ADR-0007
ADR-0007 defines String (UTF-8, SSO), fixed arrays, List, Slice, no implicit coercion. Depends on Epic 8 (memory model) — SSO and growable buffers need allocation strategy. Depends on Epic 6 (type system) — generic types like List need type parameters.
- 10.1 Refine tasks from ADR-0007 — break down String type, SSO, fixed arrays, List, Slice, explicit conversions into implementable tasks
ADR: ADR-0002 (phases 5–7: sum types, discriminated unions, pattern matching)
ADR-0002 phases 5–7 cover sum types (
type Option<T> = Some T | None), tuples, and pattern matching. Deferred from Epic 6 because sum types need the memory model (Epic 8) to decide tag+payload layout, and pattern matching benefits from having strings and collections (Epic 10) to match against.
- 11.1 Refine tasks from ADR-0002 phases 5–7 — break down sum types, tuples, discriminated unions, pattern matching, exhaustiveness checking into implementable tasks
ADR: ADR-0003
ADR-0003 defines M:N green thread scheduler, spawn keyword, yield points, user-space stacks, I/O integration. Most independent epic — no hard dependency on type system or memory model, but benefits from both. Placed last because the language is usable without async; the runtime is a large standalone effort.
- 12.1 Refine tasks from ADR-0003 — break down runtime library, scheduler, spawn keyword, yield insertion, context switching, I/O integration into implementable tasks
- User-defined infix operators —
let (|>) = pipeForwardpattern, pipe-forward as stdlib not syntax - Match expressions — pattern matching on values, destructuring
- String literals — parsing, type inference, emit
myproject/
├── furst.yaml
├── src/
│ └── main.fu # entry point (executables)
├── bin/ # final output (executable or .a)
└── build/ # intermediates (.fso, .o, .ll)
mycompany/
├── furst-workspace.yaml
├── services/
│ ├── api/
│ │ ├── furst.yaml # type: executable
│ │ └── src/main.fu
│ └── worker/
│ ├── furst.yaml # type: executable
│ └── src/main.fu
└── libs/
└── shared/
├── furst.yaml # type: library
└── src/lib.fu
# Executable project
name: myapi # project name (all project types)
version: 0.1.0
type: executable
entry: src/main.fu
targets:
- arch: x86_64
os: linux
dependencies:
- path: ../libs/shared # project reference# Library project
name: furst-collections # project name (used locally, e.g. output filenames)
version: 0.1.0
type: library
library:
name: Furst.Collections # fully qualified lib root (what consumers see/open)
sources:
- src/list.fu # → Furst.Collections.List (implicit)
- src/map.fu # → Furst.Collections.Map (implicit)
targets:
- arch: x86_64
os: linux