v3 Parsing: Postfix, Maps, and Arrays

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

View Slides

Before you start

  • Parse v2 statements and understand v3 callable/container values.

What you will practice

  • Build repeated postfix wrappers in left-to-right order.
  • Separate assignable syntax from runtime index validity.
  • Distinguish map expressions from statement blocks by context.
In this lesson

Lesson

3 min read

v3 Parsing: Postfix, Maps, and Arrays

v3 changes both statement placement and expression shape. fn is now a statement, so nested declarations parse inside blocks. A call stores its callee expression in ExprCall.Left, not a name in Value. Assignment stores an identifier or index expression in Stmt.Target.

Worked reconstruction

Start with primary factory in factory(2)[0](5):

Consumed suffix New root Prior expression retained as
(2) Call callee Left = Ident(factory)
[0] Index target Left = previous Call
(5) Call callee Left = previous Index

A postfix loop keeps wrapping the accumulated expression until the next token is not ( or [. All suffixes bind more tightly than unary and binary operators. Thus -a[0] is Neg(Index(a,0)), not Index(Neg(a),0).

Assignment is a second decision

Parse an expression first. If the next token is =, require its root to be an identifier or index, parse the RHS, and form a statement. a[0]=7; has an index target. a+b=7; and f()=7; are parser errors. Conversely 3[0]=7; has an index-shaped target and can parse; runtime will reject the non-container base. Shape validation does not prove the target is operationally valid.

Delimiter context

At statement entry, { opens a block. In expression context, such as after let m=, { opens a map literal whose pairs need colons. Arrays and maps can be empty, and pair/element expressions reuse the full expression parser. let m={"x":[2,3]}; nests map, array, and declaration terminators; each parser owns its delimiter and returns at the first following token. This grammar does not accept a trailing comma before ] or }.

A bare map at statement start is ambiguous with a block under these dispatch rules. Parenthesizing it, as ({"x":2});, explicitly enters expression context. The language remains dynamically typed; do not infer key or element types in this parser.

Debugging lab

A parser accepts factory(2) then returns before [0](5), and represents a call by putting factory’s text in Value. Identify the two v2 assumptions that no longer hold.

Compare your diagnosis after attempting the lab

Postfix syntax must loop through every suffix. The callee is now an arbitrary expression retained in Left; extracting only its name loses nested call/index structure.

Transfer task

Draw the tree for build()[pick(1)] = source()[0];, labeling target, index, and RHS. Change only the target root to make it syntactically invalid, then create a different target that parses but is invalid at runtime.

Carry forward

Carry the postfix tree and explicit assignment target into v3 analysis. Store one chain with three suffixes as a regression against accidental one-suffix parsing.

Build program-parser-v3. 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