feat: add record spread syntax { ...base, field: val }
Adds spread operator for records, allowing concise record updates:
let p2 = { ...p, x: 5.0 }
Changes across the full pipeline:
- Lexer: new DotDotDot (...) token
- AST: optional spread field on Record variant
- Parser: detect ... at start of record expression
- Typechecker: merge spread record fields with explicit overrides
- Interpreter: evaluate spread, overlay explicit fields
- JS backend: emit native JS spread syntax
- C backend: copy spread into temp, assign overrides
- Formatter, linter, LSP, symbol table: propagate spread
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2208,6 +2208,11 @@ impl Parser {
|
||||
}));
|
||||
}
|
||||
|
||||
// Check for record spread: { ...expr, field: val }
|
||||
if matches!(self.peek_kind(), TokenKind::DotDotDot) {
|
||||
return self.parse_record_expr_rest(start);
|
||||
}
|
||||
|
||||
// Check if it's a record (ident: expr) or block
|
||||
if matches!(self.peek_kind(), TokenKind::Ident(_)) {
|
||||
let lookahead = self.tokens.get(self.pos + 1).map(|t| &t.kind);
|
||||
@@ -2222,6 +2227,20 @@ impl Parser {
|
||||
|
||||
fn parse_record_expr_rest(&mut self, start: Span) -> Result<Expr, ParseError> {
|
||||
let mut fields = Vec::new();
|
||||
let mut spread = None;
|
||||
|
||||
// Check for spread: { ...expr, ... }
|
||||
if self.check(TokenKind::DotDotDot) {
|
||||
self.advance(); // consume ...
|
||||
let spread_expr = self.parse_expr()?;
|
||||
spread = Some(Box::new(spread_expr));
|
||||
|
||||
self.skip_newlines();
|
||||
if self.check(TokenKind::Comma) {
|
||||
self.advance();
|
||||
}
|
||||
self.skip_newlines();
|
||||
}
|
||||
|
||||
while !self.check(TokenKind::RBrace) {
|
||||
let name = self.parse_ident()?;
|
||||
@@ -2238,7 +2257,11 @@ impl Parser {
|
||||
|
||||
self.expect(TokenKind::RBrace)?;
|
||||
let span = start.merge(self.previous_span());
|
||||
Ok(Expr::Record { fields, span })
|
||||
Ok(Expr::Record {
|
||||
spread,
|
||||
fields,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_block_rest(&mut self, start: Span) -> Result<Expr, ParseError> {
|
||||
|
||||
Reference in New Issue
Block a user