mirror of
https://github.com/imjasonh/nescript
synced 2026-07-14 11:36:40 +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
|
|
@ -321,8 +321,19 @@ impl LoweringContext {
|
|||
});
|
||||
}
|
||||
if let Some(init) = &var.init {
|
||||
let val = self.lower_expr(init);
|
||||
self.emit(IrOp::StoreVar(var_id, val));
|
||||
// Struct literal initializers expand to per-field
|
||||
// stores on the synthetic field variables.
|
||||
if let Expr::StructLiteral(_, fields, _) = init {
|
||||
for (fname, fexpr) in fields {
|
||||
let full = format!("{}.{fname}", var.name);
|
||||
let fvid = self.get_or_create_var(&full);
|
||||
let val = self.lower_expr(fexpr);
|
||||
self.emit(IrOp::StoreVar(fvid, val));
|
||||
}
|
||||
} else {
|
||||
let val = self.lower_expr(init);
|
||||
self.emit(IrOp::StoreVar(var_id, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::Assign(lvalue, op, expr, _) => {
|
||||
|
|
@ -425,6 +436,21 @@ impl LoweringContext {
|
|||
}
|
||||
|
||||
fn lower_assign(&mut self, lvalue: &LValue, op: AssignOp, expr: &Expr) {
|
||||
// Special case: `var = StructLiteral { ... }` expands to
|
||||
// per-field stores against the analyzer-synthesized field
|
||||
// variables. This avoids needing struct values as IR temps.
|
||||
if let (LValue::Var(name), AssignOp::Assign, Expr::StructLiteral(_, fields, _)) =
|
||||
(lvalue, op, expr)
|
||||
{
|
||||
for (fname, fexpr) in fields {
|
||||
let full = format!("{name}.{fname}");
|
||||
let field_var = self.get_or_create_var(&full);
|
||||
let val = self.lower_expr(fexpr);
|
||||
self.emit(IrOp::StoreVar(field_var, val));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
match lvalue {
|
||||
LValue::Var(name) => {
|
||||
let var_id = self.get_or_create_var(name);
|
||||
|
|
@ -757,6 +783,16 @@ impl LoweringContext {
|
|||
self.emit(IrOp::LoadImm(t, 0));
|
||||
t
|
||||
}
|
||||
Expr::StructLiteral(_, _, _) => {
|
||||
// Struct literals are only supported as the right
|
||||
// hand side of a plain assignment (see lower_assign).
|
||||
// Falling through here means the literal was used in
|
||||
// an expression context the lowering can't handle;
|
||||
// emit zero so the build still produces a ROM.
|
||||
let t = self.fresh_temp();
|
||||
self.emit(IrOp::LoadImm(t, 0));
|
||||
t
|
||||
}
|
||||
Expr::Cast(inner, _, _) => {
|
||||
// For now, just evaluate the inner expression (truncation/extension is a no-op on 8-bit)
|
||||
self.lower_expr(inner)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue