use super::*;
mod class;
mod control_flow;
mod functions;
mod locals;
impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
'name: 'ast,
{
pub(in crate::parser) fn parse_statement(&mut self) -> Result<ParsedStatement<'ast>> {
let location = self.current_location();
match self.current {
Token::Attribute(_) | Token::AttributeOpen => self.parse_attribute_statement(location),
Token::Reserved(R::Break) => {
self.advance();
if !self.in_loop() {
let message_index =
self.report_parse_error(self.loop_control_error(location, "break"));
let statement = self.arena.alloc_statement_node(StatementUnit::new(
StatementTag::Break,
location,
false,
));
let error = self.arena.alloc_statement_node(StatementError::new(
location,
false,
self.empty_expression_slice(),
self.arena.alloc_slice_copy(&[statement]),
message_index,
));
return Ok(self.parsed_statement_from_node(error, None));
}
let statement = self.arena.alloc_statement_node(StatementUnit::new(
StatementTag::Break,
location,
false,
));
Ok(self.parsed_statement_from_node(statement, None))
}
Token::Reserved(R::Do) => self.parse_do(location),
Token::Reserved(R::For) => self.parse_for(location),
Token::Reserved(R::Function) => {
self.parse_function_declaration(location, ParsedAttributes::default())
}
Token::Reserved(R::If) => self
.parse_if(location)
.map(|statement| self.parsed_statement_from_node(statement, None)),
Token::Reserved(R::Local) => self.parse_local(location, ParsedAttributes::default()),
Token::Reserved(R::Repeat) => self.parse_repeat(location),
Token::Reserved(R::Return) => {
self.advance();
let (expressions, comma_positions) = self.parse_return_values()?;
let end = expressions
.last()
.map(|expression| expression.location.end)
.unwrap_or(location.end);
let statement = self.arena.alloc_statement_node(StatementReturn::new(
Location::new(location.begin, end),
false,
self.arena.alloc_slice_copy(&expressions),
));
let statement = self.parsed_statement_from_node(
statement,
if self.cst.enabled() {
Some(CstNode::StatReturn(CstStatReturn { comma_positions }))
} else {
None
},
);
if flags::LuauExportValueSyntax.get() && self.contexts.functions.is_empty() {
if !self.declared_export_bindings.is_empty() {
self.report_parse_error(self.error_at(
statement.statement.location(),
"Exporting values is not compatible with top-level return (export/return conflict)",
));
}
self.has_module_return = true;
}
Ok(statement)
}
Token::Reserved(R::While) => self.parse_while(location),
Token::Ident(_) => {
let expression = self.parse_primary_expression_statement()?;
self.parse_expression_statement_tail(expression)
}
Token::LeftParen => {
let expression = self.parse_primary_expression_statement()?;
self.parse_expression_statement_tail(expression)
}
_ => {
let expression = self.parse_primary_expression_statement()?;
let parsed = self.parse_expression_statement_tail(expression)?;
if self.current_location() == location {
self.advance();
}
Ok(parsed)
}
}
}
pub(in crate::parser) fn parse_attribute_statement(
&mut self,
location: Location,
) -> Result<ParsedStatement<'ast>> {
let attributes = match self.parse_attributes() {
Ok(attributes) => attributes,
Err(error) => {
let error_location = error.location;
return Ok(self.report_statement_error(error_location, error));
}
};
match self.current {
Token::Reserved(R::Function) => self.parse_function_declaration(location, attributes),
Token::Reserved(R::Local) => self.parse_local(location, attributes),
Token::Ident(name) if name == "const" => self.parse_const(location, attributes),
Token::Ident(name) if name == "export" && flags::LuauExportValueSyntax.get() => {
let keyword = self.current_token_location();
self.advance();
self.parse_export_value_after_keyword(location, keyword, attributes)
}
Token::Ident(name) if name == "declare" && self.options.allow_declaration_syntax() => {
let expression = self.parse_primary_expression_statement()?;
self.parse_declaration_after_keyword(expression.location, attributes.attributes)
.map(|statement| self.parsed_statement_from_node(statement, None))
}
_ => {
let error_location = self.current_location();
let error = self.attribute_statement_error();
Ok(self.report_statement_error(error_location, error))
}
}
}
pub(in crate::parser) fn parse_expression_statement_tail(
&mut self,
expression: Expression<'ast>,
) -> Result<ParsedStatement<'ast>> {
if expression.is_call_statement() {
let statement = self.arena.alloc_statement_node(StatementExpression::new(
expression.location,
false,
expression,
));
return Ok(self.parsed_statement_from_node(statement, None));
}
if matches!(self.current, Token::Comma | Token::Equal) {
return self.parse_assignment(expression);
}
if let Some(op) = self.current.compound_assignment_op() {
let var = self.check_assignable_expression(expression);
let op_position = self.current_position();
self.advance();
let value = self.parse_expression()?;
let location = Location::new(expression.location.begin, value.location.end);
let cst = if self.cst.enabled() {
Some(CstNode::StatCompoundAssign(CstStatCompoundAssign {
op: op_position,
}))
} else {
None
};
let statement = self
.arena
.alloc_statement_node(StatementCompoundAssign::new(
location, false, var, op, value,
));
return Ok(self.parsed_statement_from_node(statement, cst));
}
if let Some(identifier) = self.statement_identifier(expression) {
return self.parse_contextual_statement(identifier, expression);
}
let (location, expression, message_index) = if let ExpressionKind::Error {
expressions,
message_index,
} = expression.kind()
{
let expression = self.arena.alloc_expression_error_direct(
expression.location,
expressions,
message_index,
);
(expression.location, expression, message_index)
} else {
let expression_location = expression.location;
let message_index = self.report_parse_error(ParseError::new(
expression_location,
"Incomplete statement: expected assignment or a function call",
));
(expression_location, expression, message_index)
};
let expressions = self.arena.alloc_slice_copy(&[expression]);
let statements = self.empty_statement_slice();
let statement = self.arena.alloc_statement_node(StatementError::new(
location,
false,
expressions,
statements,
message_index,
));
Ok(self.parsed_statement_from_node(statement, None))
}
pub(in crate::parser) fn statement_identifier(
&self,
expression: Expression<'ast>,
) -> Option<AstName<'ast>> {
match expression.kind() {
ExpressionKind::Global(name) => Some(name),
ExpressionKind::Local { local, .. } => Some(local.name),
_ => None,
}
}
pub(in crate::parser) fn parse_contextual_statement(
&mut self,
identifier: AstName<'ast>,
expression: Expression<'ast>,
) -> Result<ParsedStatement<'ast>> {
if identifier == "type" {
return self.parse_type_alias_after_keyword(
expression.location,
false,
expression.location.begin,
);
}
if identifier == "export" && matches!(self.current, Token::Ident(name) if name == "type") {
let type_keyword = self.current_position();
self.advance();
return self.parse_type_alias_after_keyword(expression.location, true, type_keyword);
}
if identifier == "export"
&& flags::LuauExportValueSyntax.get()
&& (matches!(self.current, Token::Reserved(R::Local | R::Function))
|| matches!(self.current, Token::Ident(name) if name == "const")
|| (flags::DebugLuauUserDefinedClasses.get()
&& matches!(self.current, Token::Ident(name) if name == "class")))
{
return self.parse_export_value_after_keyword(
expression.location,
expression.location,
ParsedAttributes::default(),
);
}
if identifier == "continue" {
return Ok(self.parse_continue_statement(expression.location));
}
if identifier == "const" {
return self.parse_local_after_keyword(
expression.location,
ParsedAttributes::default(),
true,
false,
expression.location,
);
}
if flags::DebugLuauUserDefinedClasses.get() && identifier == "class" {
return self
.parse_class_statement_after_keyword(expression.location, false)
.map(|parsed| self.parsed_statement_from_node(parsed.statement, None));
}
if self.options.allow_declaration_syntax() && identifier == "declare" {
return self
.parse_declaration_after_keyword(expression.location, Vec::new())
.map(|statement| self.parsed_statement_from_node(statement, None));
}
let expression_location = expression.location;
let message_index = self.report_parse_error(ParseError::new(
expression_location,
"Incomplete statement: expected assignment or a function call",
));
Ok(self.parsed_statement_from_node(
self.arena.alloc_statement_node(StatementError::new(
expression_location,
false,
self.arena.alloc_slice_copy(&[expression]),
self.empty_statement_slice(),
message_index,
)),
None,
))
}
pub(in crate::parser) fn parse_continue_statement(
&mut self,
location: Location,
) -> ParsedStatement<'ast> {
if !self.in_loop() {
let message_index =
self.report_parse_error(self.loop_control_error(location, "continue"));
let statement = self.arena.alloc_statement_node(StatementUnit::new(
StatementTag::Continue,
location,
false,
));
let error = self.arena.alloc_statement_node(StatementError::new(
location,
false,
self.empty_expression_slice(),
self.arena.alloc_slice_copy(&[statement]),
message_index,
));
return self.parsed_statement_from_node(error, None);
}
let statement = self.arena.alloc_statement_node(StatementUnit::new(
StatementTag::Continue,
location,
false,
));
self.parsed_statement_from_node(statement, None)
}
pub(in crate::parser) fn report_statement_error(
&mut self,
location: Location,
error: ParseError,
) -> ParsedStatement<'ast> {
let message_index = self.report_parse_error(error);
let statement = self.error_statement(location, message_index);
self.parsed_statement_from_node(statement, None)
}
pub(in crate::parser) fn parse_assignment(
&mut self,
first: Expression<'ast>,
) -> Result<ParsedStatement<'ast>> {
let mut vars = self.temp_expressions();
vars.push_back(self.check_assignable_expression(first));
let mut variable_commas = self.temp_positions();
while self.current.kind() == TokenKind::Comma {
if self.cst.enabled() {
variable_commas.push_back(self.current_position());
}
self.advance();
let target = self.parse_primary_expression_statement()?;
vars.push_back(self.check_assignable_expression(target));
}
let equals = if self.expect_and_consume(Token::Equal, "assignment") {
self.previous_token_location().begin
} else {
Position::missing()
};
let mut values = self.temp_expressions_aux();
let mut value_commas = self.temp_positions();
self.parse_expression_list_into(&mut values, &mut value_commas)?;
let end = values
.as_slice()
.last()
.map(|value| value.location.end)
.unwrap_or(first.location.end);
let location = Location::new(first.location.begin, end);
let statement = self.arena.alloc_statement_node(StatementAssign::new(
location,
false,
self.arena.alloc_slice_copy(vars.as_slice()),
self.arena.alloc_slice_copy(values.as_slice()),
));
Ok(self.parsed_statement_from_node(
statement,
if self.cst.enabled() {
Some(CstNode::StatAssign(CstStatAssign {
variable_commas: self.arena.alloc_slice_copy(variable_commas.as_slice()),
equals,
value_commas: self.arena.alloc_slice_copy(value_commas.as_slice()),
}))
} else {
None
},
))
}
}