1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-17 06:02:01 +00:00

Language: struct types

Adds composite \`struct\` types with field access:

    struct Vec2 { x: u8, y: u8 }

    var pos: Vec2
    pos.x = 100
    pos.y = pos.x + 5

- Lexer: \`struct\` keyword
- AST: \`StructDecl\` with \`StructField\` list; \`NesType::Struct(name)\`
  for struct-typed variable declarations; \`Expr::FieldAccess\` and
  \`LValue::Field\` for reads/writes
- Parser: top-level \`struct Name { field: type, ... }\` (optional
  trailing comma) and \`ident.field\` syntax in both expression and
  lvalue position
- Analyzer: \`register_struct\` computes contiguous field offsets
  (no padding) and stores them in \`struct_layouts\`. Struct variables
  synthesize a \`VarAllocation\` per field under the name
  \`"struct_var.field"\`, and \`Expr::FieldAccess\` / \`LValue::Field\`
  resolve against those. Unknown struct types and unknown fields
  emit E0201.
- IR lowering + AST codegen: treat struct field access as ordinary
  variable access against the synthetic per-field symbols. No new IR
  ops are needed.

v1 structs only support primitive fields (u8/i8/bool). Nested structs,
u16 fields, and array fields are not yet allowed.

https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
Claude 2026-04-12 16:18:05 +00:00
parent 9c102cb68e
commit 225d40cea5
No known key found for this signature in database
12 changed files with 464 additions and 3 deletions

View file

@ -298,6 +298,68 @@ fn analyze_return_wrong_type() {
);
}
#[test]
fn analyze_struct_variable_allocates_fields() {
let result = analyze_ok(
r#"
game "Test" { mapper: NROM }
struct Vec2 { x: u8, y: u8 }
var pos: Vec2
on frame {
pos.x = 10
pos.y = pos.x
}
start Main
"#,
);
// The analyzer should synthesize pos.x and pos.y as separate
// variables with consecutive addresses.
let px = result
.var_allocations
.iter()
.find(|a| a.name == "pos.x")
.expect("pos.x should be allocated");
let py = result
.var_allocations
.iter()
.find(|a| a.name == "pos.y")
.expect("pos.y should be allocated");
assert_eq!(py.address, px.address + 1);
}
#[test]
fn analyze_struct_unknown_field_errors() {
let errors = analyze_errors(
r#"
game "Test" { mapper: NROM }
struct Vec2 { x: u8, y: u8 }
var pos: Vec2
on frame { pos.z = 5 }
start Main
"#,
);
assert!(
errors.contains(&ErrorCode::E0201),
"unknown field should emit E0201: {errors:?}"
);
}
#[test]
fn analyze_unknown_struct_type_errors() {
let errors = analyze_errors(
r#"
game "Test" { mapper: NROM }
var pos: NoSuchStruct
on frame { wait_frame }
start Main
"#,
);
assert!(
errors.contains(&ErrorCode::E0201),
"unknown struct type should emit E0201: {errors:?}"
);
}
#[test]
fn analyze_enum_variants_as_constants() {
let result = analyze_ok(