mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
Language: struct literals
struct Vec2 { x: u8, y: u8 }
var pos: Vec2 = Vec2 { x: 100, y: 50 }
on frame {
pos = Vec2 { x: pos.x + 1, y: pos.y }
}
- AST: new \`Expr::StructLiteral(name, fields, span)\` variant
- Parser: in expression position, \`Ident {\` enters struct-literal
mode when the new \`restrict_struct_literals\` flag is off.
\`if\`/\`while\`/\`for\` conditions set the flag so the \`{\` keeps
going to the following block. Condition contexts can still use
struct literals by parenthesizing them.
- Analyzer: validates that the struct type exists, each named field
belongs to it, and each field value has a compatible type.
- IR lowering: desugars \`var = StructLiteral { ... }\` (both in
assignments and variable initializers) into per-field StoreVar
operations against the analyzer-synthesized \`var.field\`
variables. No IR type for struct values is needed.
- AST codegen: no-op (legacy path).
- examples/structs_enums_for.ne now uses a struct literal for the
initial \`player\` state instead of per-field assignments.
https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
f17f1e7267
commit
c8ae433a7c
9 changed files with 221 additions and 13 deletions
|
|
@ -13,6 +13,11 @@ pub struct Parser {
|
|||
tokens: Vec<Token>,
|
||||
pos: usize,
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
/// When true, `parse_primary` refuses to consume an `Ident {`
|
||||
/// pattern as a struct literal — the `{` is reserved for the
|
||||
/// following `if` / `while` / `for` block. Struct literals in
|
||||
/// conditions must be parenthesized: `if x == (Foo { a: 1 })`.
|
||||
restrict_struct_literals: bool,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
|
|
@ -21,6 +26,7 @@ impl Parser {
|
|||
tokens,
|
||||
pos: 0,
|
||||
diagnostics: Vec::new(),
|
||||
restrict_struct_literals: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -945,7 +951,10 @@ impl Parser {
|
|||
fn parse_if(&mut self) -> Result<Statement, Diagnostic> {
|
||||
let start = self.current_span();
|
||||
self.expect(&TokenKind::KwIf)?;
|
||||
let saved = self.restrict_struct_literals;
|
||||
self.restrict_struct_literals = true;
|
||||
let condition = self.parse_expr()?;
|
||||
self.restrict_struct_literals = saved;
|
||||
let then_block = self.parse_block()?;
|
||||
|
||||
let mut else_ifs = Vec::new();
|
||||
|
|
@ -955,7 +964,9 @@ impl Parser {
|
|||
self.advance();
|
||||
if *self.peek() == TokenKind::KwIf {
|
||||
self.advance();
|
||||
self.restrict_struct_literals = true;
|
||||
let cond = self.parse_expr()?;
|
||||
self.restrict_struct_literals = saved;
|
||||
let block = self.parse_block()?;
|
||||
else_ifs.push((cond, block));
|
||||
} else {
|
||||
|
|
@ -972,7 +983,10 @@ impl Parser {
|
|||
fn parse_while(&mut self) -> Result<Statement, Diagnostic> {
|
||||
let start = self.current_span();
|
||||
self.expect(&TokenKind::KwWhile)?;
|
||||
let saved = self.restrict_struct_literals;
|
||||
self.restrict_struct_literals = true;
|
||||
let condition = self.parse_expr()?;
|
||||
self.restrict_struct_literals = saved;
|
||||
let body = self.parse_block()?;
|
||||
Ok(Statement::While(condition, body, start))
|
||||
}
|
||||
|
|
@ -989,9 +1003,12 @@ impl Parser {
|
|||
self.expect(&TokenKind::KwFor)?;
|
||||
let (var, _) = self.expect_ident()?;
|
||||
self.expect(&TokenKind::KwIn)?;
|
||||
let saved = self.restrict_struct_literals;
|
||||
self.restrict_struct_literals = true;
|
||||
let start_expr = self.parse_expr()?;
|
||||
self.expect(&TokenKind::DotDot)?;
|
||||
let end_expr = self.parse_expr()?;
|
||||
self.restrict_struct_literals = saved;
|
||||
let body = self.parse_block()?;
|
||||
Ok(Statement::For {
|
||||
var,
|
||||
|
|
@ -1565,6 +1582,38 @@ impl Parser {
|
|||
return Ok(Expr::FieldAccess(name, field, span));
|
||||
}
|
||||
|
||||
// Check for struct literal: `Name { field: expr, ... }`.
|
||||
// Disabled in condition contexts to keep parsing
|
||||
// unambiguous for `if`/`while`/`for`.
|
||||
if !self.restrict_struct_literals && *self.peek() == TokenKind::LBrace {
|
||||
self.advance();
|
||||
let mut fields = Vec::new();
|
||||
while *self.peek() != TokenKind::RBrace && *self.peek() != TokenKind::Eof {
|
||||
let (field_name, _) = self.expect_ident()?;
|
||||
self.expect(&TokenKind::Colon)?;
|
||||
// Struct literal field values can contain
|
||||
// their own nested struct literals, so we
|
||||
// temporarily allow them regardless of the
|
||||
// outer restriction.
|
||||
let saved = self.restrict_struct_literals;
|
||||
self.restrict_struct_literals = false;
|
||||
let value = self.parse_expr()?;
|
||||
self.restrict_struct_literals = saved;
|
||||
fields.push((field_name, value));
|
||||
if *self.peek() == TokenKind::Comma {
|
||||
self.advance();
|
||||
} else if *self.peek() != TokenKind::RBrace {
|
||||
return Err(Diagnostic::error(
|
||||
ErrorCode::E0201,
|
||||
"expected ',' or '}' in struct literal",
|
||||
self.current_span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.expect(&TokenKind::RBrace)?;
|
||||
return Ok(Expr::StructLiteral(name, fields, span));
|
||||
}
|
||||
|
||||
Ok(Expr::Ident(name, span))
|
||||
}
|
||||
TokenKind::LBracket => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue