v3 Parsing: Postfix, Maps, and Arrays
Lesson, slides, and applied problem sets.
View SlidesLesson
v3 Parsing: postfix calls, indexing, and literals
v3 upgrades expression parsing from flat primaries to compound expressions that can call and index at any point.
Problem this module solves
- represent nested functions and higher-order expressions
- build call chains like
f()(1) - access indexed values
a[i]and nested forms - parse collection literals as first-class primary expressions
Core grammar
program -> stmt* EOF
stmt -> varDecl
| ifStmt
| whileStmt
| returnStmt
| block
| fnDecl
| assignOrExpr
fnDecl -> "fn" IDENT "(" params? ")" block
params -> IDENT ("," IDENT)*
varDecl -> ("let"|"var"|"const") IDENT ("=" expr)? ";"
assignOrExpr -> expr ("=" expr)? ";"
block -> "{" stmt* "}"
expr -> equality
postfix -> primary ("(" args? ")" | "[" expr "]")*
args -> expr ("," expr)*
primary -> NUMBER | STRING | TRUE | FALSE | NIL
| IDENT
| "(" expr ")"
| array
| map
array -> "[" (expr ("," expr)*)? "]"
map -> "{" (pair ("," pair)*)? "}"
pair -> expr ":" expr
Parsing flow and structure
1) parseStmt dispatch is token-driven.
let/var/const=> declarationif/while=> statement forms with blocks{=> nested blockfn=> function declaration- everything else =>
assignOrExpr
2) parseExpr starts at lowest-precedence (parseEquality) and recurses down.
3) parsePostfix keeps a loop and repeatedly wraps the previous expression with:
- call node when
(is seen, - index node when
[is seen.
This naturally creates left-to-right chains.
Nested function declarations and scoping effects
Function declarations are ordinary statements and can nest inside blocks:
- parse block as statement list,
- each inner function has its own parameter and body parse flow,
- no special “definition pass” is needed at parse stage.
L-value rule in v3
Assignment target validation remains explicit:
- valid targets: identifier and index expressions
- invalid target (e.g.,
"x" = 1,a + b = c,f() = 1) should fail withinvalid assignment target
This is enforced by a dedicated check (isLValue).
Literal forms and delimiters
- arrays: accept both empty and populated forms
- maps: allow empty and comma-separated key/value pairs
- map parsing requires
:between key and value - missing
}/]and:errors should be surfaced where they occur
Common pitfall:
ifandwhilealways parse a block after condition, never a bare expression.
Deep practice checkpoints (almost solved)
1) Postfix chaining
Input: f(1)[idx](x);
Expected:
- parse as nested postfix chain (call -> index -> call)
2) Nested function declaration
Input: fn outer(x) { fn inner(y) { return x + y; } return inner(x); }
Expected:
outerbody contains a function statement (inner) and a return statement
3) Map shape errors
Input: {1 2}
Expected:
- parse error
expected ':' after map key
4) Empty collection literals
[]and{}parse as array/map literals with empty element lists.
5) Assignment target restriction
Input: f(1) = 2;
Expected:
- error
invalid assignment target
6) Block vs literal distinction
{}in statement position is parsed as empty block only when reached throughparseStmt.{}in expression context (after operator,=RHS, etc.) is parsed throughparsePrimaryas empty map.
7) Precedence check
Input: 1 + 2 * 3 == 7;
Expected:
*grouped before+- root comparison is
==
8) Return shape
return 1 + 2; and return;
Expected:
- both parse when semicolon is present; no trailing expression in
return;
9) Call argument optionality
f(); and f(1, 2);
Expected:
- both are valid call expressions inside expression statements
Module Items
Program Parser v3