v5 Parsing: modules, imports, and member access
Work through examples, try the reasoning yourself, then put it into practice.
View SlidesBefore you start
- Parse v4 statements and repeated postfix expressions.
- Distinguish module identity, import alias, and exported binding.
What you will practice
- Build Program.Modules and choose parse mode once.
- Preserve member/call/index postfix structure and export flags.
- Reject invalid export forms without performing module resolution.
In this lesson
Lesson
3 min readv5 Parsing: modules, imports, and member access
Program now holds Modules. If the first token is module, parse only explicit
module declarations. Otherwise parse ordinary statements into an implicit main
module. The choice is made once: a later module token does not switch an
implicit-main program into explicit mode. Import/export forms belong inside
explicit module bodies.
Worked reconstruction
Parse module main { import util as u; let x=u.make()[0]; }:
| Representation | Recorded fields |
|---|---|
| Program | one Module named main |
| Import | Name=util, Alias=u |
| variable x | initializer Index(Call(Member(Ident(u),make)),0) |
The parser accepts this shape without proving util exists; the analyzer checks
that later. Dot wraps the accumulated postfix expression just like calls and
indexes. For m.get(1).x[2], the wrappers are Member, Call, Member, Index in
source order. Some resulting member bases may later be rejected by module
semantics even though their syntax is valid.
Export declarations and assignment targets
export fn and export let/var/const mark the resulting statement Exported.
export if and export import are syntax errors. Export does not create a
separate runtime statement kind around the declaration.
Assignments still accept identifier or index roots only. u.x=3; is rejected
because Member is not an l-value in this grammar. u.x[0]=3; has Index at the
root and can parse; semantic/runtime rules still decide whether the target is
meaningful. Do not use assignment acceptance as proof of mutability or module
visibility.
Practical construction order
First parse one explicit empty module, then imports, exports, and regular statements. Reuse the v4 statement parser inside the module rather than reimplementing loop syntax. Extend the postfix loop with dot+identifier and keep existing calls/indexes composable. Finally test implicit main and mixed-mode rejection. A correct tree preserves source module order because subsequent dependency validation relies on it.
Debugging lab
A parser produces Call(Ident("u.make")) for u.make() and accepts export while(true){}. What structure and grammar boundary were lost?
Compare your diagnosis after attempting the lab
Member access must be a Member node with base u and member make, wrapped by Call; joining text prevents separate alias/export validation. Export is restricted to function and variable declarations, so exporting a while statement must fail parsing.
Transfer task
Draw ASTs for u.f()[1] and u.f[1]() and explain which value each calls. Construct an implicit-main program and then append an explicit module declaration; predict the fixed-mode parsing failure.
Carry forward
Carry ordered Modules, Imports, Exported flags, and Member nodes into v5 analysis. Keep unresolved-module syntax tests distinct from module-graph validity tests.
Build program-parser-v5. Keep a before/after trace of one failing test. Use the checkpoint to diagnose specific skills; passing it does not replace implementing and testing the artifact.
Practice sequence
Use the order below for a guided path, or choose an item to revisit. A pass records a successful attempt; try a fresh example later to check what you can reconstruct.