use super::{
parser_structs::{ParserResult, ParserState},
punctuated::{Pair, Punctuated},
span::ContainedSpan,
Expression, FunctionBody, Parameter,
};
#[cfg(any(feature = "cfxlua", feature = "luau"))]
use ast::Var;
use crate::{
ast,
node::Node,
tokenizer::{Symbol, Token, TokenKind, TokenReference, TokenType},
};
#[cfg(feature = "luau")]
use crate::tokenizer::InterpolatedStringKind;
#[cfg(feature = "luau")]
static PARSE_NAME_ERROR: &str = "%error-id%";
#[cfg(feature = "luau")]
fn error_token() -> TokenReference {
TokenReference::new(
Vec::new(),
Token::new(TokenType::Identifier {
identifier: PARSE_NAME_ERROR.into(),
}),
Vec::new(),
)
}
pub fn parse_block(state: &mut ParserState) -> ParserResult<ast::Block> {
let mut stmts = Vec::new();
loop {
match parse_stmt(state) {
ParserResult::Value(StmtVariant::Stmt(stmt)) => {
let semicolon = state.consume_if(Symbol::Semicolon);
stmts.push((stmt, semicolon));
}
ParserResult::Value(StmtVariant::LastStmt(last_stmt)) => {
let semicolon = state.consume_if(Symbol::Semicolon);
let last_stmt = Some((last_stmt, semicolon));
return ParserResult::Value(ast::Block { stmts, last_stmt });
}
ParserResult::NotFound => break,
ParserResult::LexerMoved => {
if stmts.is_empty() {
return ParserResult::LexerMoved;
} else {
break;
}
}
}
}
let last_stmt = match parse_last_stmt(state) {
ParserResult::Value(stmt) => Some(stmt),
ParserResult::LexerMoved | ParserResult::NotFound => None,
};
ParserResult::Value(ast::Block { stmts, last_stmt })
}
fn expect_block_with_end(
state: &mut ParserState,
name: &str,
start_for_errors: &TokenReference,
) -> Result<(ast::Block, TokenReference), ()> {
let block = match parse_block(state) {
ParserResult::Value(block) => block,
ParserResult::NotFound => unreachable!("parse_block should always return a value"),
ParserResult::LexerMoved => return Err(()),
};
let (start, end) = if let Some(last_stmt) = block.last_stmt() {
let mut tokens = last_stmt.tokens();
let start = tokens.next().unwrap();
let end = tokens.next_back().unwrap_or(start);
(start, end)
} else if let Some(the_last_of_the_stmts) = block.stmts().last() {
let mut tokens = the_last_of_the_stmts.tokens();
let start = tokens.next().unwrap();
let end = tokens.next_back().unwrap_or(start);
(start, end)
} else {
(start_for_errors, start_for_errors)
};
let Some(end_token) = state.require_with_reference_range(
Symbol::End,
|| format!("expected `end` to close {} block", name),
start,
end,
) else {
return Ok((block, TokenReference::basic_symbol("end")));
};
Ok((block, end_token))
}
enum StmtVariant {
Stmt(ast::Stmt),
#[allow(unused)]
LastStmt(ast::LastStmt),
}
#[cfg(any(feature = "luau", feature = "cfxlua"))]
fn parse_compound_assignment(state: &mut ParserState, var: Var) -> ParserResult<StmtVariant> {
let compound_operator = state.consume().unwrap();
let ParserResult::Value(expr) = parse_expression(state) else {
state.token_error(compound_operator, "expected expression to set to");
return ParserResult::LexerMoved;
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::CompoundAssignment(
ast::CompoundAssignment {
lhs: var,
compound_operator: ast::CompoundOp::from_token(compound_operator),
rhs: expr,
},
)))
}
fn parse_stmt(state: &mut ParserState) -> ParserResult<StmtVariant> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
match current_token.token_type() {
TokenType::Symbol {
symbol: Symbol::Local,
} => {
let local_token = state.consume().unwrap();
let next_token = match state.current() {
Ok(token) => token,
Err(()) => return ParserResult::LexerMoved,
};
match next_token.token_type() {
TokenType::Identifier { .. } => ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::LocalAssignment(match expect_local_assignment(state, local_token) {
Ok(local_assignment) => local_assignment,
Err(()) => return ParserResult::LexerMoved,
}),
)),
TokenType::Symbol {
symbol: Symbol::Function,
} => {
let function_token = state.consume().unwrap();
let local_function =
match expect_local_function_declaration(state, local_token, function_token)
{
Ok(local_function) => local_function,
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::LocalFunction(local_function)))
}
_ => {
state.token_error(
next_token.clone(),
"expected either a variable name or `function`",
);
ParserResult::LexerMoved
}
}
}
TokenType::Symbol {
symbol: Symbol::For,
} => {
let for_token = state.consume().unwrap();
ParserResult::Value(StmtVariant::Stmt(match expect_for_stmt(state, for_token) {
Ok(for_stmt) => for_stmt,
Err(()) => return ParserResult::LexerMoved,
}))
}
TokenType::Symbol { symbol: Symbol::Do } => {
let do_token = state.consume().unwrap();
let (block, end_token) = match expect_block_with_end(state, "do", &do_token) {
Ok(block) => block,
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::Do(ast::Do {
do_token,
block,
end_token,
})))
}
TokenType::Symbol { symbol: Symbol::If } => {
let if_token = state.consume().unwrap();
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::If(
match expect_if_stmt(state, if_token) {
Ok(if_stmt) => if_stmt,
Err(()) => return ParserResult::LexerMoved,
},
)))
}
TokenType::Symbol {
symbol: Symbol::Function,
} => {
let function_token = state.consume().unwrap();
let function_declaration = match expect_function_declaration(state, function_token) {
Ok(function_declaration) => function_declaration,
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::FunctionDeclaration(
function_declaration,
)))
}
TokenType::Symbol {
symbol: Symbol::Repeat,
} => {
let repeat_token = state.consume().unwrap();
ParserResult::Value(StmtVariant::Stmt(
match expect_repeat_stmt(state, repeat_token) {
Ok(repeat_stmt) => repeat_stmt,
Err(()) => return ParserResult::LexerMoved,
},
))
}
TokenType::Symbol {
symbol: Symbol::While,
} => {
let while_token = state.consume().unwrap();
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::While(
match expect_while_stmt(state, while_token) {
Ok(while_stmt) => while_stmt,
Err(()) => return ParserResult::LexerMoved,
},
)))
}
TokenType::Symbol {
symbol: Symbol::LeftParen,
}
| TokenType::Identifier { .. } => {
let (prefix, suffixes) = try_parser!(parse_prefix_and_suffixes(state)).unwrap();
let var = match suffixes.last() {
Some(ast::Suffix::Call(_)) => {
return ParserResult::Value(StmtVariant::Stmt(ast::Stmt::FunctionCall(
ast::FunctionCall { prefix, suffixes },
)));
}
Some(ast::Suffix::Index(_)) => {
ast::Var::Expression(Box::new(ast::VarExpression { prefix, suffixes }))
}
#[cfg(feature = "luau")]
Some(ast::Suffix::TypeInstantiation(_)) => {
ast::Var::Expression(Box::new(ast::VarExpression { prefix, suffixes }))
}
None => match prefix {
ast::Prefix::Name(name) => ast::Var::Name(name),
prefix @ ast::Prefix::Expression(_) => {
ast::Var::Expression(Box::new(ast::VarExpression { prefix, suffixes }))
}
},
};
match state.current() {
#[cfg(feature = "luau")]
Ok(token)
if state.lua_version().has_luau()
&& (token.is_symbol(Symbol::PlusEqual)
|| token.is_symbol(Symbol::MinusEqual)
|| token.is_symbol(Symbol::StarEqual)
|| token.is_symbol(Symbol::SlashEqual)
|| token.is_symbol(Symbol::DoubleSlashEqual)
|| token.is_symbol(Symbol::PercentEqual)
|| token.is_symbol(Symbol::CaretEqual)
|| token.is_symbol(Symbol::TwoDotsEqual)) =>
{
return parse_compound_assignment(state, var);
}
#[cfg(feature = "cfxlua")]
Ok(token)
if state.lua_version().has_cfxlua()
&& (token.is_symbol(Symbol::PlusEqual)
|| token.is_symbol(Symbol::MinusEqual)
|| token.is_symbol(Symbol::StarEqual)
|| token.is_symbol(Symbol::SlashEqual)
|| token.is_symbol(Symbol::CaretEqual)
|| token.is_symbol(Symbol::DoubleLessThanEqual)
|| token.is_symbol(Symbol::DoubleGreaterThanEqual)
|| token.is_symbol(Symbol::AmpersandEqual)
|| token.is_symbol(Symbol::PipeEqual)) =>
{
return parse_compound_assignment(state, var);
}
Ok(token) if token.is_symbol(Symbol::Comma) || token.is_symbol(Symbol::Equal) => {}
Ok(token) => {
#[cfg(feature = "luau")]
if state.lua_version().has_luau() {
if let ast::Var::Name(token) = var {
match token.token_type() {
TokenType::Identifier { identifier }
if identifier.as_str() == "export" =>
{
let export_token = token;
let type_token = match state.current() {
Ok(token) if matches!(token.token_type(), TokenType::Identifier { identifier } if identifier.as_str() == "type") => {
state.consume().unwrap()
}
Ok(token) => {
state.token_error_ranged(
token.clone(),
"expected `type` after `export`",
&export_token,
&token.clone(),
);
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
};
if let Some(function_token) = state.consume_if(Symbol::Function)
{
return ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::ExportedTypeFunction(
ast::ExportedTypeFunction {
export_token,
type_function: match expect_type_function(
state,
type_token,
function_token,
) {
Ok(type_function) => type_function,
Err(()) => return ParserResult::LexerMoved,
},
},
),
));
}
return ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::ExportedTypeDeclaration(
ast::ExportedTypeDeclaration {
export_token,
type_declaration: match expect_type_declaration(
state, type_token,
) {
Ok(type_declaration) => type_declaration,
Err(()) => return ParserResult::LexerMoved,
},
},
),
));
}
TokenType::Identifier { identifier }
if identifier.as_str() == "type" =>
{
let type_token = token;
if let Some(function_token) = state.consume_if(Symbol::Function)
{
return ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::TypeFunction(
match expect_type_function(
state,
type_token,
function_token,
) {
Ok(type_function) => type_function,
Err(()) => return ParserResult::LexerMoved,
},
),
));
}
return ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::TypeDeclaration(
match expect_type_declaration(state, type_token) {
Ok(type_declaration) => type_declaration,
Err(()) => return ParserResult::LexerMoved,
},
),
));
}
TokenType::Identifier { identifier }
if identifier.as_str() == "continue" =>
{
let continue_token = token;
return ParserResult::Value(StmtVariant::LastStmt(
ast::LastStmt::Continue(continue_token),
));
}
TokenType::Identifier { identifier }
if identifier.as_str() == "const" =>
{
let const_token = token;
let next_token_type = match state.current() {
Ok(next) => next.token_type().clone(),
Err(()) => return ParserResult::LexerMoved,
};
match next_token_type {
TokenType::Identifier { .. } => {
return ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::ConstAssignment(
match expect_const_assignment(
state,
const_token,
) {
Ok(ca) => ca,
Err(()) => return ParserResult::LexerMoved,
},
),
));
}
TokenType::Symbol {
symbol: Symbol::Function,
} => {
let function_token = state.consume().unwrap();
let const_function =
match expect_const_function_declaration(
state,
const_token,
function_token,
) {
Ok(cf) => cf,
Err(()) => return ParserResult::LexerMoved,
};
return ParserResult::Value(StmtVariant::Stmt(
ast::Stmt::ConstFunction(const_function),
));
}
_ => {
if let Ok(t) = state.current() {
state.token_error(
t.clone(),
"expected a variable name or `function` after `const`",
);
}
return ParserResult::LexerMoved;
}
}
}
_ => (),
}
}
}
state.token_error(
token.clone(),
"unexpected expression when looking for a statement",
);
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
};
let mut var_list = Punctuated::new();
var_list.push(Pair::End(var));
loop {
let next_comma = match state.current() {
Ok(token) if token.is_symbol(Symbol::Comma) => state.consume().unwrap(),
Ok(_) => break,
Err(()) => return ParserResult::LexerMoved,
};
let (next_prefix, next_suffixes) = match parse_prefix_and_suffixes(state) {
ParserResult::Value((prefix, suffixes)) => (prefix, suffixes),
ParserResult::LexerMoved => {
break;
}
ParserResult::NotFound => {
state.token_error(next_comma, "expected another variable");
break;
}
};
match next_suffixes.last() {
Some(ast::Suffix::Call(call)) => {
state.token_error(
call.tokens().next_back().unwrap().clone(),
"can't assign to the result of a call",
);
break;
}
Some(ast::Suffix::Index(_)) => {
var_list.push_punctuated(
ast::Var::Expression(Box::new(ast::VarExpression {
prefix: next_prefix,
suffixes: next_suffixes,
})),
next_comma,
);
}
#[cfg(feature = "luau")]
Some(ast::Suffix::TypeInstantiation(_)) => {
var_list.push_punctuated(
ast::Var::Expression(Box::new(ast::VarExpression {
prefix: next_prefix,
suffixes: next_suffixes,
})),
next_comma,
);
}
None => match next_prefix {
ast::Prefix::Name(name) => {
var_list.push_punctuated(ast::Var::Name(name), next_comma);
}
prefix @ ast::Prefix::Expression(_) => var_list.push_punctuated(
ast::Var::Expression(Box::new(ast::VarExpression {
prefix,
suffixes: next_suffixes,
})),
next_comma,
),
},
}
}
let Some(equal_token) = state.require(Symbol::Equal, "expected `=` after name") else {
return ParserResult::LexerMoved;
};
let expr_list = match parse_expression_list(state) {
ParserResult::Value(expr_list) => expr_list,
ParserResult::NotFound => {
state.token_error(equal_token.clone(), "expected values to set to");
Punctuated::new()
}
ParserResult::LexerMoved => Punctuated::new(),
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::Assignment(ast::Assignment {
var_list,
equal_token,
expr_list,
})))
}
#[cfg(any(feature = "lua52", feature = "luajit", feature = "cfxlua"))]
TokenType::Symbol {
symbol: Symbol::Goto,
} => {
debug_assert!(state.lua_version().has_lua52() || state.lua_version().has_luajit());
let goto_token = state.consume().unwrap();
match state.current() {
Ok(token) if matches!(token.token_type(), TokenType::Identifier { .. }) => {
let label_name = state.consume().unwrap();
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::Goto(ast::Goto {
goto_token,
label_name,
})))
}
Ok(token) => {
state.token_error_ranged(
token.clone(),
"expected label name after `goto`",
&goto_token,
&token.clone(),
);
ParserResult::LexerMoved
}
Err(()) => {
state.token_error(goto_token, "expected label name after `goto`");
ParserResult::LexerMoved
}
}
}
#[cfg(any(feature = "lua52", feature = "luajit"))]
TokenType::Symbol {
symbol: Symbol::TwoColons,
} if state.lua_version().has_lua52() || state.lua_version().has_luajit() => {
let left_colons = state.consume().unwrap();
let name = match state.current() {
Ok(token) if matches!(token.token_type(), TokenType::Identifier { .. }) => {
state.consume().unwrap()
}
Ok(token) => {
state.token_error_ranged(
token.clone(),
"expected label name after `::`",
&left_colons,
&token.clone(),
);
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
};
let right_colons = match state.require(Symbol::TwoColons, "expected `::` after label") {
Some(token) => token,
None => TokenReference::symbol("::").unwrap(),
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::Label(ast::Label {
left_colons,
name,
right_colons,
})))
}
#[cfg(feature = "luau")]
TokenType::Symbol {
symbol: Symbol::AtSign,
} if state.lua_version().has_luau() => {
let ParserResult::Value(attributes) = parse_attributes(state) else {
return ParserResult::LexerMoved;
};
match state.current() {
Ok(token) if token.is_symbol(Symbol::Function) => {
let function_token = state.consume().unwrap();
let function_declaration =
match expect_function_declaration(state, function_token) {
Ok(function_declaration) => function_declaration,
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::FunctionDeclaration(
function_declaration.with_attributes(attributes),
)))
}
Ok(token) if token.is_symbol(Symbol::Local) => {
let local_token = state.consume().unwrap();
match state.current() {
Ok(token) if token.is_symbol(Symbol::Function) => {
let function_token = state.consume().unwrap();
let local_function = match expect_local_function_declaration(
state,
local_token,
function_token,
) {
Ok(local_function) => local_function,
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::LocalFunction(
local_function.with_attributes(attributes),
)))
}
Ok(token) => {
state.token_error(
token.clone(),
"expected `local function` after attribute",
);
ParserResult::LexerMoved
}
Err(()) => ParserResult::LexerMoved,
}
}
Ok(token) if matches!(token.token_type(), TokenType::Identifier { identifier } if identifier.as_str() == "const") =>
{
let const_token = state.consume().unwrap();
match state.current() {
Ok(token) if token.is_symbol(Symbol::Function) => {
let function_token = state.consume().unwrap();
let const_function = match expect_const_function_declaration(
state,
const_token,
function_token,
) {
Ok(const_function) => const_function,
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(StmtVariant::Stmt(ast::Stmt::ConstFunction(
const_function.with_attributes(attributes),
)))
}
Ok(token) => {
state.token_error(
token.clone(),
"expected `const function` after attribute",
);
ParserResult::LexerMoved
}
Err(()) => ParserResult::LexerMoved,
}
}
Ok(token) => {
state.token_error(
token.clone(),
"expected `function`, `local function`, or `const function` after attribute",
);
ParserResult::LexerMoved
}
Err(()) => ParserResult::LexerMoved,
}
}
_ => ParserResult::NotFound,
}
}
fn parse_last_stmt(
state: &mut ParserState,
) -> ParserResult<(ast::LastStmt, Option<TokenReference>)> {
let last_stmt = match state.current() {
Ok(token) if token.is_symbol(Symbol::Return) => {
let return_token = state.consume().unwrap();
let expr_list = match parse_expression_list(state) {
ParserResult::Value(expr_list) => expr_list,
ParserResult::LexerMoved | ParserResult::NotFound => Punctuated::new(),
};
ast::LastStmt::Return(ast::Return {
token: return_token,
returns: expr_list,
})
}
Ok(token) if token.is_symbol(Symbol::Break) => {
let break_token = state.consume().unwrap();
ast::LastStmt::Break(break_token)
}
_ => return ParserResult::NotFound,
};
let semicolon = state.consume_if(Symbol::Semicolon);
ParserResult::Value((last_stmt, semicolon))
}
fn expect_function_name(state: &mut ParserState) -> ParserResult<ast::FunctionName> {
let mut names = Punctuated::new();
let name = match state.current() {
Ok(token) if matches!(token.token_type(), TokenType::Identifier { .. }) => {
state.consume().unwrap()
}
Ok(token) => {
state.token_error(token.clone(), "expected function name");
return ParserResult::NotFound;
}
Err(()) => return ParserResult::NotFound,
};
names.push(Pair::End(name));
loop {
let middle_token = match state.current() {
Ok(token) if token.is_symbol(Symbol::Colon) || token.is_symbol(Symbol::Dot) => {
state.consume().unwrap()
}
Ok(_) => break,
Err(()) => return ParserResult::LexerMoved,
};
let name = match state.current() {
Ok(token) if matches!(token.token_type(), TokenType::Identifier { .. }) => {
state.consume().unwrap()
}
Ok(token) => {
state.token_error(
token.clone(),
format!("expected name after `{}`", middle_token.token()),
);
return ParserResult::NotFound;
}
Err(()) => {
return ParserResult::LexerMoved;
}
};
if middle_token.is_symbol(Symbol::Dot) {
names.push_punctuated(name, middle_token);
} else if middle_token.is_symbol(Symbol::Colon) {
return ParserResult::Value(ast::FunctionName {
names,
colon_name: Some((middle_token, name)),
});
} else {
unreachable!();
}
}
ParserResult::Value(ast::FunctionName {
names,
colon_name: None,
})
}
fn expect_function_declaration(
state: &mut ParserState,
function_token: TokenReference,
) -> Result<ast::FunctionDeclaration, ()> {
let function_name = match expect_function_name(state) {
ParserResult::Value(name) => name,
ParserResult::NotFound | ParserResult::LexerMoved => return Err(()),
};
let function_body = match parse_function_body(state) {
ParserResult::Value(body) => body,
ParserResult::LexerMoved => ast::FunctionBody::new(),
ParserResult::NotFound => {
state.token_error(function_token.clone(), "expected a function body");
ast::FunctionBody::new()
}
};
Ok(ast::FunctionDeclaration {
#[cfg(feature = "luau")]
attributes: Vec::new(),
function_token,
name: function_name,
body: function_body,
})
}
fn expect_local_function_declaration(
state: &mut ParserState,
local_token: TokenReference,
function_token: TokenReference,
) -> Result<ast::LocalFunction, ()> {
let function_name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => state.consume().unwrap(),
Ok(token) => {
state.token_error(token.clone(), "expected a function name");
return Err(());
}
Err(()) => return Err(()),
};
let function_body = match parse_function_body(state) {
ParserResult::Value(function_body) => function_body,
ParserResult::NotFound => {
state.token_error(function_token, "expected a function body");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
Ok(ast::LocalFunction {
#[cfg(feature = "luau")]
attributes: Vec::new(),
local_token,
function_token,
name: function_name,
body: function_body,
})
}
fn expect_for_stmt(state: &mut ParserState, for_token: TokenReference) -> Result<ast::Stmt, ()> {
let name_list = match parse_name_list(state) {
ParserResult::Value(name_list) => name_list,
ParserResult::NotFound => {
state.token_error(for_token, "expected name after `for`");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
let current_token = match state.current() {
Ok(token) => token,
Err(()) => return Err(()),
};
debug_assert!(!name_list.is_empty());
if name_list.len() == 1 && current_token.is_symbol(Symbol::Equal) {
return Ok(ast::Stmt::NumericFor(expect_numeric_for_stmt(
state,
for_token,
name_list.into_iter().next().unwrap(),
)?));
}
let in_token = match current_token {
token if token.is_symbol(Symbol::In) => state.consume().unwrap(),
token => {
state.token_error(token.clone(), "expected `in` after name list");
return Err(());
}
};
let expressions = match parse_expression_list(state) {
ParserResult::Value(expressions) => expressions,
ParserResult::NotFound => {
state.token_error(in_token, "expected expressions after `in`");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
let Some(do_token) = state.require(Symbol::Do, "expected `do` after expression list") else {
return Ok(ast::Stmt::GenericFor(ast::GenericFor {
for_token,
names: name_list
.clone()
.into_pairs()
.map(|pair| pair.map(|name| name.name))
.collect(),
#[cfg(feature = "luau")]
type_specifiers: name_list
.into_iter()
.map(|name| name.type_specifier)
.collect(),
in_token,
expr_list: expressions,
do_token: TokenReference::basic_symbol("do"),
block: ast::Block::new(),
end_token: TokenReference::basic_symbol("end"),
}));
};
let (block, end) = match expect_block_with_end(state, "for loop", &do_token) {
Ok(block) => block,
Err(()) => (ast::Block::new(), TokenReference::basic_symbol("end")),
};
Ok(ast::Stmt::GenericFor(ast::GenericFor {
for_token,
names: name_list
.clone()
.into_pairs()
.map(|pair| pair.map(|name| name.name))
.collect(),
#[cfg(feature = "luau")]
type_specifiers: name_list
.into_iter()
.map(|name| name.type_specifier)
.collect(),
in_token,
expr_list: expressions,
do_token,
block,
end_token: end,
}))
}
fn expect_numeric_for_stmt(
state: &mut ParserState,
for_token: TokenReference,
index_variable: Name,
) -> Result<ast::NumericFor, ()> {
let equal_token = state.consume().unwrap();
debug_assert!(equal_token.is_symbol(Symbol::Equal));
let start = match parse_expression(state) {
ParserResult::Value(start) => start,
ParserResult::NotFound => {
state.token_error(equal_token, "expected start expression after `=`");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
let Some(start_end_comma) = state.require(Symbol::Comma, "expected `,` after start expression")
else {
return Err(());
};
let end = match parse_expression(state) {
ParserResult::Value(end) => end,
ParserResult::NotFound => {
state.token_error(start_end_comma, "expected end expression after `,`");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
let (end_step_comma, step) = match state.consume_if(Symbol::Comma) {
Some(end_step_comma) => match parse_expression(state) {
ParserResult::Value(step) => (Some(end_step_comma), Some(step)),
ParserResult::NotFound => {
state.token_error(start_end_comma, "expected step expression after `,`");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
},
None => (None, None),
};
let Some(do_token) = state.require(Symbol::Do, "expected `do` after step expression") else {
return Err(());
};
let (block, end_token) = match expect_block_with_end(state, "numeric for loop", &do_token) {
Ok(block) => block,
Err(()) => (ast::Block::new(), TokenReference::basic_symbol("end")),
};
Ok(ast::NumericFor {
for_token,
index_variable: index_variable.name,
#[cfg(feature = "luau")]
type_specifier: index_variable.type_specifier,
equal_token,
start,
start_end_comma,
end,
end_step_comma,
step,
do_token,
block,
end_token,
})
}
fn expect_if_stmt(state: &mut ParserState, if_token: TokenReference) -> Result<ast::If, ()> {
let condition = match parse_expression(state) {
ParserResult::Value(condition) => condition,
ParserResult::NotFound => {
state.token_error(if_token, "expected condition after `if`");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
let Some(then_token) = state.require(Symbol::Then, "expected `then` after condition") else {
return Err(());
};
let then_block = match parse_block(state) {
ParserResult::Value(block) => block,
ParserResult::NotFound => {
state.token_error(then_token, "expected block after `then`");
return Ok(ast::If::new(condition));
}
ParserResult::LexerMoved => {
return Ok(ast::If::new(condition));
}
};
let mut else_if = Vec::new();
let else_if_optional = |else_if: Vec<ast::ElseIf>| {
if else_if.is_empty() {
None
} else {
Some(else_if)
}
};
let unfinished_if =
|condition, else_if| Ok(ast::If::new(condition).with_else_if(else_if_optional(else_if)));
loop {
let else_if_token = match state.current() {
Ok(else_if_token) if else_if_token.is_symbol(Symbol::ElseIf) => {
state.consume().unwrap()
}
Ok(_) => break,
Err(()) => {
return unfinished_if(condition, else_if);
}
};
let condition = match parse_expression(state) {
ParserResult::Value(condition) => condition,
ParserResult::NotFound => {
state.token_error(else_if_token, "expected condition after `elseif`");
return unfinished_if(condition, else_if);
}
ParserResult::LexerMoved => {
return unfinished_if(condition, else_if);
}
};
let Some(then_token) = state.require(Symbol::Then, "expected `then` after condition")
else {
return unfinished_if(condition, else_if);
};
let then_block = match parse_block(state) {
ParserResult::Value(block) => block,
ParserResult::NotFound => {
state.token_error(then_token, "expected block after `then`");
return unfinished_if(condition, else_if);
}
ParserResult::LexerMoved => {
return unfinished_if(condition, else_if);
}
};
else_if.push(ast::ElseIf {
else_if_token,
condition,
then_token,
block: then_block,
});
}
let (else_block, else_token) = match state.consume_if(Symbol::Else) {
Some(else_token) => match parse_block(state) {
ParserResult::Value(block) => (Some(block), Some(else_token)),
ParserResult::NotFound => {
state.token_error(else_token.clone(), "expected block after `else`");
(Some(ast::Block::new()), Some(else_token))
}
ParserResult::LexerMoved => (Some(ast::Block::new()), Some(else_token)),
},
None => (None, None),
};
let end_token = match state.current() {
Ok(token) if token.is_symbol(Symbol::End) => state.consume().unwrap(),
Ok(token) => {
state.token_error(token.clone(), "expected `end` to conclude `if`");
TokenReference::basic_symbol("end")
}
Err(()) => TokenReference::basic_symbol("end"),
};
Ok(ast::If {
if_token,
condition,
then_token,
block: then_block,
else_if: else_if_optional(else_if),
else_token,
r#else: else_block,
end_token,
})
}
fn expect_local_assignment(
state: &mut ParserState,
local_token: TokenReference,
) -> Result<ast::LocalAssignment, ()> {
let names = match one_or_more(state, parse_name_with_attributes, Symbol::Comma) {
ParserResult::Value(names) => names,
ParserResult::NotFound => {
unreachable!("expect_local_assignment called without upcoming identifier");
}
ParserResult::LexerMoved => return Err(()),
};
let mut name_list = Punctuated::new();
#[cfg(feature = "luau")]
let mut type_specifiers = Vec::new();
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
let mut attributes = Vec::new();
for name in names.into_pairs() {
let (name, punctuation) = name.into_tuple();
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
attributes.push(name.attribute);
#[cfg(feature = "luau")]
type_specifiers.push(name.type_specifier);
name_list.push(match punctuation {
Some(punctuation) => Pair::Punctuated(name.name, punctuation),
None => Pair::End(name.name),
});
}
let mut local_assignment = ast::LocalAssignment {
local_token,
name_list,
#[cfg(feature = "luau")]
type_specifiers,
equal_token: None,
expr_list: Punctuated::new(),
#[cfg(feature = "lua54")]
attributes,
};
#[cfg(not(feature = "cfxlua"))]
let symbols = [Symbol::Equal];
#[cfg(feature = "cfxlua")]
let symbols = [Symbol::Equal, Symbol::In];
local_assignment.equal_token = match state.consume_if_symbols(&symbols) {
Some(equal_token) => Some(equal_token),
None => return Ok(local_assignment),
};
match parse_expression_list(state) {
ParserResult::Value(expr_list) => local_assignment.expr_list = expr_list,
ParserResult::NotFound => {
state.token_error(
local_assignment.equal_token.clone().unwrap(),
"expected an expression",
);
}
ParserResult::LexerMoved => {}
};
Ok(local_assignment)
}
#[cfg(feature = "luau")]
fn expect_const_assignment(
state: &mut ParserState,
const_token: TokenReference,
) -> Result<ast::ConstAssignment, ()> {
let names = match one_or_more(state, parse_name_with_type_specifiers, Symbol::Comma) {
ParserResult::Value(names) => names,
ParserResult::NotFound => {
unreachable!("expect_const_assignment called without upcoming identifier");
}
ParserResult::LexerMoved => return Err(()),
};
let mut name_list = Punctuated::new();
let mut type_specifiers = Vec::new();
for name in names.into_pairs() {
let (name, punctuation) = name.into_tuple();
type_specifiers.push(name.type_specifier);
name_list.push(match punctuation {
Some(punctuation) => Pair::Punctuated(name.name, punctuation),
None => Pair::End(name.name),
});
}
let mut const_assignment = ast::ConstAssignment {
const_token,
name_list,
type_specifiers,
equal_token: None,
expr_list: Punctuated::new(),
};
const_assignment.equal_token = match state.consume_if_symbols(&[Symbol::Equal]) {
Some(equal_token) => Some(equal_token),
None => {
let last_name = const_assignment.name_list.last().unwrap().value().clone();
state.token_error_ranged(
const_assignment.const_token.clone(),
"const declaration must have an assignment",
&const_assignment.const_token,
&last_name,
);
return Ok(const_assignment);
}
};
match parse_expression_list(state) {
ParserResult::Value(expr_list) => {
const_assignment.expr_list = expr_list;
let name_count = const_assignment.name_list.len();
let expr_count = const_assignment.expr_list.len();
let last_can_expand = const_assignment
.expr_list
.last()
.map(|expr| match expr.value() {
ast::Expression::FunctionCall(_) => true,
ast::Expression::Symbol(t) => t.is_symbol(Symbol::Ellipsis),
_ => false,
})
.unwrap_or(false);
if !last_can_expand && name_count != expr_count {
state.token_error(
const_assignment.const_token.clone(),
format!(
"const declaration has {} name{} but {} value{}",
name_count,
if name_count == 1 { "" } else { "s" },
expr_count,
if expr_count == 1 { "" } else { "s" },
),
);
}
}
ParserResult::NotFound => {
state.token_error(
const_assignment.equal_token.clone().unwrap(),
"expected an expression",
);
}
ParserResult::LexerMoved => {}
};
Ok(const_assignment)
}
#[cfg(feature = "luau")]
fn expect_const_function_declaration(
state: &mut ParserState,
const_token: TokenReference,
function_token: TokenReference,
) -> Result<ast::ConstFunction, ()> {
let function_name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => state.consume().unwrap(),
Ok(token) => {
state.token_error(token.clone(), "expected a function name");
return Err(());
}
Err(()) => return Err(()),
};
let function_body = match parse_function_body(state) {
ParserResult::Value(function_body) => function_body,
ParserResult::NotFound => {
state.token_error(function_token, "expected a function body");
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
Ok(ast::ConstFunction {
attributes: Vec::new(),
const_token,
function_token,
name: function_name,
body: function_body,
})
}
fn expect_expression_key(
state: &mut ParserState,
left_bracket: TokenReference,
) -> Result<ast::Field, ()> {
let expression = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(left_bracket, "expected an expression after `[`");
return Err(());
}
ParserResult::LexerMoved => {
return Err(());
}
};
let Some(right_bracket) = state.require_with_reference_range_callback(
Symbol::RightBracket,
"expected `]` after expression",
|| {
(
left_bracket.clone(),
expression.tokens().next_back().unwrap().clone(),
)
},
) else {
return Err(());
};
let Some(equal_token) = state.require_with_reference_range(
Symbol::Equal,
"expected `=` after expression",
&left_bracket,
&right_bracket,
) else {
return Err(());
};
let value = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(equal_token, "expected an expression after `=`");
return Err(());
}
ParserResult::LexerMoved => {
return Err(());
}
};
Ok(ast::Field::ExpressionKey {
brackets: ContainedSpan::new(left_bracket, right_bracket),
key: expression,
equal: equal_token,
value,
})
}
fn force_table_constructor(
state: &mut ParserState,
left_brace: TokenReference,
) -> ast::TableConstructor {
let mut fields = Punctuated::new();
let unfinished_table =
|left_brace: TokenReference, fields: Punctuated<ast::Field>| ast::TableConstructor {
braces: ContainedSpan::new(left_brace, TokenReference::basic_symbol("}")),
fields,
};
loop {
let Ok(current_token) = state.current() else {
return unfinished_table(left_brace, fields);
};
let field = match current_token.token_type() {
TokenType::Symbol {
symbol: Symbol::RightBrace,
} => {
return ast::TableConstructor {
braces: ContainedSpan::new(left_brace, state.consume().unwrap()),
fields,
};
}
TokenType::Symbol {
symbol: Symbol::LeftBracket,
} => {
let left_bracket = state.consume().unwrap();
match expect_expression_key(state, left_bracket) {
Ok(field) => field,
Err(()) => {
return unfinished_table(left_brace, fields);
}
}
}
TokenType::Identifier { .. } if matches!(state.peek(), Ok(peek_token) if peek_token.is_symbol(Symbol::Equal)) =>
{
let key = state.consume().unwrap();
let equal_token = state.consume().unwrap();
let value = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(equal_token, "expected an expression after `=`");
return unfinished_table(left_brace, fields);
}
ParserResult::LexerMoved => {
return unfinished_table(left_brace, fields);
}
};
ast::Field::NameKey {
key,
equal: equal_token,
value,
}
}
#[cfg(feature = "cfxlua")]
TokenType::Symbol {
symbol: Symbol::Dot,
} => {
let dot = state.consume().unwrap();
let key = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => {
state.consume().unwrap()
}
Ok(token) => {
state.token_error(token.clone(), "expected identifier after `.`");
return unfinished_table(left_brace, fields);
}
Err(()) => {
return unfinished_table(left_brace, fields);
}
};
ast::Field::SetConstructor { dot, name: key }
}
_ => {
let value = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(
match fields.last() {
Some(Pair::End(field)) => {
field.tokens().next_back().unwrap().clone()
}
Some(Pair::Punctuated(field, _)) => {
field.tokens().next_back().unwrap().clone()
}
None => left_brace.clone(),
},
"expected a field",
);
return unfinished_table(left_brace, fields);
}
ParserResult::LexerMoved => {
return unfinished_table(left_brace, fields);
}
};
ast::Field::NoKey(value)
}
};
match state.current() {
Ok(token) => {
if token.is_symbol(Symbol::Comma) || token.is_symbol(Symbol::Semicolon) {
fields.push(Pair::Punctuated(field, state.consume().unwrap()))
} else {
fields.push(Pair::End(field));
break;
}
}
Err(()) => {
fields.push(Pair::End(field));
break;
}
};
}
let right_brace = match state.require(Symbol::RightBrace, "expected `}` after last field") {
Some(right_brace) => right_brace,
None => TokenReference::basic_symbol("}"),
};
ast::TableConstructor {
braces: ContainedSpan::new(left_brace, right_brace),
fields,
}
}
fn expect_repeat_stmt(
state: &mut ParserState,
repeat_token: TokenReference,
) -> Result<ast::Stmt, ()> {
let block = match parse_block(state) {
ParserResult::Value(block) => block,
ParserResult::NotFound => {
state.token_error(repeat_token, "expected a block after `repeat`");
return Err(());
}
ParserResult::LexerMoved => {
return Err(());
}
};
let Some(until_token) = state.require(Symbol::Until, "expected `until` after block") else {
return Ok(ast::Stmt::Do(ast::Do::new().with_block(block)));
};
let condition = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(until_token, "expected a condition after `until`");
return Ok(ast::Stmt::Do(ast::Do::new().with_block(block)));
}
ParserResult::LexerMoved => {
return Ok(ast::Stmt::Do(ast::Do::new().with_block(block)));
}
};
Ok(ast::Stmt::Repeat(ast::Repeat {
repeat_token,
block,
until: condition,
until_token,
}))
}
fn expect_while_stmt(
state: &mut ParserState,
while_token: TokenReference,
) -> Result<ast::While, ()> {
let condition = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(while_token, "expected a condition after `while`");
return Err(());
}
ParserResult::LexerMoved => {
return Err(());
}
};
let Some(do_token) = state.require(Symbol::Do, "expected `do` after condition") else {
return Ok(ast::While::new(condition));
};
let (block, end_token) = match expect_block_with_end(state, "while loop", &do_token) {
Ok((block, end_token)) => (block, end_token),
Err(()) => {
return Ok(ast::While::new(condition));
}
};
Ok(ast::While {
while_token,
condition,
do_token,
block,
end_token,
})
}
#[cfg(feature = "luau")]
fn expect_type_declaration(
state: &mut ParserState,
type_token: TokenReference,
) -> Result<ast::TypeDeclaration, ()> {
let base = match state.current()? {
token if token.token_kind() == TokenKind::Identifier => state.consume().unwrap(),
token => {
state.token_error(token.clone(), "expected type name");
return Err(());
}
};
let generics = match parse_generic_type_list(state, TypeListStyle::WithDefaults) {
ParserResult::Value(generics) => Some(generics),
ParserResult::NotFound => None,
_ => return Err(()),
};
let equal_token = state
.require(Symbol::Equal, "expected `=` after type name")
.unwrap_or_else(|| TokenReference::basic_symbol("="));
let ParserResult::Value(declare_as) = parse_type(state) else {
return Err(());
};
Ok(ast::TypeDeclaration {
type_token,
base,
generics,
equal_token,
declare_as,
})
}
#[cfg(feature = "luau")]
fn expect_type_function(
state: &mut ParserState,
type_token: TokenReference,
function_token: TokenReference,
) -> Result<ast::TypeFunction, ()> {
let function_name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => state.consume().unwrap(),
Ok(token) => {
state.token_error(token.clone(), "expected a type function name");
return Err(());
}
Err(()) => return Err(()),
};
let function_body = match parse_function_body(state) {
ParserResult::Value(body) => body,
ParserResult::LexerMoved => return Err(()),
ParserResult::NotFound => {
state.token_error(function_token, "expected a type function body");
return Err(());
}
};
Ok(ast::TypeFunction {
type_token,
function_token,
function_name,
function_body,
})
}
fn parse_prefix(state: &mut ParserState) -> ParserResult<ast::Prefix> {
let current_token = match state.current() {
Ok(token) => token,
Err(()) => return ParserResult::NotFound,
};
match current_token.token_type() {
TokenType::Symbol {
symbol: Symbol::LeftParen,
} => {
let left_parenthesis = state.consume().unwrap();
let expression = Box::new(match try_parser!(parse_expression(state)) {
Some(expression) => expression,
None => {
state.token_error(left_parenthesis, "expected an expression after `(`");
return ParserResult::LexerMoved;
}
});
let Some(right_parenthesis) =
state.require(Symbol::RightParen, "expected `)` after expression")
else {
return ParserResult::Value(ast::Prefix::Expression(Box::new(
ast::Expression::Parentheses {
contained: ContainedSpan::new(
left_parenthesis,
TokenReference::basic_symbol(")"),
),
expression,
},
)));
};
ParserResult::Value(ast::Prefix::Expression(Box::new(
ast::Expression::Parentheses {
contained: ContainedSpan::new(left_parenthesis, right_parenthesis),
expression,
},
)))
}
TokenType::Identifier { .. } => {
ParserResult::Value(ast::Prefix::Name(state.consume().unwrap()))
}
_ => ParserResult::NotFound,
}
}
fn parse_arguments(state: &mut ParserState) -> ParserResult<ast::FunctionArgs> {
let current = match state.current() {
Ok(token) => token,
Err(()) => return ParserResult::NotFound,
};
match current.token_type() {
TokenType::Symbol {
symbol: Symbol::LeftParen,
} => {
let left_parenthesis = state.consume().unwrap();
let arguments = try_parser!(parse_expression_list(state)).unwrap_or_default();
let right_parenthesis = match state.require_with_reference_token(
Symbol::RightParen,
"expected `)` to close function call",
&left_parenthesis,
) {
Some(token) => token,
None => TokenReference::basic_symbol(")"),
};
ParserResult::Value(ast::FunctionArgs::Parentheses {
parentheses: ContainedSpan::new(left_parenthesis, right_parenthesis),
arguments,
})
}
TokenType::Symbol {
symbol: Symbol::LeftBrace,
} => {
let left_brace = state.consume().unwrap();
ParserResult::Value(ast::FunctionArgs::TableConstructor(
force_table_constructor(state, left_brace),
))
}
TokenType::StringLiteral { .. } => {
ParserResult::Value(ast::FunctionArgs::String(state.consume().unwrap()))
}
_ => ParserResult::NotFound,
}
}
fn parse_suffix(state: &mut ParserState) -> ParserResult<ast::Suffix> {
let Ok(current) = state.current() else {
return ParserResult::NotFound;
};
match current.token_type() {
#[cfg(feature = "cfxlua")]
TokenType::Symbol {
symbol: Symbol::QuestionMarkDot,
} => {
let safe_navigation = state.consume().unwrap();
let name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => {
state.consume().unwrap()
}
Ok(_) => {
state.token_error(safe_navigation, "expected identifier after `?.`");
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(ast::Suffix::Index(ast::Index::Dot {
dot: safe_navigation,
name,
}))
}
TokenType::Symbol {
symbol: Symbol::Dot,
} => {
let dot = state.consume().unwrap();
let name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => {
state.consume().unwrap()
}
Ok(_) => {
state.token_error(dot, "expected identifier after `.`");
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(ast::Suffix::Index(ast::Index::Dot { dot, name }))
}
TokenType::Symbol {
symbol: Symbol::LeftBracket,
} => {
let left_bracket = state.consume().unwrap();
let expression = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
ParserResult::NotFound => {
state.token_error(left_bracket, "expected expression after `[`");
return ParserResult::LexerMoved;
}
};
let right_bracket = match state.require_with_reference_range(
Symbol::RightBracket,
"expected `]` to close index expression",
&left_bracket,
expression.tokens().next_back().unwrap(),
) {
Some(right_bracket) => right_bracket,
None => TokenReference::basic_symbol("]"),
};
ParserResult::Value(ast::Suffix::Index(ast::Index::Brackets {
brackets: ContainedSpan::new(left_bracket, right_bracket),
expression,
}))
}
TokenType::Symbol {
symbol: Symbol::LeftParen | Symbol::LeftBrace,
}
| TokenType::StringLiteral { .. } => {
let arguments = try_parser!(parse_arguments(state)).unwrap();
ParserResult::Value(ast::Suffix::Call(ast::Call::AnonymousCall(arguments)))
}
#[cfg(feature = "luau")]
TokenType::Symbol {
symbol: Symbol::LessThan,
} if state.lua_version().has_luau()
&& matches!(
state.peek().map(|peek| peek.token_type()),
Ok(TokenType::Symbol {
symbol: Symbol::LessThan
})
) =>
{
let outer_0 = state.consume().unwrap();
match expect_type_instantiation(state, outer_0) {
Ok(type_instantiation) => {
ParserResult::Value(ast::Suffix::TypeInstantiation(type_instantiation))
}
Err(_) => ParserResult::LexerMoved,
}
}
TokenType::Symbol {
symbol: Symbol::Colon,
} => {
let colon_token = state.consume().unwrap();
let name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => {
state.consume().unwrap()
}
Ok(_) => {
state.token_error(colon_token, "expected identifier after `:`");
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
};
#[cfg(feature = "luau")]
let type_instantiation = if state.lua_version().has_luau() {
if let Ok(token) = state.current() {
if matches!(
token.token_type(),
TokenType::Symbol {
symbol: Symbol::LessThan
}
) {
let outer_0 = state.consume().unwrap();
match expect_type_instantiation(state, outer_0) {
Ok(instantiation) => Some(instantiation),
Err(()) => return ParserResult::LexerMoved,
}
} else {
None
}
} else {
None
}
} else {
None
};
let args = match parse_arguments(state) {
ParserResult::Value(args) => args,
ParserResult::LexerMoved => ast::FunctionArgs::empty(),
ParserResult::NotFound => {
state.token_error(name.clone(), "expected arguments after `:`");
ast::FunctionArgs::empty()
}
};
ParserResult::Value(ast::Suffix::Call(ast::Call::MethodCall(ast::MethodCall {
colon_token,
name,
args,
#[cfg(feature = "luau")]
type_instantiation,
})))
}
_ => ParserResult::NotFound,
}
}
fn parse_prefix_and_suffixes(
state: &mut ParserState,
) -> ParserResult<(ast::Prefix, Vec<ast::Suffix>)> {
let prefix = match parse_prefix(state) {
ParserResult::Value(prefix) => prefix,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
ParserResult::NotFound => return ParserResult::NotFound,
};
let mut suffixes = Vec::new();
loop {
match parse_suffix(state) {
ParserResult::Value(suffix) => {
suffixes.push(suffix);
}
ParserResult::LexerMoved => {
break;
}
ParserResult::NotFound => {
break;
}
};
}
ParserResult::Value((prefix, suffixes))
}
fn parse_expression(state: &mut ParserState) -> ParserResult<Expression> {
let primary_expression = match parse_primary_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => return ParserResult::NotFound,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
parse_expression_with_precedence(state, primary_expression, 0)
}
fn parse_primary_expression(state: &mut ParserState) -> ParserResult<Expression> {
let current_token = match state.current() {
Ok(token) => token,
Err(()) => return ParserResult::NotFound,
};
let expression = match current_token.token_type() {
#[cfg(feature = "luau")]
TokenType::Symbol {
symbol: Symbol::AtSign,
} => {
let ParserResult::Value(attributes) = parse_attributes(state) else {
return ParserResult::LexerMoved;
};
match state.current() {
Ok(token) if token.is_symbol(Symbol::Function) => {
let function_token = state.consume().unwrap();
let function_body = match parse_function_body(state) {
ParserResult::Value(body) => body,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
ParserResult::NotFound => {
state.token_error(function_token, "expected a function body");
return ParserResult::LexerMoved;
}
};
ParserResult::Value(Expression::Function(Box::new(ast::AnonymousFunction {
#[cfg(feature = "luau")]
attributes,
function_token,
body: function_body,
})))
}
Ok(token) => {
state.token_error(token.clone(), "expected `function` after attributes");
return ParserResult::LexerMoved;
}
Err(()) => return ParserResult::LexerMoved,
}
}
TokenType::Symbol {
symbol: Symbol::Function,
} => {
let function_token = state.consume().unwrap();
let function_body = match parse_function_body(state) {
ParserResult::Value(body) => body,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
ParserResult::NotFound => {
state.token_error(function_token, "expected a function body");
return ParserResult::LexerMoved;
}
};
ParserResult::Value(Expression::Function(Box::new(ast::AnonymousFunction {
#[cfg(feature = "luau")]
attributes: Vec::new(),
function_token,
body: function_body,
})))
}
TokenType::Symbol {
symbol: Symbol::True | Symbol::False | Symbol::Nil | Symbol::Ellipsis,
} => ParserResult::Value(Expression::Symbol(state.consume().unwrap())),
TokenType::StringLiteral { .. } => {
let string_token = state.consume().unwrap();
ParserResult::Value(Expression::String(string_token))
}
TokenType::Number { .. } => {
let number_token = state.consume().unwrap();
ParserResult::Value(Expression::Number(number_token))
}
TokenType::Identifier { .. }
| TokenType::Symbol {
symbol: Symbol::LeftParen,
} => {
let (prefix, suffixes) = match parse_prefix_and_suffixes(state) {
ParserResult::Value(value) => value,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
ParserResult::NotFound => {
unreachable!("identifier found but parse_prefix_and_suffixes didn't even move");
}
};
if suffixes.is_empty() {
match prefix {
ast::Prefix::Expression(expression) => ParserResult::Value(*expression),
ast::Prefix::Name(name) => {
ParserResult::Value(Expression::Var(ast::Var::Name(name)))
}
}
} else if matches!(suffixes.last().unwrap(), ast::Suffix::Call(_)) {
ParserResult::Value(Expression::FunctionCall(ast::FunctionCall {
prefix,
suffixes,
}))
} else {
ParserResult::Value(Expression::Var(ast::Var::Expression(Box::new(
ast::VarExpression { prefix, suffixes },
))))
}
}
#[cfg(feature = "lua53")]
TokenType::Symbol {
symbol: Symbol::Minus | Symbol::Not | Symbol::Hash | Symbol::Tilde,
} => {
let unary_operator_token = state.consume().unwrap();
parse_unary_expression(state, unary_operator_token)
}
#[cfg(not(feature = "lua53"))]
TokenType::Symbol {
symbol: Symbol::Minus | Symbol::Not | Symbol::Hash,
} => {
let unary_operator_token = state.consume().unwrap();
parse_unary_expression(state, unary_operator_token)
}
TokenType::Symbol {
symbol: Symbol::LeftBrace,
} => {
let left_brace = state.consume().unwrap();
ParserResult::Value(ast::Expression::TableConstructor(force_table_constructor(
state, left_brace,
)))
}
#[cfg(feature = "luau")]
TokenType::Symbol { symbol: Symbol::If } if state.lua_version().has_luau() => {
let if_token = state.consume().unwrap();
match expect_if_else_expression(state, if_token) {
Ok(if_expression) => {
ParserResult::Value(ast::Expression::IfExpression(if_expression))
}
Err(_) => ParserResult::LexerMoved,
}
}
#[cfg(feature = "luau")]
TokenType::InterpolatedString { kind, .. } if state.lua_version().has_luau() => {
let kind = *kind;
let interpolated_string_begin = state.consume().unwrap();
match kind {
InterpolatedStringKind::Simple => ParserResult::Value(
ast::Expression::InterpolatedString(ast::InterpolatedString {
segments: Vec::new(),
last_string: interpolated_string_begin,
}),
),
InterpolatedStringKind::Begin => {
ParserResult::Value(ast::Expression::InterpolatedString(
expect_interpolated_string(state, interpolated_string_begin),
))
}
_ => ParserResult::NotFound,
}
}
_ => ParserResult::NotFound,
};
match expression {
#[cfg(feature = "luau")]
ParserResult::Value(expression) if state.lua_version().has_luau() => {
if let Some(assertion_op) = state.consume_if(Symbol::TwoColons) {
let ParserResult::Value(cast_to) = parse_type(state) else {
return ParserResult::LexerMoved;
};
ParserResult::Value(ast::Expression::TypeAssertion {
expression: Box::new(expression),
type_assertion: ast::TypeAssertion {
assertion_op,
cast_to,
},
})
} else {
ParserResult::Value(expression)
}
}
_ => expression,
}
}
fn parse_expression_with_precedence(
state: &mut ParserState,
mut lhs: Expression,
precedence: u8,
) -> ParserResult<Expression> {
loop {
let Some(bin_op_precedence) = ast::BinOp::precedence_of_token(match state.current() {
Ok(token) => token,
Err(()) => return ParserResult::Value(lhs),
}) else {
return ParserResult::Value(lhs);
};
if bin_op_precedence < precedence {
return ParserResult::Value(lhs);
}
let bin_op = ast::BinOp::consume(state).unwrap();
let mut rhs = match parse_primary_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(
bin_op.token().clone(),
"expected expression after binary operator",
);
return ParserResult::Value(lhs);
}
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
while let Ok(next_bin_op_token) = state.current() {
let Some(next_bin_op_precedence) = ast::BinOp::precedence_of_token(next_bin_op_token)
else {
break;
};
let precedence_to_search;
if next_bin_op_precedence > bin_op_precedence {
precedence_to_search = bin_op_precedence + 1;
} else if ast::BinOp::is_right_associative_token(next_bin_op_token)
&& next_bin_op_precedence == bin_op_precedence
{
precedence_to_search = bin_op_precedence;
} else {
break;
}
rhs = match parse_expression_with_precedence(state, rhs, precedence_to_search) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(
bin_op.token().clone(),
"expected expression after binary operator",
);
return ParserResult::Value(lhs);
}
ParserResult::LexerMoved => return ParserResult::Value(lhs),
};
}
lhs = Expression::BinaryOperator {
lhs: Box::new(lhs),
binop: bin_op,
rhs: Box::new(rhs),
};
}
}
fn parse_unary_expression(
state: &mut ParserState,
unary_operator_token: ast::TokenReference,
) -> ParserResult<ast::Expression> {
let unary_operator = match unary_operator_token.token_type() {
TokenType::Symbol { symbol } => match symbol {
Symbol::Minus => ast::UnOp::Minus(unary_operator_token),
Symbol::Not => ast::UnOp::Not(unary_operator_token),
Symbol::Hash => ast::UnOp::Hash(unary_operator_token),
#[cfg(feature = "lua53")]
Symbol::Tilde if state.lua_version().has_lua53() => {
ast::UnOp::Tilde(unary_operator_token)
}
_ => unreachable!(),
},
_ => unreachable!(),
};
let primary_expression = match parse_primary_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(
unary_operator.token().clone(),
format!(
"expected an expression after {}",
unary_operator.token().token()
),
);
return ParserResult::NotFound;
}
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
let expression = match parse_expression_with_precedence(
state,
primary_expression,
ast::UnOp::precedence(),
) {
ParserResult::Value(expression) => expression,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
ParserResult::NotFound => {
state.token_error(
unary_operator.token().clone(),
format!(
"expected an expression after {}",
unary_operator.token().token()
),
);
return ParserResult::LexerMoved;
}
};
ParserResult::Value(Expression::UnaryOperator {
unop: unary_operator,
expression: Box::new(expression),
})
}
fn parse_function_body(state: &mut ParserState) -> ParserResult<FunctionBody> {
const NO_TRAILING_COMMAS_ERROR: &str = "trailing commas in arguments are not allowed";
#[cfg(feature = "luau")]
let generics = match parse_generic_type_list(state, TypeListStyle::Plain) {
ParserResult::Value(generic_declaration) => Some(generic_declaration),
ParserResult::NotFound => None,
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
let Some(left_parenthesis) = state.consume_if(Symbol::LeftParen) else {
return ParserResult::NotFound;
};
let mut parameters = Punctuated::new();
#[cfg(feature = "luau")]
let mut type_specifiers = Vec::new();
let right_parenthesis;
let unfinished_function_body =
|left_parenthesis: TokenReference, mut parameters: Punctuated<Parameter>| {
if matches!(parameters.last(), Some(Pair::Punctuated(..))) {
let last_parameter = parameters.pop().unwrap();
parameters.push(Pair::End(last_parameter.into_value()));
}
ParserResult::Value(FunctionBody {
#[cfg(feature = "luau")]
generics: None, parameters_parentheses: ContainedSpan::new(
left_parenthesis,
TokenReference::basic_symbol(")"),
),
parameters,
#[cfg(feature = "luau")]
type_specifiers: Vec::new(), #[cfg(feature = "luau")]
return_type: None,
block: ast::Block::new(),
end_token: TokenReference::basic_symbol("end"),
})
};
loop {
match state.current() {
Ok(token) if token.is_symbol(Symbol::RightParen) => {
right_parenthesis = state.consume().unwrap();
break;
}
Ok(token) if token.is_symbol(Symbol::Ellipsis) => {
let ellipsis = state.consume().unwrap();
parameters.push(Pair::End(ast::Parameter::Ellipsis(ellipsis)));
#[cfg(feature = "luau")]
if state.lua_version().has_luau() {
let type_specifier = if let Some(colon) = state.consume_if(Symbol::Colon) {
let type_info = if matches!(state.current(), Ok(token) if token.token_kind() == TokenKind::Identifier)
&& matches!(state.peek(), Ok(token) if token.is_symbol(Symbol::Ellipsis))
{
let name = match parse_name(state) {
ParserResult::Value(name) => name.name,
_ => unreachable!(),
};
let Some(ellipsis) =
state.require(Symbol::Ellipsis, "expected `...` after type name")
else {
unreachable!()
};
ast::TypeInfo::GenericPack { name, ellipsis }
} else {
match parse_type(state) {
ParserResult::Value(type_info) => type_info,
_ => return unfinished_function_body(left_parenthesis, parameters),
}
};
Some(ast::TypeSpecifier {
punctuation: colon,
type_info,
})
} else {
None
};
type_specifiers.push(type_specifier);
}
right_parenthesis = match state.require(Symbol::RightParen, "expected a `)`") {
Some(right_parenthesis) => right_parenthesis,
None => return unfinished_function_body(left_parenthesis, parameters),
};
break;
}
Ok(TokenReference {
token:
Token {
token_type: TokenType::Identifier { .. },
..
},
..
}) => {
let name_parameter = match parse_name_with_type_specifiers(state) {
ParserResult::Value(name) => {
#[cfg(feature = "luau")]
type_specifiers.push(name.type_specifier);
ast::Parameter::Name(name.name)
}
_ => unreachable!(),
};
let Some(comma) = state.consume_if(Symbol::Comma) else {
parameters.push(Pair::End(name_parameter));
match state.require(Symbol::RightParen, "expected a `)`") {
Some(new_right_parenthesis) => {
right_parenthesis = new_right_parenthesis;
break;
}
None => return unfinished_function_body(left_parenthesis, parameters),
};
};
parameters.push(Pair::Punctuated(name_parameter, comma));
}
Ok(token) => {
state.token_error(token.clone(), "expected a parameter name or `)`");
return unfinished_function_body(left_parenthesis, parameters);
}
Err(()) => {
return unfinished_function_body(left_parenthesis, parameters);
}
}
}
if matches!(parameters.last(), Some(Pair::Punctuated(..))) {
let last_parameter = parameters.pop().unwrap();
state.token_error(
last_parameter.punctuation().unwrap().clone(),
NO_TRAILING_COMMAS_ERROR,
);
parameters.push(Pair::End(last_parameter.into_value()));
}
#[cfg(feature = "luau")]
let return_type = if state.lua_version().has_luau() {
if let Some(punctuation) = state.consume_if(Symbol::Colon) {
match parse_return_type(state) {
ParserResult::Value(type_info) => Some(ast::TypeSpecifier {
punctuation,
type_info,
}),
_ => return ParserResult::LexerMoved,
}
} else if let Some(punctuation) = state.consume_if(Symbol::ThinArrow) {
state.token_error(
punctuation.clone(),
"function return type annotations should use `:` instead of `->`",
);
match parse_return_type(state) {
ParserResult::Value(type_info) => Some(ast::TypeSpecifier {
punctuation,
type_info,
}),
_ => return ParserResult::LexerMoved,
}
} else {
None
}
} else {
None
};
let (block, end) = match expect_block_with_end(state, "function body", &right_parenthesis) {
Ok((block, end)) => (block, end),
Err(()) => return ParserResult::LexerMoved,
};
ParserResult::Value(FunctionBody {
#[cfg(feature = "luau")]
generics,
parameters_parentheses: ContainedSpan::new(left_parenthesis, right_parenthesis),
parameters,
#[cfg(feature = "luau")]
type_specifiers,
#[cfg(feature = "luau")]
return_type,
block,
end_token: end,
})
}
#[cfg(feature = "luau")]
fn expect_if_else_expression(
state: &mut ParserState,
if_token: TokenReference,
) -> Result<ast::IfExpression, ()> {
let ParserResult::Value(condition) = parse_expression(state) else {
return Err(());
};
let Some(then_token) = state.require(Symbol::Then, "expected `then` after condition") else {
return Err(());
};
let ParserResult::Value(if_expression) = parse_expression(state) else {
return Err(());
};
let mut else_if_expressions = Vec::new();
while let Some(else_if_token) = state.consume_if(Symbol::ElseIf) {
let ParserResult::Value(condition) = parse_expression(state) else {
return Err(());
};
let Some(then_token) = state.require(Symbol::Then, "expected `then` after condition")
else {
return Err(());
};
let ParserResult::Value(expression) = parse_expression(state) else {
return Err(());
};
else_if_expressions.push(ast::ElseIfExpression {
else_if_token,
condition,
then_token,
expression,
})
}
let Some(else_token) = state.require(
Symbol::Else,
"expected `else` when parsing if then else expression",
) else {
return Err(());
};
let ParserResult::Value(else_expression) = parse_expression(state) else {
return Err(());
};
Ok(ast::IfExpression {
if_token,
condition: Box::new(condition),
then_token,
if_expression: Box::new(if_expression),
else_if_expressions: if else_if_expressions.is_empty() {
None
} else {
Some(else_if_expressions)
},
else_token,
else_expression: Box::new(else_expression),
})
}
#[cfg(feature = "luau")]
fn expect_interpolated_string(
state: &mut ParserState,
mut current: TokenReference,
) -> ast::InterpolatedString {
use crate::ShortString;
use super::luau::InterpolatedStringSegment;
let mut segments = Vec::new();
let first_string = current.clone();
loop {
let has_double_brace = if current.trailing_trivia.is_empty() {
if let Some(double_brace) = state.consume_if(Symbol::LeftBrace) {
state.token_error(
double_brace,
"unexpected double brace, try \\{ if you meant to escape",
);
true
} else {
false
}
} else {
false
};
let expression = match parse_expression(state) {
ParserResult::Value(expression) => expression,
ParserResult::NotFound => {
state.token_error(current, "expected expression after `{`");
break;
}
ParserResult::LexerMoved => break,
};
segments.push(InterpolatedStringSegment {
expression,
literal: current,
});
if has_double_brace {
state.consume_if(Symbol::RightBrace);
}
let Ok(next) = state.current() else {
break;
};
if matches!(
next.token_type(),
TokenType::InterpolatedString {
kind: InterpolatedStringKind::End,
..
}
) {
return ast::InterpolatedString {
segments,
last_string: state.consume().unwrap(),
};
}
if next.token_kind() != TokenKind::InterpolatedString {
state.token_error(
next.clone(),
"interpolated string parameter can only contain an expression",
);
break;
}
current = match state.consume() {
ParserResult::Value(token) => token,
ParserResult::NotFound | ParserResult::LexerMoved => break,
}
}
ast::InterpolatedString {
last_string: if segments.is_empty() {
first_string
} else {
TokenReference::new(
Vec::new(),
Token::new(TokenType::InterpolatedString {
literal: ShortString::default(),
kind: InterpolatedStringKind::End,
}),
Vec::new(),
)
},
segments,
}
}
#[cfg(feature = "luau")]
fn parse_type(state: &mut ParserState) -> ParserResult<ast::TypeInfo> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
if let TokenType::Symbol {
symbol: Symbol::Pipe | Symbol::Ampersand,
} = current_token.token_type()
{
parse_type_suffix(state, None)
} else {
let ParserResult::Value(simple_type) = parse_simple_type(state, SimpleTypeStyle::Default)
else {
return ParserResult::LexerMoved;
};
parse_type_suffix(state, Some(simple_type))
}
}
#[cfg(feature = "luau")]
fn parse_type_or_pack(state: &mut ParserState) -> ParserResult<ast::TypeInfo> {
if matches!(
state.current(),
Ok(token) if matches!(
token.token_type(),
TokenType::Symbol { symbol: Symbol::Pipe | Symbol::Ampersand }
)
) {
return parse_type_suffix(state, None);
}
let ParserResult::Value(simple_type) = parse_simple_type(state, SimpleTypeStyle::AllowPack)
else {
return ParserResult::LexerMoved;
};
match simple_type {
ast::TypeInfo::Tuple { ref types, .. } if types.len() != 1 => {
ParserResult::Value(simple_type)
}
ast::TypeInfo::VariadicPack { .. } | ast::TypeInfo::GenericPack { .. } => unreachable!(),
_ => parse_type_suffix(state, Some(simple_type)),
}
}
#[cfg(feature = "luau")]
fn parse_type_pack(state: &mut ParserState) -> ParserResult<ast::TypeInfo> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
if current_token.is_symbol(Symbol::Ellipsis) {
let ellipsis = state.consume().unwrap();
let ParserResult::Value(type_info) = parse_type(state) else {
return ParserResult::LexerMoved;
};
ParserResult::Value(ast::TypeInfo::Variadic {
ellipsis,
type_info: Box::new(type_info),
})
} else if current_token.token_kind() == TokenKind::Identifier
&& matches!(state.peek(), Ok(token) if token.is_symbol(Symbol::Ellipsis))
{
let name = match parse_name(state) {
ParserResult::Value(name) => name.name,
_ => unreachable!(),
};
let Some(ellipsis) = state.require(Symbol::Ellipsis, "expected `...` after type name")
else {
unreachable!()
};
ParserResult::Value(ast::TypeInfo::GenericPack { name, ellipsis })
} else {
ParserResult::NotFound
}
}
#[cfg(feature = "luau")]
#[derive(PartialEq, Eq)]
enum SimpleTypeStyle {
Default,
AllowPack,
}
#[cfg(feature = "luau")]
fn parse_simple_type(
state: &mut ParserState,
style: SimpleTypeStyle,
) -> ParserResult<ast::TypeInfo> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
match current_token.token_type() {
TokenType::Symbol {
symbol: Symbol::Nil,
} => {
let nil_token = state.consume().unwrap();
ParserResult::Value(ast::TypeInfo::Basic(nil_token))
}
TokenType::Symbol {
symbol: Symbol::True,
}
| TokenType::Symbol {
symbol: Symbol::False,
} => {
let token = state.consume().unwrap();
ParserResult::Value(ast::TypeInfo::Boolean(token))
}
TokenType::StringLiteral { .. } => {
let token = state.consume().unwrap();
ParserResult::Value(ast::TypeInfo::String(token))
}
TokenType::InterpolatedString { .. } => {
let token = state.consume().unwrap();
state.token_error(
token,
"interpolated string literals cannot be used as types",
);
ParserResult::LexerMoved
}
TokenType::Identifier { .. } => {
let name = state.consume().unwrap();
if name.token.to_string() == "typeof" {
let left_parenthesis =
match state.require(Symbol::LeftParen, "expected `(` after `typeof`") {
Some(token) => token,
None => TokenReference::basic_symbol("("),
};
let ParserResult::Value(expression) = parse_expression(state) else {
return ParserResult::LexerMoved;
};
let right_parenthesis = match state.require_with_reference_token(
Symbol::RightParen,
"expected `)` to close typeof call",
&left_parenthesis,
) {
Some(token) => token,
None => TokenReference::basic_symbol(")"),
};
ParserResult::Value(ast::TypeInfo::Typeof {
typeof_token: name,
parentheses: ContainedSpan::new(left_parenthesis, right_parenthesis),
inner: Box::new(expression),
})
} else {
match state.current() {
Ok(token) if token.is_symbol(Symbol::Dot) => {
let punctuation = state.consume().unwrap();
let type_base = match parse_name(state) {
ParserResult::Value(name) => name.name,
ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected identifier after `.`",
);
return ParserResult::LexerMoved;
}
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
let type_info = if let Some(left_arrow) = state.consume_if(Symbol::LessThan)
{
let Ok(generics) = expect_generic_type_params(state, left_arrow) else {
return ParserResult::LexerMoved;
};
ast::IndexedTypeInfo::Generic {
base: type_base,
arrows: generics.arrows,
generics: generics.generics,
}
} else {
ast::IndexedTypeInfo::Basic(type_base)
};
ParserResult::Value(ast::TypeInfo::Module {
module: name,
punctuation,
type_info: Box::new(type_info),
})
}
Ok(token) if token.is_symbol(Symbol::LessThan) => {
let left_arrow = state.consume().unwrap();
let Ok(generics) = expect_generic_type_params(state, left_arrow) else {
return ParserResult::LexerMoved;
};
ParserResult::Value(ast::TypeInfo::Generic {
base: name,
arrows: generics.arrows,
generics: generics.generics,
})
}
_ => ParserResult::Value(ast::TypeInfo::Basic(name)),
}
}
}
TokenType::Symbol {
symbol: Symbol::LeftBrace,
} => {
let left_brace = state.consume().unwrap();
match expect_type_table(state, left_brace) {
Ok(table) => ParserResult::Value(table),
Err(_) => ParserResult::LexerMoved,
}
}
TokenType::Symbol {
symbol: Symbol::LeftParen,
}
| TokenType::Symbol {
symbol: Symbol::LessThan,
} => match expect_function_type(state, style) {
Ok(type_info) => ParserResult::Value(type_info),
Err(_) => ParserResult::LexerMoved,
},
_ => ParserResult::NotFound,
}
}
#[cfg(feature = "luau")]
fn parse_type_suffix(
state: &mut ParserState,
simple_type: Option<ast::TypeInfo>,
) -> ParserResult<ast::TypeInfo> {
let mut is_union = false;
let mut is_intersection = false;
let mut types = Punctuated::new();
let leading = if simple_type.is_some() {
None
} else {
let Ok(current_token) = state.current() else {
unreachable!("parse_type_suffix called with no simple_type and no current token")
};
match current_token.token_type() {
TokenType::Symbol {
symbol: Symbol::Pipe,
} => is_union = true,
TokenType::Symbol {
symbol: Symbol::Ampersand,
} => is_intersection = true,
_ => unreachable!("parse_type_suffix called with no simple_type and no `|` or `&`"),
}
Some(state.consume().unwrap())
};
let mut current_type = simple_type;
loop {
let type_info = if current_type.is_some() {
current_type.take().unwrap()
} else {
let ParserResult::Value(type_info) = parse_simple_type(state, SimpleTypeStyle::Default)
else {
return ParserResult::LexerMoved;
};
type_info
};
let Ok(current_token) = state.current() else {
types.push(Pair::End(type_info));
break;
};
match current_token.token_type() {
TokenType::Symbol {
symbol: Symbol::QuestionMark,
} => {
if is_intersection {
state.token_error(
current_token.clone(),
"cannot mix union and intersection types",
);
return ParserResult::LexerMoved;
}
let question_mark = state.consume().unwrap();
current_type = Some(ast::TypeInfo::Optional {
base: Box::new(type_info),
question_mark,
});
is_union = true;
}
TokenType::Symbol {
symbol: Symbol::Pipe,
} => {
if is_intersection {
state.token_error(
current_token.clone(),
"cannot mix union and intersection types",
);
return ParserResult::LexerMoved;
}
let pipe = state.consume().unwrap();
types.push(Pair::new(type_info, Some(pipe)));
is_union = true;
}
TokenType::Symbol {
symbol: Symbol::Ampersand,
} => {
if is_union {
state.token_error(
current_token.clone(),
"cannot mix union and intersection types",
);
return ParserResult::LexerMoved;
}
let ampersand = state.consume().unwrap();
types.push(Pair::new(type_info, Some(ampersand)));
is_intersection = true;
}
_ if types.is_empty() && leading.is_none() => return ParserResult::Value(type_info),
_ => {
types.push(Pair::End(type_info));
break;
}
}
}
match (is_union, is_intersection) {
(true, false) => {
ParserResult::Value(ast::TypeInfo::Union(ast::TypeUnion::new(leading, types)))
}
(false, true) => ParserResult::Value(ast::TypeInfo::Intersection(
ast::TypeIntersection::new(leading, types),
)),
_ => unreachable!(),
}
}
#[cfg(feature = "luau")]
fn expect_type_table(
state: &mut ParserState,
left_brace: TokenReference,
) -> Result<ast::TypeInfo, ()> {
let mut fields = Punctuated::new();
let mut has_indexer = false;
let mut array_type = None;
loop {
let access = if matches!(state.current(), Ok(token) if token.token_kind() == TokenKind::Identifier && matches!(token.token().to_string().as_str(), "read" | "write"))
&& !matches!(state.peek(), Ok(token) if token.is_symbol(Symbol::Colon))
{
Some(state.consume().unwrap())
} else {
None
};
let current_token = state.current()?;
let field = if current_token.is_symbol(Symbol::RightBrace) {
debug_assert!(
array_type.is_none(),
"consuming right brace in loop but have seen array type"
);
let braces = ContainedSpan::new(left_brace, state.consume().unwrap());
return Ok(ast::TypeInfo::Table { braces, fields });
} else if current_token.is_symbol(Symbol::LeftBracket)
&& matches!(state.peek(), Ok(token) if token.token_kind() == TokenKind::StringLiteral)
{
let left_brace = state.consume().unwrap();
let property = state.consume().unwrap();
let Some(right_brace) = state.require(
Symbol::RightBracket,
"expected `]` to close `[` for type table field",
) else {
return Err(());
};
let Some(colon) = state.require(Symbol::Colon, "expected `:` after type field key")
else {
return Err(());
};
let value = match parse_type(state) {
ParserResult::Value(value) => value,
ParserResult::LexerMoved | ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected type after type field key",
);
return Err(());
}
};
ast::TypeField {
access,
key: ast::TypeFieldKey::IndexSignature {
brackets: ContainedSpan::new(left_brace, right_brace),
inner: ast::TypeInfo::String(property),
},
colon,
value,
}
} else if current_token.is_symbol(Symbol::LeftBracket) {
let left_brace = state.consume().unwrap();
let key = match parse_type(state) {
ParserResult::Value(value) => value,
ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected type for type field key",
);
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
let Some(right_brace) = state.require(
Symbol::RightBracket,
"expected `]` to close `[` for type table field",
) else {
return Err(());
};
let Some(colon) = state.require(Symbol::Colon, "expected `:` after type field key")
else {
return Err(());
};
let value = match parse_type(state) {
ParserResult::Value(value) => value,
ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected type after type field key",
);
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
if has_indexer {
state.token_error_ranged(
left_brace.clone(),
"cannot have more than one table indexer",
&left_brace,
value.tokens().next_back().unwrap(),
);
}
has_indexer = true;
ast::TypeField {
access,
key: ast::TypeFieldKey::IndexSignature {
brackets: ContainedSpan::new(left_brace, right_brace),
inner: key,
},
colon,
value,
}
} else if fields.is_empty()
&& !has_indexer
&& !(current_token.token_kind() == TokenKind::Identifier
&& matches!(state.peek(), Ok(token) if token.is_symbol(Symbol::Colon)))
{
array_type = Some((
access,
match parse_type(state) {
ParserResult::Value(value) => value,
ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected type for table array",
);
return Err(());
}
ParserResult::LexerMoved => return Err(()),
},
));
break;
} else {
match parse_name(state) {
ParserResult::Value(name) => {
let Some(colon) =
state.require(Symbol::Colon, "expected `:` after type field key")
else {
return Err(());
};
let value = match parse_type(state) {
ParserResult::Value(value) => value,
ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected type after type field key",
);
return Err(());
}
ParserResult::LexerMoved => return Err(()),
};
ast::TypeField {
access,
key: ast::TypeFieldKey::Name(name.name),
colon,
value,
}
}
ParserResult::NotFound => break,
ParserResult::LexerMoved => return Err(()),
}
};
match state.current() {
Ok(token) => {
if token.is_symbol(Symbol::Comma) || token.is_symbol(Symbol::Semicolon) {
fields.push(Pair::Punctuated(field, state.consume().unwrap()))
} else {
fields.push(Pair::End(field));
break;
}
}
Err(()) => {
fields.push(Pair::End(field));
break;
}
};
}
let right_brace = state
.require(Symbol::RightBrace, "expected `}` to close type table")
.unwrap_or_else(|| TokenReference::basic_symbol("}"));
let braces = ContainedSpan::new(left_brace, right_brace);
if let Some((access, type_info)) = array_type {
Ok(ast::TypeInfo::Array {
braces,
access,
type_info: Box::new(type_info),
})
} else {
Ok(ast::TypeInfo::Table { braces, fields })
}
}
#[cfg(feature = "luau")]
fn expect_function_type(
state: &mut ParserState,
style: SimpleTypeStyle,
) -> Result<ast::TypeInfo, ()> {
let mut force_function_type =
matches!(state.current(), Ok(token) if token.is_symbol(Symbol::LessThan));
let generics = match parse_generic_type_list(state, TypeListStyle::Plain) {
ParserResult::Value(generics) => Some(generics),
ParserResult::NotFound => None,
ParserResult::LexerMoved => return Err(()),
};
let mut arguments = Punctuated::new();
let Some(left_paren) = state.require(Symbol::LeftParen, "expected `(`") else {
return Err(());
};
while !matches!(state.current(), Ok(token) if token.is_symbol(Symbol::RightParen)) {
match parse_type_pack(state) {
ParserResult::Value(type_info) => {
arguments.push(Pair::End(ast::TypeArgument {
name: None,
type_info,
}));
break;
}
ParserResult::LexerMoved => return Err(()),
ParserResult::NotFound => (),
}
let Ok(current_token) = state.current() else {
return Err(());
};
let name = if current_token.token_kind() == TokenKind::Identifier
&& matches!(state.peek(), Ok(token) if token.is_symbol(Symbol::Colon))
{
Some((state.consume().unwrap(), state.consume().unwrap()))
} else {
None
};
let ParserResult::Value(type_info) = parse_type(state) else {
return Err(());
};
let type_argument = ast::TypeArgument { name, type_info };
if !matches!(state.current(), Ok(token) if token.is_symbol(Symbol::Comma)) {
arguments.push(Pair::End(type_argument));
break;
}
let punctuation = state.consume().unwrap();
arguments.push(Pair::Punctuated(type_argument, punctuation));
if matches!(state.current(), Ok(token) if token.is_symbol(Symbol::RightParen)) {
state.token_error(
state.current().unwrap().clone(),
"expected type after `,` but got `)` instead",
);
break;
}
}
let Some(right_paren) = state.require(Symbol::RightParen, "expected `)` to close `(`") else {
return Err(());
};
let parentheses = ContainedSpan::new(left_paren, right_paren);
if arguments
.iter()
.any(|arg: &ast::TypeArgument| arg.name().is_some())
{
force_function_type = true;
}
if !force_function_type
&& !matches!(state.current(), Ok(token) if token.is_symbol(Symbol::ThinArrow))
{
if (arguments.len() == 1
&& !matches!(
arguments.iter().next().unwrap().type_info,
ast::TypeInfo::Variadic { .. }
))
|| style == SimpleTypeStyle::AllowPack
{
return Ok(ast::TypeInfo::Tuple {
parentheses,
types: arguments
.into_pairs()
.map(|pair| pair.map(|argument| argument.type_info))
.collect(),
});
}
}
let Some(arrow) = state.require(
Symbol::ThinArrow,
"expected `->` after `()` for function type",
) else {
return Err(());
};
let return_type = match parse_return_type(state) {
ParserResult::Value(return_type) => return_type,
ParserResult::LexerMoved | ParserResult::NotFound => return Err(()),
};
Ok(ast::TypeInfo::Callback {
generics,
parentheses,
arguments,
arrow,
return_type: Box::new(return_type),
})
}
#[cfg(feature = "luau")]
fn expect_type_specifier(
state: &mut ParserState,
punctuation: TokenReference,
) -> Result<ast::TypeSpecifier, ()> {
let ParserResult::Value(type_info) = parse_type(state) else {
state.token_error(punctuation, "expected type info after `:`");
return Err(());
};
Ok(ast::TypeSpecifier {
punctuation,
type_info,
})
}
#[cfg(feature = "luau")]
fn parse_return_type(state: &mut ParserState) -> ParserResult<ast::TypeInfo> {
match parse_type_pack(state) {
ParserResult::Value(type_info) => ParserResult::Value(type_info),
ParserResult::NotFound => parse_type_or_pack(state),
ParserResult::LexerMoved => ParserResult::LexerMoved,
}
}
#[cfg(feature = "luau")]
#[derive(PartialEq, Eq)]
enum TypeListStyle {
Plain,
WithDefaults,
}
#[cfg(feature = "luau")]
fn parse_generic_type_list(
state: &mut ParserState,
style: TypeListStyle,
) -> ParserResult<ast::GenericDeclaration> {
if !state.lua_version().has_luau() {
return ParserResult::NotFound;
}
let Some(left_angle_bracket) = state.consume_if(Symbol::LessThan) else {
return ParserResult::NotFound;
};
let mut generics = Punctuated::new();
let mut seen_pack = false;
let mut seen_default = false;
loop {
let name = match state.current() {
Ok(token) if token.token_kind() == TokenKind::Identifier => state.consume().unwrap(),
Ok(token) => {
state.token_error(token.clone(), "expected a generic type name");
error_token()
}
Err(()) => return ParserResult::LexerMoved,
};
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
let (parameter, default) = if seen_pack || current_token.is_symbol(Symbol::Ellipsis) {
seen_pack = true;
let ellipsis = match state.consume_if(Symbol::Ellipsis) {
Some(token) => token,
None => {
state.token_error(name.clone(), "generic types come before generic type packs");
TokenReference::basic_symbol("...")
}
};
let default = if style == TypeListStyle::WithDefaults
&& matches!(state.current(), Ok(token) if token.is_symbol(Symbol::Equal))
{
seen_default = true;
let equal_token = state.consume().unwrap();
let default_type = match parse_type_pack(state) {
ParserResult::Value(type_info) => type_info,
ParserResult::NotFound => match parse_type_or_pack(state) {
ParserResult::Value(type_info)
if matches!(type_info, ast::TypeInfo::Tuple { .. }) =>
{
type_info
}
ParserResult::Value(type_info) => {
state.token_error_ranged(
equal_token.clone(),
"expected type pack after `=` but got type instead",
type_info.tokens().next().unwrap(),
type_info.tokens().next_back().unwrap(),
);
type_info
}
_ => return ParserResult::LexerMoved,
},
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
Some((equal_token, default_type))
} else {
if seen_default {
state.token_error(
state.current().unwrap().clone(),
"expected default type after type name",
)
}
None
};
(
ast::GenericParameterInfo::Variadic { name, ellipsis },
default,
)
} else {
let default = if style == TypeListStyle::WithDefaults
&& matches!(state.current(), Ok(token) if token.is_symbol(Symbol::Equal))
{
seen_default = true;
let equal_token = state.consume().unwrap();
let default_type = match parse_type(state) {
ParserResult::Value(default_type) => default_type,
_ => return ParserResult::LexerMoved,
};
Some((equal_token, default_type))
} else {
if seen_default {
state.token_error(
state.current().unwrap().clone(),
"expected default type after type name",
)
}
None
};
(ast::GenericParameterInfo::Name(name), default)
};
match state.current() {
Ok(token) if token.is_symbol(Symbol::Comma) => {
let punctuation = state.consume().unwrap();
generics.push(Pair::Punctuated(
ast::GenericDeclarationParameter { parameter, default },
punctuation,
));
if let Ok(token) = state.current() {
if token.is_symbol(Symbol::GreaterThan) {
state.token_error(
token.clone(),
"expected type after `,` but got `>` instead",
);
}
}
}
Ok(_) => {
generics.push(Pair::End(ast::GenericDeclarationParameter {
parameter,
default,
}));
break;
}
Err(()) => return ParserResult::LexerMoved,
};
}
let right_angle_bracket = state
.require(Symbol::GreaterThan, "expected `>` to close generic list")
.unwrap_or_else(|| TokenReference::basic_symbol(">"));
ParserResult::Value(ast::GenericDeclaration {
arrows: ContainedSpan::new(left_angle_bracket, right_angle_bracket),
generics,
})
}
#[cfg(feature = "luau")]
struct GenericTypeParams {
arrows: ContainedSpan,
generics: Punctuated<ast::TypeInfo>,
}
#[cfg(feature = "luau")]
fn expect_generic_type_params(
state: &mut ParserState,
left_arrow: TokenReference,
) -> Result<GenericTypeParams, ()> {
let mut generics = Punctuated::new();
loop {
if state.current()?.is_symbol(Symbol::GreaterThan) {
break;
}
let type_info = match parse_type_pack(state) {
ParserResult::Value(type_info) => type_info,
ParserResult::NotFound => match parse_type_or_pack(state) {
ParserResult::Value(type_info) => type_info,
_ => return Err(()),
},
ParserResult::LexerMoved => return Err(()),
};
if let Some(punctuation) = state.consume_if(Symbol::Comma) {
generics.push(Pair::Punctuated(type_info, punctuation));
if state.current()?.is_symbol(Symbol::GreaterThan) {
state.token_error(
state.current()?.clone(),
"expected type after ',' but got '>' instead",
);
break;
}
} else {
generics.push(Pair::End(type_info));
break;
}
}
let Some(right_arrow) = state.require(
Symbol::GreaterThan,
"expected '>' to close generic type parameter list",
) else {
return Err(());
};
Ok(GenericTypeParams {
arrows: ContainedSpan::new(left_arrow, right_arrow),
generics,
})
}
#[cfg(feature = "luau")]
fn parse_attributes(state: &mut ParserState) -> ParserResult<Vec<ast::LuauAttribute>> {
debug_assert!(state
.current()
.is_ok_and(|token| token.is_symbol(Symbol::AtSign)));
let mut attributes = Vec::new();
while let Some(at_sign) = state.consume_if(Symbol::AtSign) {
let name = match parse_name(state) {
ParserResult::Value(name) => name.name,
ParserResult::NotFound => {
state.token_error(
state.current().unwrap().clone(),
"expected identifier after `@`",
);
return ParserResult::LexerMoved;
}
ParserResult::LexerMoved => return ParserResult::LexerMoved,
};
attributes.push(ast::LuauAttribute { at_sign, name });
}
ParserResult::Value(attributes)
}
#[derive(Clone)]
struct Name {
name: TokenReference,
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
attribute: Option<super::lua54::Attribute>,
#[cfg(feature = "luau")]
type_specifier: Option<ast::TypeSpecifier>,
}
#[cfg(feature = "luau")]
fn parse_name(state: &mut ParserState) -> ParserResult<Name> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
match current_token.token_type() {
TokenType::Identifier { .. } => {
let name_token = state.consume().unwrap();
ParserResult::Value(force_name(state, name_token))
}
_ => ParserResult::NotFound,
}
}
fn parse_name_with_attributes(state: &mut ParserState) -> ParserResult<Name> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
match current_token.token_type() {
TokenType::Identifier { .. } => {
let name_token = state.consume().unwrap();
ParserResult::Value(force_name_with_attributes(state, name_token))
}
_ => ParserResult::NotFound,
}
}
fn parse_name_with_type_specifiers(state: &mut ParserState) -> ParserResult<Name> {
let Ok(current_token) = state.current() else {
return ParserResult::NotFound;
};
match current_token.token_type() {
TokenType::Identifier { .. } => {
let name_token = state.consume().unwrap();
ParserResult::Value(force_name_with_type_specifiers(state, name_token))
}
_ => ParserResult::NotFound,
}
}
fn force_name(_state: &mut ParserState, name: TokenReference) -> Name {
Name {
name,
#[cfg(feature = "lua54")]
attribute: None,
#[cfg(feature = "luau")]
type_specifier: None,
}
}
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
fn force_name_with_attributes(state: &mut ParserState, name: TokenReference) -> Name {
if !state.lua_version().has_lua54() && !state.lua_version().has_cfxlua() {
return force_name_with_type_specifiers(state, name);
}
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
if let Some(left_angle_bracket) = state.consume_if(Symbol::LessThan) {
const ERROR_INVALID_ATTRIBUTE: &str = "expected identifier after `<` for attribute";
let attribute_name = match state.current() {
Ok(token) if matches!(token.token_type(), TokenType::Identifier { .. }) => {
state.consume().unwrap()
}
Ok(token) => {
state.token_error_ranged(
token.clone(),
ERROR_INVALID_ATTRIBUTE,
&left_angle_bracket,
&token.clone(),
);
return Name {
name,
attribute: None,
#[cfg(feature = "luau")]
type_specifier: None,
};
}
Err(()) => {
state.token_error(left_angle_bracket, ERROR_INVALID_ATTRIBUTE);
return Name {
name,
attribute: None,
#[cfg(feature = "luau")]
type_specifier: None,
};
}
};
let Some(right_angle_bracket) =
state.require(Symbol::GreaterThan, "expected `>` to close attribute")
else {
return Name {
name,
attribute: Some(super::lua54::Attribute {
brackets: ContainedSpan::new(
left_angle_bracket,
TokenReference::basic_symbol(">"),
),
name: attribute_name,
}),
#[cfg(feature = "luau")]
type_specifier: None,
};
};
return Name {
name,
attribute: Some(super::lua54::Attribute {
brackets: ContainedSpan::new(left_angle_bracket, right_angle_bracket),
name: attribute_name,
}),
#[cfg(feature = "luau")]
type_specifier: None,
};
}
force_name_with_type_specifiers(state, name)
}
#[cfg(feature = "luau")]
fn force_name_with_type_specifiers(state: &mut ParserState, name: TokenReference) -> Name {
if !state.lua_version().has_luau() {
return force_name(state, name);
}
if let Some(punctuation) = state.consume_if(Symbol::Colon) {
let Ok(type_specifier) = expect_type_specifier(state, punctuation) else {
return Name {
name,
#[cfg(feature = "lua54")]
attribute: None,
type_specifier: None,
};
};
Name {
name,
#[cfg(feature = "lua54")]
attribute: None,
type_specifier: Some(type_specifier),
}
} else {
force_name(state, name)
}
}
#[cfg(not(feature = "luau"))]
fn force_name_with_type_specifiers(state: &mut ParserState, name: TokenReference) -> Name {
force_name(state, name)
}
#[cfg(not(feature = "lua54"))]
fn force_name_with_attributes(state: &mut ParserState, name: TokenReference) -> Name {
force_name_with_type_specifiers(state, name)
}
#[cfg(feature = "luau")]
fn expect_type_instantiation(
state: &mut ParserState,
outer_0: TokenReference,
) -> Result<super::luau::TypeInstantiation, ()> {
let Some(inner_0) = state.require(
Symbol::LessThan,
"expected second `<` in type instantiation",
) else {
return Err(());
};
let types = expect_generic_type_params(state, inner_0)?;
let Some(outer_1) = state.require(
Symbol::GreaterThan,
"expected second `>` in type instantiation",
) else {
return Err(());
};
Ok(super::luau::TypeInstantiation {
outer_arrows: ContainedSpan::new(outer_0, outer_1),
inner_arrows: types.arrows,
types: types.generics,
})
}
fn one_or_more<T, F: Fn(&mut ParserState) -> ParserResult<T>>(
state: &mut ParserState,
parser: F,
delimiter: Symbol,
) -> ParserResult<Punctuated<T>> {
let mut values = Punctuated::new();
loop {
let value = match parser(state) {
ParserResult::Value(value) => value,
ParserResult::NotFound | ParserResult::LexerMoved => break,
};
match state.consume_if(delimiter) {
Some(delimiter) => {
values.push(Pair::Punctuated(value, delimiter));
}
None => {
values.push(Pair::End(value));
break;
}
}
}
if values.is_empty() {
return ParserResult::NotFound;
}
if let Some(Pair::Punctuated(..)) = values.last() {
let last_value = values.pop().unwrap();
state.token_error(
last_value.punctuation().unwrap().clone(),
"trailing commas are not allowed",
);
values.push(Pair::End(last_value.into_value()));
}
ParserResult::Value(values)
}
fn parse_name_list(state: &mut ParserState) -> ParserResult<Punctuated<Name>> {
one_or_more(state, parse_name_with_type_specifiers, Symbol::Comma)
}
fn parse_expression_list(state: &mut ParserState) -> ParserResult<Punctuated<Expression>> {
one_or_more(state, parse_expression, Symbol::Comma)
}