1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# Chapter 29: Tech Debt Cleanup
English | [日本語](README.ja.md)
[Previous: Chapter 28b — Separate Compilation](../chapter28b/README.md)
This chapter makes the compiler easier to read and change while preserving
Chapter 28b's language and separate-compilation behavior. It starts by breaking
the parser into grammar-sized pieces. Further cleanup belongs here; earlier
chapters retain their teaching snapshots.
## Following the grammar
The old `Parser.java` had grown beyond 3,000 lines, mixing recursive descent,
name resolution, scope management, and IR construction. The
[`parser/` package](src/main/java/com/seaofnodes/simple/parser/README.md) separates
those responsibilities without introducing an AST: parsing still builds
Sea-of-Nodes IR directly.
`Parser` owns the source cursor, active scope, type registry, and common token
and IR helpers. Each component shares that state through `Parser P`.
`Statements`, `Expressions`, `Declarations`, `Types`, `Functions`, and `Structs`
follow the corresponding grammar productions. `References` handles name and
field resolution; `Allocations`, `Conversions`, and `ControlFlow` hold shared
graph-building operations. `Lexer` owns tokenization and source locations, and
`Var` records lexical bindings.
The [grammar and code map](docs/29-grammar.md) connects productions to methods.
Start with `Statements.parseStatement` or `Expressions.parseAssignment` and
follow the calls alongside it. Deferred parsing remains explicit: `for` updates
are parsed after their bodies, and declared constructors after field discovery.
The [source layout](src/main/java/com/seaofnodes/simple/README.md) describes the
other packages. `IterPeeps` now lives in `codegen/` beside the compilation passes.
## Checking constructor completion
`addReturn` merges field values across exit paths. After `parseBody` finishes,
`Structs.parseInitializer` checks the completed helper Returns; no separate exit
maps survive scope teardown. Required fields without an assignment on every
returning path retain `TOP` in private memory. They emit no Store, so numeric
conversions cannot hide the missing assignment.
The declaration initializer and user constructor have separate Returns. Check
the user constructor when present, or the declaration initializer for the default
constructor. The older same-named constructor binding can precede field
declarations; fields absent from its Return use the declaration initializer's
result. Nonreturning helpers require no completed object. Completion errors
point to the relevant helper's source location.
`ConstructorCompletionTest` covers partial numeric assignments, early returns,
nonreturning paths, declaration defaults, and fields declared after constructors.
## Further cleanup
Source privacy is deferred. Leading underscores retain the existing export
filtering behavior; this chapter does not yet enforce access restrictions on
source uses. The [roadmap](../docs/roadmap.md) retains that proposal and the other
language candidates.
## Build and test
From the repository root:
```sh
make lib tests release CHAPTERS=chapter29 CTAGS=
```
The suite includes the inherited compiler, evaluator, native execution,
serialization, and separate-compilation tests, plus parser regression tests for
deferred fragments. Historical test class names retain their original identities.
The [Chapter 28b library/client example](../chapter28b/README.md) also runs from
this chapter directory.
IntelliJ has a `chapter29` module and a **chapter29 tests** run configuration.
Build the native runtime and `sys` library with Make before debugging tests
that need them; see [the IntelliJ setup](../docs/intellij.md).
|