This commit is contained in:
Dustin Swan 2026-02-13 17:31:08 -07:00
parent 1961ac6249
commit 164f752338
No known key found for this signature in database
GPG key ID: 30D46587E2100467
6 changed files with 95 additions and 122 deletions

View file

@ -202,6 +202,22 @@ export class Parser {
return expr;
}
private parseCommaSeparated<T>(closeToken: Token['kind'], parseItem: () => T): T[] {
const items: T[] = [];
let first = true;
while (this.current().kind !== closeToken) {
if (!first) {
this.expect('comma');
if (this.current().kind === closeToken) break; // trailing commas
}
first = false;
items.push(parseItem());
}
return items;
}
private parseMatch(expr: AST): AST {
const token = this.current();
const cases: MatchCase[] = [];
@ -288,23 +304,16 @@ export class Parser {
// Record
if (token.kind === 'open-brace') {
this.advance();
const fields: { [key: string]: Pattern } = {};
let first = true;
while (this.current().kind !== 'close-brace') {
if (!first) {
this.expect('comma');
if (this.current().kind === 'close-brace') break; // trailing commas
}
first = false;
const items = this.parseCommaSeparated('close-brace', () => {
const keyToken = this.expect('ident');
const key = (keyToken as { value: string }).value;
this.expect('equals');
fields[key] = this.parsePattern();
}
return { key, pattern: this.parsePattern() };
});
this.expect('close-brace');
const fields: { [key: string]: Pattern } = {};
for (const item of items) fields[item.key] = item.pattern;
return { kind: 'record', fields };
}
@ -429,23 +438,17 @@ export class Parser {
if (this.current().kind === 'open-brace') {
// Record update
this.advance();
const updates: { [key: string]: AST } = {};
let first = true;
while (this.current().kind !== 'close-brace') {
if (!first) {
this.expect('comma');
if (this.current().kind === 'close-brace') break; // trailing commas
}
first = false;
const items = this.parseCommaSeparated('close-brace', () => {
const keyToken = this.expect('ident');
const key = (keyToken as { value: string }).value;
this.expect('equals');
updates[key] = this.parseExpression();
}
return { key, value: this.parseExpression() };
});
this.expect('close-brace');
const updates: { [key: string]: AST } = {};
for (const item of items) updates[item.key] = item.value;
expr = { kind: 'record-update', record: expr, updates, ...this.getPos(token) }
} else {
@ -475,26 +478,15 @@ export class Parser {
if (token.kind === 'open-bracket') {
this.advance();
const items: AST[] = [];
let first = true;
while (this.current().kind !== 'close-bracket') {
if (!first) {
this.expect('comma');
if (this.current().kind === 'close-bracket') break; // trailing commas
}
first = false;
const items = this.parseCommaSeparated('close-bracket', () => {
// Spread
if (this.current().kind === 'dot-dot-dot') {
const spreadToken = this.current();
this.advance();
const expr = this.parseExpression();
items.push({ kind: 'list-spread', spread: expr, ...this.getPos(spreadToken) })
} else {
items.push(this.parseExpression());
return { kind: 'list-spread' as const, spread: this.parseExpression(), ...this.getPos(spreadToken) };
}
}
return this.parseExpression();
});
this.expect('close-bracket');
return { kind: 'list', elements: items, ...this.getPos(token) };
@ -502,28 +494,16 @@ export class Parser {
if (token.kind === 'open-brace') {
this.advance();
const entries: Array<{ kind: 'field', key: string, value: AST } | { kind: 'spread', expr: AST }> = [];
let first = true;
while (this.current().kind !== 'close-brace') {
if (!first) {
this.expect('comma');
if (this.current().kind === 'close-brace') break; // trailing commas
}
first = false;
const entries = this.parseCommaSeparated('close-brace', () => {
if (this.current().kind === 'dot-dot-dot') {
this.advance();
const expr = this.parseExpression();
entries.push({ kind: 'spread', expr });
} else {
const keyToken = this.expect('ident');
const key = (keyToken as { value: string }).value;
this.expect('equals');
entries.push({ kind: 'field', key, value: this.parseExpression() });
return { kind: 'spread' as const, expr: this.parseExpression() };
}
}
const keyToken = this.expect('ident');
const key = (keyToken as { value: string }).value;
this.expect('equals');
return { kind: 'field' as const, key, value: this.parseExpression() };
});
this.expect('close-brace');
return { kind: 'record', entries, ...this.getPos(token) };