mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
Struct field types beyond the v1 scalar set (`u8`, `i8`, `u16`,
`bool`) used to error out with `E0201: struct fields must be
u8/i8/u16/bool`. The size accumulator already handled them
correctly — what was missing was: (1) the analyzer side that
synthesizes per-leaf symbols and allocations for nested structs
plus a single array-typed symbol for array fields, (2) the
parser's chained-field-access path, and (3) the IR-lowering
recursion through nested struct literal initializers and array
literal field values.
The synthetic-variable model carries through unchanged: a
`var p: Player` where `Player { pos: Vec2, hp: u8, inv: u8[4] }`
and `Vec2 { x: u8, y: u8 }` produces flat allocations for
`p.pos.x`, `p.pos.y`, `p.hp`, and `p.inv`, plus an intermediate
`p.pos` Struct symbol so dotted-name lookups still resolve. Array
fields get a single allocation with the array type so the
existing `Expr::ArrayIndex` lowering path handles `p.inv[i]`
without changes. Array-of-structs is still rejected with E0201
because the synthetic model can't index per-element layouts
without further codegen work.
The parser change is the only structural move: `parse_primary`
and `parse_assign_or_call` now loop the dot chain into a single
joined identifier so `p.pos.x` becomes `FieldAccess("p.pos", "x")`
and `p.inv[0]` becomes `ArrayIndex("p.inv", 0)`. The downstream
analyzer and IR lowering use the same `format!("{name}.{field}")`
join they already used for one-level access — no plumbing
changes required.
Includes a new `examples/nested_structs.ne` that exercises both
features end-to-end with two `Hero` instances carrying nested
positions and inventory arrays. The reproducibility tripwire
ROM is committed alongside it and the emulator harness has a
matching pair of golden files.
https://claude.ai/code/session_01KEczoNUX3WmcFLfq6iAQxB
81 lines
2.1 KiB
Text
81 lines
2.1 KiB
Text
// Demonstrates struct types, enums, and for loops.
|
|
//
|
|
// A small "platformer" scaffold: the player has a position, velocity,
|
|
// and animation state held in a struct, enums describe directions and
|
|
// animation phases, and a for loop iterates over an array of stationary
|
|
// enemies each frame.
|
|
//
|
|
// Compile with the default IR codegen:
|
|
// nescript build examples/structs_enums_for.ne
|
|
|
|
game "StructsDemo" { mapper: NROM }
|
|
|
|
// Named constants for the four cardinal directions. Each variant is
|
|
// a u8 value equal to its declaration order (Up=0, Down=1, etc.).
|
|
enum Direction { Up, Down, Left, Right }
|
|
|
|
enum AnimFrame { Idle, Run1, Run2 }
|
|
|
|
// A struct bundles related state into a single variable.
|
|
// See examples/nested_structs.ne for nested-struct and array-field
|
|
// fields; this example sticks to flat scalar fields for simplicity.
|
|
struct Player {
|
|
x: u8,
|
|
y: u8,
|
|
vx: u8,
|
|
vy: u8,
|
|
facing: u8, // Direction enum value
|
|
frame: u8, // AnimFrame enum value
|
|
alive: bool,
|
|
}
|
|
|
|
// Struct literal initializer in declaration.
|
|
var player: Player = Player {
|
|
x: 120,
|
|
y: 112,
|
|
vx: 0,
|
|
vy: 0,
|
|
facing: Down,
|
|
frame: Idle,
|
|
alive: true,
|
|
}
|
|
|
|
// A small fixed-size array of enemy x-positions. In a real game this
|
|
// would be an array of structs once those are supported.
|
|
var enemies_x: u8[4] = [32, 80, 160, 208]
|
|
var enemy_y: u8 = 100
|
|
|
|
const SPEED: u8 = 1
|
|
|
|
on frame {
|
|
// Read controls and update position. Velocities are u8 so we
|
|
// treat them as signed by adding/subtracting SPEED.
|
|
if button.left {
|
|
player.x -= SPEED
|
|
player.facing = Left
|
|
}
|
|
if button.right {
|
|
player.x += SPEED
|
|
player.facing = Right
|
|
}
|
|
if button.up {
|
|
player.y -= SPEED
|
|
player.facing = Up
|
|
}
|
|
if button.down {
|
|
player.y += SPEED
|
|
player.facing = Down
|
|
}
|
|
|
|
// Draw the player — the IR codegen allocates the OAM slot.
|
|
draw Smiley at: (player.x, player.y)
|
|
|
|
// And each enemy, using a for loop over the array.
|
|
for i in 0..4 {
|
|
draw Smiley at: (enemies_x[i], enemy_y)
|
|
}
|
|
|
|
wait_frame
|
|
}
|
|
|
|
start Main
|