mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +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:
parent
9c102cb68e
commit
225d40cea5
12 changed files with 464 additions and 3 deletions
|
|
@ -117,6 +117,7 @@ impl Parser {
|
|||
let mut globals = Vec::new();
|
||||
let mut constants = Vec::new();
|
||||
let mut enums: Vec<EnumDecl> = Vec::new();
|
||||
let mut structs: Vec<StructDecl> = Vec::new();
|
||||
let mut functions = Vec::new();
|
||||
let mut states = Vec::new();
|
||||
let mut sprites = Vec::new();
|
||||
|
|
@ -147,6 +148,9 @@ impl Parser {
|
|||
TokenKind::KwEnum => {
|
||||
enums.push(self.parse_enum_decl()?);
|
||||
}
|
||||
TokenKind::KwStruct => {
|
||||
structs.push(self.parse_struct_decl()?);
|
||||
}
|
||||
TokenKind::KwState => {
|
||||
states.push(self.parse_state_decl()?);
|
||||
}
|
||||
|
|
@ -221,6 +225,7 @@ impl Parser {
|
|||
globals,
|
||||
constants,
|
||||
enums,
|
||||
structs,
|
||||
functions,
|
||||
states,
|
||||
sprites,
|
||||
|
|
@ -232,6 +237,40 @@ impl Parser {
|
|||
})
|
||||
}
|
||||
|
||||
fn parse_struct_decl(&mut self) -> Result<StructDecl, Diagnostic> {
|
||||
let start = self.current_span();
|
||||
self.expect(&TokenKind::KwStruct)?;
|
||||
let (name, _) = self.expect_ident()?;
|
||||
self.expect(&TokenKind::LBrace)?;
|
||||
let mut fields = Vec::new();
|
||||
while *self.peek() != TokenKind::RBrace && *self.peek() != TokenKind::Eof {
|
||||
let field_span = self.current_span();
|
||||
let (field_name, _) = self.expect_ident()?;
|
||||
self.expect(&TokenKind::Colon)?;
|
||||
let field_type = self.parse_type()?;
|
||||
fields.push(StructField {
|
||||
name: field_name,
|
||||
field_type,
|
||||
span: field_span,
|
||||
});
|
||||
if *self.peek() == TokenKind::Comma {
|
||||
self.advance();
|
||||
} else if *self.peek() != TokenKind::RBrace {
|
||||
return Err(Diagnostic::error(
|
||||
ErrorCode::E0201,
|
||||
"expected ',' or '}' in struct body",
|
||||
self.current_span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.expect(&TokenKind::RBrace)?;
|
||||
Ok(StructDecl {
|
||||
name,
|
||||
fields,
|
||||
span: Span::new(start.file_id, start.start, self.current_span().end),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_enum_decl(&mut self) -> Result<EnumDecl, Diagnostic> {
|
||||
let start = self.current_span();
|
||||
self.expect(&TokenKind::KwEnum)?;
|
||||
|
|
@ -1121,6 +1160,19 @@ impl Parser {
|
|||
start,
|
||||
))
|
||||
}
|
||||
TokenKind::Dot => {
|
||||
// Field assignment: name.field = value
|
||||
self.advance();
|
||||
let (field, _) = self.expect_ident()?;
|
||||
let op = self.parse_assign_op()?;
|
||||
let value = self.parse_expr()?;
|
||||
Ok(Statement::Assign(
|
||||
LValue::Field(name, field),
|
||||
op,
|
||||
value,
|
||||
start,
|
||||
))
|
||||
}
|
||||
TokenKind::LParen => {
|
||||
// Function call
|
||||
self.advance();
|
||||
|
|
@ -1207,6 +1259,12 @@ impl Parser {
|
|||
self.advance();
|
||||
NesType::Bool
|
||||
}
|
||||
TokenKind::Ident(name) => {
|
||||
// User-declared struct types are referenced by name.
|
||||
// The analyzer validates that the name exists.
|
||||
self.advance();
|
||||
NesType::Struct(name)
|
||||
}
|
||||
_ => {
|
||||
return Err(Diagnostic::error(
|
||||
ErrorCode::E0201,
|
||||
|
|
@ -1464,6 +1522,13 @@ impl Parser {
|
|||
return Ok(Expr::Call(name, args, span));
|
||||
}
|
||||
|
||||
// Check for field access: `name.field`
|
||||
if *self.peek() == TokenKind::Dot {
|
||||
self.advance();
|
||||
let (field, _) = self.expect_ident()?;
|
||||
return Ok(Expr::FieldAccess(name, field, span));
|
||||
}
|
||||
|
||||
Ok(Expr::Ident(name, span))
|
||||
}
|
||||
TokenKind::LBracket => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue