298 lines
11 KiB
Rust
298 lines
11 KiB
Rust
//! Parsing of callable definitions for Fermented `UnrealScript`
|
|
//! (functions, events, delegates, operators).
|
|
|
|
use crate::arena::ArenaVec;
|
|
|
|
use crate::ast::{
|
|
CallableDefinition, CallableDefinitionRef, CallableKind, CallableModifier,
|
|
CallableModifierKind, CallableName, IdentifierToken, InfixOperator, InfixOperatorName,
|
|
ParameterRef, PostfixOperator, PostfixOperatorName, PrefixOperator, PrefixOperatorName,
|
|
TypeSpecifierRef,
|
|
};
|
|
use crate::lexer::{Keyword, Token, TokenPosition, TokenSpan};
|
|
use crate::parser::{
|
|
ParseError, ParseErrorKind, ParseResult, Parser, ResultRecoveryExt, SyncLevel,
|
|
recovery::RecoveryFallback,
|
|
};
|
|
|
|
/// Temporary parsed representation of a callable header without its body.
|
|
#[derive(Debug)]
|
|
pub(super) struct ParsedCallableHeader<'src, 'arena> {
|
|
pub start_position: TokenPosition,
|
|
pub modifiers: crate::arena::ArenaVec<'arena, CallableModifier>,
|
|
pub kind: CallableKind,
|
|
pub return_type_specifier: Option<TypeSpecifierRef<'src, 'arena>>,
|
|
pub name: CallableName,
|
|
pub parameters: crate::arena::ArenaVec<'arena, ParameterRef<'src, 'arena>>,
|
|
}
|
|
|
|
impl<'src, 'arena> RecoveryFallback<'src, 'arena> for ParsedCallableHeader<'src, 'arena> {
|
|
fn fallback_value(parser: &Parser<'src, 'arena>, error: &ParseError) -> Self {
|
|
let fallback_position = error.covered_span.start;
|
|
ParsedCallableHeader {
|
|
start_position: fallback_position,
|
|
modifiers: parser.arena.vec(),
|
|
kind: CallableKind::Function,
|
|
return_type_specifier: None,
|
|
name: CallableName::Identifier(IdentifierToken(fallback_position)),
|
|
parameters: parser.arena.vec(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'src, 'arena> Parser<'src, 'arena> {
|
|
/// Parses a callable definition.
|
|
///
|
|
/// Assumes [`Parser::is_callable_header_ahead`] has already confirmed that
|
|
/// a callable declaration begins at the current position. This affects
|
|
/// the diagnostics produced for malformed input.
|
|
#[must_use]
|
|
pub(crate) fn parse_callable_definition(&mut self) -> CallableDefinitionRef<'src, 'arena> {
|
|
let header = self.parse_callable_header().unwrap_or_fallback(self);
|
|
|
|
let body = if self.eat(Token::LeftBrace) {
|
|
Some(self.parse_braced_block_statements_tail(self.last_consumed_position_or_start()))
|
|
} else {
|
|
self.expect(
|
|
Token::Semicolon,
|
|
ParseErrorKind::CallableMissingBodyOrSemicolon,
|
|
)
|
|
.report_error(self);
|
|
None
|
|
};
|
|
|
|
let span = TokenSpan::range(
|
|
header.start_position,
|
|
self.last_consumed_position_or_start(),
|
|
);
|
|
|
|
self.arena.alloc_node(
|
|
CallableDefinition {
|
|
name: header.name,
|
|
kind: header.kind,
|
|
return_type_specifier: header.return_type_specifier,
|
|
modifiers: header.modifiers,
|
|
parameters: header.parameters,
|
|
body,
|
|
},
|
|
span,
|
|
)
|
|
}
|
|
|
|
/// Parses a callable header without the body.
|
|
fn parse_callable_header(
|
|
&mut self,
|
|
) -> ParseResult<'src, 'arena, ParsedCallableHeader<'src, 'arena>> {
|
|
let start_position = self.require_position(ParseErrorKind::CallableExpectedHeader)?;
|
|
let mut modifiers = self.arena.vec();
|
|
self.collect_callable_modifiers(&mut modifiers);
|
|
let kind = self.parse_callable_kind()?;
|
|
self.collect_callable_modifiers(&mut modifiers);
|
|
|
|
// `(` cannot appear inside a return type in this grammar,
|
|
// so seeing it here means the callable has no return type specifier.
|
|
let return_type_specifier = match self.peek_token_at(1) {
|
|
Some(Token::LeftParenthesis) => None,
|
|
_ => Some(self.parse_type_specifier()?),
|
|
};
|
|
let name = self.parse_callable_name(kind)?;
|
|
|
|
self.expect(
|
|
Token::LeftParenthesis,
|
|
ParseErrorKind::CallableParamsMissingOpeningParenthesis,
|
|
)
|
|
.report_error(self);
|
|
let parameters = self.parse_parameter_list();
|
|
self.expect(
|
|
Token::RightParenthesis,
|
|
ParseErrorKind::CallableParamsMissingClosingParenthesis,
|
|
)
|
|
.sync_error_at(self, SyncLevel::CloseParenthesis)
|
|
.report_error(self);
|
|
|
|
Ok(ParsedCallableHeader {
|
|
start_position,
|
|
modifiers,
|
|
kind,
|
|
return_type_specifier,
|
|
name,
|
|
parameters,
|
|
})
|
|
}
|
|
|
|
fn parse_callable_kind(&mut self) -> ParseResult<'src, 'arena, CallableKind> {
|
|
if let Some(keyword) = self.peek_keyword() {
|
|
// Handle this separately because only infix operators can carry
|
|
// an optional precedence and cannot, therefore, be handled by
|
|
// a simple converter.
|
|
if keyword == Keyword::Operator {
|
|
self.advance();
|
|
let precedence = self.parse_optional_parenthesized_integer(
|
|
ParseErrorKind::CallableOperatorInvalidPrecedence,
|
|
);
|
|
return Ok(CallableKind::InfixOperator(precedence));
|
|
}
|
|
if let Ok(kind) = CallableKind::try_from(keyword) {
|
|
self.advance();
|
|
return Ok(kind);
|
|
}
|
|
}
|
|
Err(self.make_error_at_last_consumed(ParseErrorKind::CallableExpectedKind))
|
|
}
|
|
|
|
fn parse_callable_name(
|
|
&mut self,
|
|
kind: CallableKind,
|
|
) -> ParseResult<'src, 'arena, CallableName> {
|
|
match kind {
|
|
CallableKind::Function | CallableKind::Event | CallableKind::Delegate => self
|
|
.parse_identifier(ParseErrorKind::CallableNameNotIdentifier)
|
|
.map(CallableName::Identifier),
|
|
CallableKind::PrefixOperator => {
|
|
let (token, operator_position) = self.require_token_and_position(
|
|
ParseErrorKind::CallablePrefixOperatorInvalidSymbol,
|
|
)?;
|
|
let operator = PrefixOperator::try_from(token).map_err(|()| {
|
|
self.make_error_at_last_consumed(ParseErrorKind::CallablePrefixOperatorInvalidSymbol)
|
|
})?;
|
|
self.advance();
|
|
Ok(CallableName::PrefixOperator(PrefixOperatorName {
|
|
kind: operator,
|
|
position: operator_position,
|
|
}))
|
|
}
|
|
CallableKind::InfixOperator(_) => {
|
|
let (token, operator_position) = self.require_token_and_position(
|
|
ParseErrorKind::CallableInfixOperatorInvalidSymbol,
|
|
)?;
|
|
let operator = InfixOperator::try_from(token).map_err(|()| {
|
|
self.make_error_at_last_consumed(ParseErrorKind::CallableInfixOperatorInvalidSymbol)
|
|
})?;
|
|
self.advance();
|
|
Ok(CallableName::InfixOperator(InfixOperatorName {
|
|
kind: operator,
|
|
position: operator_position,
|
|
}))
|
|
}
|
|
CallableKind::PostfixOperator => {
|
|
let (token, operator_position) = self.require_token_and_position(
|
|
ParseErrorKind::CallablePostfixOperatorInvalidSymbol,
|
|
)?;
|
|
let operator = PostfixOperator::try_from(token).map_err(|()| {
|
|
self.make_error_at_last_consumed(ParseErrorKind::CallablePostfixOperatorInvalidSymbol)
|
|
})?;
|
|
self.advance();
|
|
Ok(CallableName::PostfixOperator(PostfixOperatorName {
|
|
kind: operator,
|
|
position: operator_position,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Parses an uninterrupted sequence of function modifiers into
|
|
/// given vector.
|
|
pub(crate) fn collect_callable_modifiers(
|
|
&mut self,
|
|
modifiers: &mut ArenaVec<'arena, CallableModifier>,
|
|
) {
|
|
while let Some(next_mod) = self.parse_function_modifier() {
|
|
modifiers.push(next_mod);
|
|
}
|
|
}
|
|
|
|
fn parse_function_modifier(&mut self) -> Option<CallableModifier> {
|
|
let (keyword, start) = self.peek_keyword_and_position()?;
|
|
|
|
let kind = match keyword {
|
|
Keyword::Native => {
|
|
self.advance();
|
|
let native_id = self.parse_optional_parenthesized_integer(
|
|
ParseErrorKind::NativeModifierIdNotIntegerLiteral,
|
|
);
|
|
CallableModifierKind::Native(native_id)
|
|
}
|
|
Keyword::Config => {
|
|
self.advance();
|
|
let ident = self
|
|
.parse_required_parenthesized_identifier(
|
|
ParseErrorKind::ParenthesisedIdentifierMissingClosingParenthesis,
|
|
ParseErrorKind::ParenthesisedIdentifierMissingClosingParenthesis,
|
|
)
|
|
.unwrap_or(IdentifierToken(start));
|
|
CallableModifierKind::Config(ident)
|
|
}
|
|
_ => {
|
|
let simple = CallableModifierKind::try_from(keyword).ok()?;
|
|
// Only advance after confirming it is the modifier
|
|
self.advance();
|
|
simple
|
|
}
|
|
};
|
|
|
|
let span = TokenSpan::range(start, self.last_consumed_position_or_start());
|
|
Some(CallableModifier { kind, span })
|
|
}
|
|
|
|
fn parse_optional_parenthesized_integer(&mut self, close_err: ParseErrorKind) -> Option<u128> {
|
|
if !self.eat(Token::LeftParenthesis) {
|
|
return None;
|
|
}
|
|
|
|
let value = match self.peek_token_and_lexeme() {
|
|
Some((Token::IntegerLiteral, lex)) => {
|
|
self.advance();
|
|
self.decode_integer_literal(lex).ok_or_report(self)
|
|
}
|
|
Some(_) => {
|
|
self.report_error_here(ParseErrorKind::OperatorPrecedenceNotIntegerLiteral);
|
|
self.advance();
|
|
None
|
|
}
|
|
None => {
|
|
self.report_error_here(ParseErrorKind::OperatorPrecedenceNotIntegerLiteral);
|
|
None
|
|
}
|
|
};
|
|
|
|
self.expect(Token::RightParenthesis, close_err)
|
|
.sync_error_at(self, SyncLevel::CloseParenthesis)
|
|
.report_error(self);
|
|
|
|
value
|
|
}
|
|
|
|
fn parse_required_parenthesized_identifier(
|
|
&mut self,
|
|
close_err: ParseErrorKind,
|
|
ident_err: ParseErrorKind,
|
|
) -> Option<IdentifierToken> {
|
|
if !self.eat(Token::LeftParenthesis) {
|
|
self.report_error_here(ident_err);
|
|
return None;
|
|
}
|
|
|
|
let ident = match self.peek_token_lexeme_and_position() {
|
|
Some((tok, _, pos)) if tok.is_valid_identifier_name() => {
|
|
self.advance();
|
|
Some(IdentifierToken(pos))
|
|
}
|
|
Some(_) => {
|
|
self.report_error_here(ident_err);
|
|
self.advance();
|
|
None
|
|
}
|
|
None => {
|
|
self.report_error_here(ident_err);
|
|
None
|
|
}
|
|
};
|
|
|
|
self.expect(Token::RightParenthesis, close_err)
|
|
.sync_error_at(self, SyncLevel::CloseParenthesis)
|
|
.report_error(self);
|
|
|
|
ident
|
|
}
|
|
}
|