rott/rottlib/src/parser/grammar/declarations/struct_definition.rs
dkanus 588790b9b4 Refactor everything
Huge dump of refactored code. Still in the middle of the changes that
are to be squashed later in a one huge monster commit, because there is
no value in anything atomic here.
2026-04-05 20:32:11 +07:00

211 lines
8.4 KiB
Rust

//! Parsing of struct definitions for Fermented `UnrealScript`.
//!
//! ## C++ block handling
//!
//! The Fermented `UnrealScript` parser must support parsing several legacy
//! source files that contain `cpptext` or `cppstruct`. Our compiler does not
//! compile with C++ code and therefore does not need these blocks in
//! the resulting AST. We treat them the same as trivia and skip them.
//!
//! However, some related tokens are context-sensitive, so handling these
//! blocks in the general trivia-skipping path would complicate the separation
//! between the lexer and the parser.
//!
//! The resulting files will not be compiled, but they can still be used to
//! extract type information.
use crate::arena::ArenaVec;
use crate::ast::{
AstSpan, IdentifierToken, QualifiedIdentifierRef, StructDefRef, StructDefinition, StructField,
StructFieldRef, StructModifier, StructModifierKind, TypeSpecifierRef, VarEditorSpecifierRef,
VarModifier,
};
use crate::lexer::{Keyword, Token, TokenPosition};
use crate::parser::{ParseErrorKind, Parser, ResultRecoveryExt, SyncLevel};
#[derive(Debug)]
struct ParsedStructFieldPrefix<'src, 'arena> {
editor_specifiers: Option<ArenaVec<'arena, VarEditorSpecifierRef<'src, 'arena>>>,
declaration_modifiers: ArenaVec<'arena, VarModifier>,
type_specifier: TypeSpecifierRef<'src, 'arena>,
}
#[derive(Debug)]
enum StructBodyItemParseOutcome<'src, 'arena> {
Field(StructFieldRef<'src, 'arena>),
Skip,
Stop,
}
impl<'src, 'arena> Parser<'src, 'arena> {
/// Parses a `struct` definition after the `struct` keyword has been
/// consumed.
pub(crate) fn parse_struct_definition_tail(
&mut self,
struct_keyword_position: TokenPosition,
) -> StructDefRef<'src, 'arena> {
let modifiers = self.parse_struct_declaration_modifiers();
let (name, base_type_name) = self.parse_struct_name_base_and_open_brace();
let mut fields = self.arena.vec();
while let Some((next_token, next_position)) = self.peek_token_and_position()
&& next_token != Token::RightBrace
{
match self.parse_or_skip_struct_body_item() {
StructBodyItemParseOutcome::Field(new_field) => fields.push(new_field),
StructBodyItemParseOutcome::Skip => (),
StructBodyItemParseOutcome::Stop => break,
}
self.ensure_forward_progress(next_position);
}
self.expect(Token::RightBrace, ParseErrorKind::StructMissingRightBrace)
.widen_error_span_from(struct_keyword_position)
.report_error(self);
let span = AstSpan::range(
struct_keyword_position,
self.last_consumed_position_or_start(),
);
self.arena.alloc_node(
StructDefinition {
name,
base_type_name,
modifiers,
fields,
},
span,
)
}
/// Parses one item in a struct body or skips an unsupported one.
///
/// Returns [`StructBodyItemParseOutcome::Field`] for a successfully parsed
/// field, [`StructBodyItemParseOutcome::Skip`] when recovery allows parsing
/// to continue, and [`StructBodyItemParseOutcome::Stop`] when parsing
/// should stop at this level.
fn parse_or_skip_struct_body_item(&mut self) -> StructBodyItemParseOutcome<'src, 'arena> {
let Some((token, token_position)) = self.peek_token_and_position() else {
// This is the end of the file;
// it will be handled by a higher-level parser.
return StructBodyItemParseOutcome::Stop;
};
match token {
Token::Keyword(Keyword::CppText | Keyword::CppStruct) => {
self.advance();
if !self.eat(Token::CppBlock) {
self.report_error_here(ParseErrorKind::CppDirectiveMissingCppBlock);
self.recover_until(SyncLevel::Statement);
}
StructBodyItemParseOutcome::Skip
}
Token::Keyword(Keyword::Var) => {
self.advance();
self.parse_struct_field_tail(token_position)
}
_ => {
self.report_error_here(ParseErrorKind::StructBodyUnexpectedItem);
self.recover_until(SyncLevel::BlockBoundary);
StructBodyItemParseOutcome::Skip
}
}
}
/// Parses a struct field after the `var` keyword has been consumed.
///
/// Returns [`StructBodyItemParseOutcome::Skip`] if the field cannot be
/// parsed far enough to produce a usable AST node after recovery.
fn parse_struct_field_tail(
&mut self,
var_keyword_position: TokenPosition,
) -> StructBodyItemParseOutcome<'src, 'arena> {
let Some(field_prefix) = self.parse_struct_field_prefix() else {
return StructBodyItemParseOutcome::Skip;
};
let declarators = self.parse_variable_declarators();
if !self.eat(Token::Semicolon) {
self.report_error_here(ParseErrorKind::StructFieldMissingSemicolon);
self.recover_until(SyncLevel::BlockBoundary);
let _ = self.eat(Token::Semicolon);
}
if declarators.is_empty() {
return StructBodyItemParseOutcome::Skip;
}
let span = AstSpan::range(var_keyword_position, self.last_consumed_position_or_start());
StructBodyItemParseOutcome::Field(self.arena.alloc_node(
StructField {
type_specifier: field_prefix.type_specifier,
declaration_modifiers: field_prefix.declaration_modifiers,
editor_specifiers: field_prefix.editor_specifiers,
declarators,
},
span,
))
}
fn parse_struct_field_prefix(&mut self) -> Option<ParsedStructFieldPrefix<'src, 'arena>> {
let editor_specifiers = self.parse_var_editor_specifier_list();
let declaration_modifiers = self.parse_var_declaration_modifiers();
let type_specification = self
.parse_type_specifier()
.sync_error_until(self, SyncLevel::BlockBoundary)
.ok_or_report(self)?;
Some(ParsedStructFieldPrefix {
editor_specifiers,
declaration_modifiers,
type_specifier: type_specification,
})
}
/// Parses the struct name, optional base type, and opening brace.
///
/// Accepts anonymous structs that begin immediately with `{`.
fn parse_struct_name_base_and_open_brace(
&mut self,
) -> (
Option<IdentifierToken>,
Option<QualifiedIdentifierRef<'arena>>,
) {
if self.eat(Token::LeftBrace) {
return (None, None);
}
let name = self
.parse_identifier(ParseErrorKind::StructExpectedNameOrBrace)
.ok_or_report(self);
let base_type_name =
if let Some((Token::Keyword(Keyword::Extends), extends_keyword_position)) =
self.peek_token_and_position()
{
self.advance();
self.parse_qualified_identifier(ParseErrorKind::StructExpectedBaseName)
.widen_error_span_from(extends_keyword_position)
.ok_or_report(self)
} else {
None
};
self.expect(Token::LeftBrace, ParseErrorKind::StructMissingLeftBrace)
.report_error(self);
(name, base_type_name)
}
fn parse_struct_declaration_modifiers(&mut self) -> ArenaVec<'arena, StructModifier> {
let mut modifiers = self.arena.vec();
while let Some((next_keyword, next_keyword_position)) = self.peek_keyword_and_position() {
let next_modifier_kind = match next_keyword {
Keyword::Native => StructModifierKind::Native,
Keyword::Init => StructModifierKind::Init,
Keyword::Export => StructModifierKind::Export,
Keyword::NoExport => StructModifierKind::NoExport,
Keyword::Transient => StructModifierKind::Transient,
Keyword::Deprecated => StructModifierKind::Deprecated,
Keyword::Long => StructModifierKind::Long,
_ => break,
};
modifiers.push(StructModifier {
kind: next_modifier_kind,
position: next_keyword_position,
});
self.advance();
}
modifiers
}
}