v5 Parsing: modules, imports, and member access

Work through examples, try the reasoning yourself, then put it into practice.

View Slides

Before 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 read

v5 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.

Join Discord