use std::fmt::Formatter;
use std::{borrow::Cow, fmt};
use derive_more::Display;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use full_moon_derive::{Node, Visit};
#[cfg(any(feature = "lua52", feature = "luajit"))]
use lua52::*;
#[cfg(feature = "lua54")]
use lua54::*;
#[cfg(feature = "luau")]
use luau::*;
#[cfg(any(feature = "luau", feature = "cfxlua"))]
mod compound;
#[cfg(any(feature = "luau", feature = "cfxlua"))]
pub use compound::*;
pub use parser_structs::AstResult;
use punctuated::{Pair, Punctuated};
use span::ContainedSpan;
pub use versions::*;
use crate::{
tokenizer::{Position, Symbol, Token, TokenReference, TokenType},
util::*,
};
mod parser_structs;
#[macro_use]
mod parser_util;
mod parsers;
pub mod punctuated;
pub mod span;
mod update_positions;
mod visitors;
#[cfg(feature = "luau")]
pub mod luau;
#[cfg(feature = "luau")]
mod luau_visitors;
mod versions;
#[cfg(any(feature = "lua52", feature = "luajit"))]
pub mod lua52;
#[cfg(feature = "lua54")]
pub mod lua54;
#[derive(Clone, Debug, Default, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
"{}{}",
display_optional_punctuated_vec(stmts),
display_option(last_stmt.as_ref().map(display_optional_punctuated))
)]
pub struct Block {
stmts: Vec<(Stmt, Option<TokenReference>)>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
last_stmt: Option<(LastStmt, Option<TokenReference>)>,
}
impl Block {
pub fn new() -> Self {
Self {
stmts: Vec::new(),
last_stmt: None,
}
}
pub fn stmts(&self) -> impl Iterator<Item = &Stmt> {
self.stmts.iter().map(|(stmt, _)| stmt)
}
pub fn stmts_with_semicolon(&self) -> impl Iterator<Item = &(Stmt, Option<TokenReference>)> {
self.stmts.iter()
}
pub fn last_stmt(&self) -> Option<&LastStmt> {
Some(&self.last_stmt.as_ref()?.0)
}
pub fn last_stmt_with_semicolon(&self) -> Option<&(LastStmt, Option<TokenReference>)> {
self.last_stmt.as_ref()
}
pub fn with_stmts(self, stmts: Vec<(Stmt, Option<TokenReference>)>) -> Self {
Self { stmts, ..self }
}
pub fn with_last_stmt(self, last_stmt: Option<(LastStmt, Option<TokenReference>)>) -> Self {
Self { last_stmt, ..self }
}
pub(crate) fn merge_blocks(&mut self, other: Self) {
self.stmts.extend(other.stmts);
if self.last_stmt.is_none() {
self.last_stmt = other.last_stmt;
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum LastStmt {
Break(TokenReference),
#[cfg(feature = "luau")]
Continue(TokenReference),
Return(Return),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{token}{returns}")]
pub struct Return {
token: TokenReference,
returns: Punctuated<Expression>,
}
impl Return {
pub fn new() -> Self {
Self {
token: TokenReference::basic_symbol("return "),
returns: Punctuated::new(),
}
}
pub fn token(&self) -> &TokenReference {
&self.token
}
pub fn returns(&self) -> &Punctuated<Expression> {
&self.returns
}
pub fn with_token(self, token: TokenReference) -> Self {
Self { token, ..self }
}
pub fn with_returns(self, returns: Punctuated<Expression>) -> Self {
Self { returns, ..self }
}
}
impl Default for Return {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Field {
#[display(
"{}{}{}{}{}",
brackets.tokens().0,
key,
brackets.tokens().1,
equal,
value
)]
ExpressionKey {
brackets: ContainedSpan,
key: Expression,
equal: TokenReference,
value: Expression,
},
#[display("{key}{equal}{value}")]
NameKey {
key: TokenReference,
equal: TokenReference,
value: Expression,
},
#[display("{dot}{name}")]
#[cfg(feature = "cfxlua")]
SetConstructor {
dot: TokenReference,
name: TokenReference,
},
#[display("{_0}")]
NoKey(Expression),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{}{}{}", braces.tokens().0, fields, braces.tokens().1)]
pub struct TableConstructor {
#[node(full_range)]
#[visit(contains = "fields")]
braces: ContainedSpan,
fields: Punctuated<Field>,
}
impl TableConstructor {
pub fn new() -> Self {
Self {
braces: ContainedSpan::new(
TokenReference::basic_symbol("{ "),
TokenReference::basic_symbol(" }"),
),
fields: Punctuated::new(),
}
}
pub fn braces(&self) -> &ContainedSpan {
&self.braces
}
pub fn fields(&self) -> &Punctuated<Field> {
&self.fields
}
pub fn with_braces(self, braces: ContainedSpan) -> Self {
Self { braces, ..self }
}
pub fn with_fields(self, fields: Punctuated<Field>) -> Self {
Self { fields, ..self }
}
}
impl Default for TableConstructor {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(not(feature = "luau"), display("{function_token}{body}"))]
#[cfg_attr(
feature = "luau",
display("{}{}{}", join_vec(attributes), function_token, body)
)]
pub struct AnonymousFunction {
#[cfg(feature = "luau")]
attributes: Vec<LuauAttribute>,
function_token: TokenReference,
body: FunctionBody,
}
impl AnonymousFunction {
pub fn new() -> Self {
AnonymousFunction {
#[cfg(feature = "luau")]
attributes: Vec::new(),
function_token: TokenReference::basic_symbol("function"),
body: FunctionBody::new(),
}
}
#[cfg(feature = "luau")]
pub fn attributes(&self) -> impl Iterator<Item = &LuauAttribute> {
self.attributes.iter()
}
pub fn function_token(&self) -> &TokenReference {
&self.function_token
}
pub fn body(&self) -> &FunctionBody {
&self.body
}
#[cfg(feature = "luau")]
pub fn with_attributes(self, attributes: Vec<LuauAttribute>) -> Self {
Self { attributes, ..self }
}
pub fn with_function_token(self, function_token: TokenReference) -> Self {
Self {
function_token,
..self
}
}
pub fn with_body(self, body: FunctionBody) -> Self {
Self { body, ..self }
}
}
impl Default for AnonymousFunction {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Expression {
#[display("{lhs}{binop}{rhs}")]
BinaryOperator {
lhs: Box<Expression>,
binop: BinOp,
rhs: Box<Expression>,
},
#[display("{}{}{}", contained.tokens().0, expression, contained.tokens().1)]
Parentheses {
#[node(full_range)]
contained: ContainedSpan,
expression: Box<Expression>,
},
#[display("{unop}{expression}")]
UnaryOperator {
unop: UnOp,
expression: Box<Expression>,
},
#[display("{_0}")]
Function(Box<AnonymousFunction>),
#[display("{_0}")]
FunctionCall(FunctionCall),
#[cfg(feature = "luau")]
#[display("{_0}")]
IfExpression(IfExpression),
#[cfg(feature = "luau")]
#[display("{_0}")]
InterpolatedString(InterpolatedString),
#[display("{_0}")]
TableConstructor(TableConstructor),
#[display("{_0}")]
Number(TokenReference),
#[display("{_0}")]
String(TokenReference),
#[display("{_0}")]
Symbol(TokenReference),
#[cfg(feature = "luau")]
#[display("{expression}{type_assertion}")]
TypeAssertion {
expression: Box<Expression>,
type_assertion: TypeAssertion,
},
#[display("{_0}")]
Var(Var),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Stmt {
#[display("{_0}")]
Assignment(Assignment),
#[display("{_0}")]
Do(Do),
#[display("{_0}")]
FunctionCall(FunctionCall),
#[display("{_0}")]
FunctionDeclaration(FunctionDeclaration),
#[display("{_0}")]
GenericFor(GenericFor),
#[display("{_0}")]
If(If),
#[display("{_0}")]
LocalAssignment(LocalAssignment),
#[display("{_0}")]
LocalFunction(LocalFunction),
#[display("{_0}")]
NumericFor(NumericFor),
#[display("{_0}")]
Repeat(Repeat),
#[display("{_0}")]
While(While),
#[cfg(any(feature = "luau", feature = "cfxlua"))]
#[display("{_0}")]
CompoundAssignment(CompoundAssignment),
#[cfg(feature = "luau")]
#[display("{_0}")]
ConstAssignment(ConstAssignment),
#[cfg(feature = "luau")]
#[display("{_0}")]
ConstFunction(ConstFunction),
#[cfg(feature = "luau")]
ExportedTypeDeclaration(ExportedTypeDeclaration),
#[cfg(feature = "luau")]
TypeDeclaration(TypeDeclaration),
#[cfg(feature = "luau")]
ExportedTypeFunction(ExportedTypeFunction),
#[cfg(feature = "luau")]
TypeFunction(TypeFunction),
#[cfg(any(feature = "lua52", feature = "luajit"))]
Goto(Goto),
#[cfg(any(feature = "lua52", feature = "luajit"))]
Label(Label),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Prefix {
#[display("{_0}")]
Expression(Box<Expression>),
#[display("{_0}")]
Name(TokenReference),
}
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Index {
#[display("{}{}{}", brackets.tokens().0, expression, brackets.tokens().1)]
Brackets {
brackets: ContainedSpan,
expression: Expression,
},
#[display("{dot}{name}")]
Dot {
dot: TokenReference,
name: TokenReference,
},
}
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum FunctionArgs {
#[display(
"{}{}{}",
parentheses.tokens().0,
arguments,
parentheses.tokens().1
)]
Parentheses {
#[node(full_range)]
parentheses: ContainedSpan,
arguments: Punctuated<Expression>,
},
#[display("{_0}")]
String(TokenReference),
#[display("{_0}")]
TableConstructor(TableConstructor),
}
impl FunctionArgs {
pub(crate) fn empty() -> Self {
FunctionArgs::Parentheses {
parentheses: ContainedSpan::new(
TokenReference::basic_symbol("("),
TokenReference::basic_symbol(")"),
),
arguments: Punctuated::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct NumericFor {
for_token: TokenReference,
index_variable: TokenReference,
equal_token: TokenReference,
start: Expression,
start_end_comma: TokenReference,
end: Expression,
end_step_comma: Option<TokenReference>,
step: Option<Expression>,
do_token: TokenReference,
block: Block,
end_token: TokenReference,
#[cfg(feature = "luau")]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
type_specifier: Option<TypeSpecifier>,
}
impl NumericFor {
pub fn new(index_variable: TokenReference, start: Expression, end: Expression) -> Self {
Self {
for_token: TokenReference::basic_symbol("for "),
index_variable,
equal_token: TokenReference::basic_symbol(" = "),
start,
start_end_comma: TokenReference::basic_symbol(", "),
end,
end_step_comma: None,
step: None,
do_token: TokenReference::basic_symbol(" do\n"),
block: Block::new(),
end_token: TokenReference::basic_symbol("\nend"),
#[cfg(feature = "luau")]
type_specifier: None,
}
}
pub fn for_token(&self) -> &TokenReference {
&self.for_token
}
pub fn index_variable(&self) -> &TokenReference {
&self.index_variable
}
pub fn equal_token(&self) -> &TokenReference {
&self.equal_token
}
pub fn start(&self) -> &Expression {
&self.start
}
pub fn start_end_comma(&self) -> &TokenReference {
&self.start_end_comma
}
pub fn end(&self) -> &Expression {
&self.end
}
pub fn end_step_comma(&self) -> Option<&TokenReference> {
self.end_step_comma.as_ref()
}
pub fn step(&self) -> Option<&Expression> {
self.step.as_ref()
}
pub fn do_token(&self) -> &TokenReference {
&self.do_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn end_token(&self) -> &TokenReference {
&self.end_token
}
#[cfg(feature = "luau")]
pub fn type_specifier(&self) -> Option<&TypeSpecifier> {
self.type_specifier.as_ref()
}
pub fn with_for_token(self, for_token: TokenReference) -> Self {
Self { for_token, ..self }
}
pub fn with_index_variable(self, index_variable: TokenReference) -> Self {
Self {
index_variable,
..self
}
}
pub fn with_equal_token(self, equal_token: TokenReference) -> Self {
Self {
equal_token,
..self
}
}
pub fn with_start(self, start: Expression) -> Self {
Self { start, ..self }
}
pub fn with_start_end_comma(self, start_end_comma: TokenReference) -> Self {
Self {
start_end_comma,
..self
}
}
pub fn with_end(self, end: Expression) -> Self {
Self { end, ..self }
}
pub fn with_end_step_comma(self, end_step_comma: Option<TokenReference>) -> Self {
Self {
end_step_comma,
..self
}
}
pub fn with_step(self, step: Option<Expression>) -> Self {
Self { step, ..self }
}
pub fn with_do_token(self, do_token: TokenReference) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: TokenReference) -> Self {
Self { end_token, ..self }
}
#[cfg(feature = "luau")]
pub fn with_type_specifier(self, type_specifier: Option<TypeSpecifier>) -> Self {
Self {
type_specifier,
..self
}
}
}
impl fmt::Display for NumericFor {
#[cfg(feature = "luau")]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}{}{}{}{}{}{}{}{}",
self.for_token,
self.index_variable,
display_option(self.type_specifier()),
self.equal_token,
self.start,
self.start_end_comma,
self.end,
display_option(self.end_step_comma()),
display_option(self.step()),
self.do_token,
self.block,
self.end_token,
)
}
#[cfg(not(feature = "luau"))]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}{}{}{}{}{}{}{}",
self.for_token,
self.index_variable,
self.equal_token,
self.start,
self.start_end_comma,
self.end,
display_option(self.end_step_comma()),
display_option(self.step()),
self.do_token,
self.block,
self.end_token,
)
}
}
#[derive(Clone, Debug, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct GenericFor {
for_token: TokenReference,
names: Punctuated<TokenReference>,
in_token: TokenReference,
expr_list: Punctuated<Expression>,
do_token: TokenReference,
block: Block,
end_token: TokenReference,
#[cfg(feature = "luau")]
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "vec_empty_or_all_none")
)]
type_specifiers: Vec<Option<TypeSpecifier>>,
}
impl GenericFor {
pub fn new(names: Punctuated<TokenReference>, expr_list: Punctuated<Expression>) -> Self {
Self {
for_token: TokenReference::basic_symbol("for "),
names,
in_token: TokenReference::basic_symbol(" in "),
expr_list,
do_token: TokenReference::basic_symbol(" do\n"),
block: Block::new(),
end_token: TokenReference::basic_symbol("\nend"),
#[cfg(feature = "luau")]
type_specifiers: Vec::new(),
}
}
pub fn for_token(&self) -> &TokenReference {
&self.for_token
}
pub fn names(&self) -> &Punctuated<TokenReference> {
&self.names
}
pub fn in_token(&self) -> &TokenReference {
&self.in_token
}
pub fn expressions(&self) -> &Punctuated<Expression> {
&self.expr_list
}
pub fn do_token(&self) -> &TokenReference {
&self.do_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn end_token(&self) -> &TokenReference {
&self.end_token
}
#[cfg(feature = "luau")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier>> {
self.type_specifiers.iter().map(Option::as_ref)
}
pub fn with_for_token(self, for_token: TokenReference) -> Self {
Self { for_token, ..self }
}
pub fn with_names(self, names: Punctuated<TokenReference>) -> Self {
Self { names, ..self }
}
pub fn with_in_token(self, in_token: TokenReference) -> Self {
Self { in_token, ..self }
}
pub fn with_expressions(self, expr_list: Punctuated<Expression>) -> Self {
Self { expr_list, ..self }
}
pub fn with_do_token(self, do_token: TokenReference) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: TokenReference) -> Self {
Self { end_token, ..self }
}
#[cfg(feature = "luau")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier>>) -> Self {
Self {
type_specifiers,
..self
}
}
}
impl fmt::Display for GenericFor {
#[cfg(feature = "luau")]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}{}{}{}",
self.for_token,
join_type_specifiers(&self.names, self.type_specifiers()),
self.in_token,
self.expr_list,
self.do_token,
self.block,
self.end_token
)
}
#[cfg(not(feature = "luau"))]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}{}{}{}",
self.for_token,
self.names,
self.in_token,
self.expr_list,
self.do_token,
self.block,
self.end_token
)
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
"{}{}{}{}{}{}{}{}",
if_token,
condition,
then_token,
block,
display_option(else_if.as_ref().map(join_vec)),
display_option(else_token),
display_option(r#else),
end_token
)]
pub struct If {
if_token: TokenReference,
condition: Expression,
then_token: TokenReference,
block: Block,
else_if: Option<Vec<ElseIf>>,
else_token: Option<TokenReference>,
#[cfg_attr(feature = "serde", serde(rename = "else"))]
r#else: Option<Block>,
end_token: TokenReference,
}
impl If {
pub fn new(condition: Expression) -> Self {
Self {
if_token: TokenReference::basic_symbol("if "),
condition,
then_token: TokenReference::basic_symbol(" then"),
block: Block::new(),
else_if: None,
else_token: None,
r#else: None,
end_token: TokenReference::basic_symbol("\nend"),
}
}
pub fn if_token(&self) -> &TokenReference {
&self.if_token
}
pub fn condition(&self) -> &Expression {
&self.condition
}
pub fn then_token(&self) -> &TokenReference {
&self.then_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn else_token(&self) -> Option<&TokenReference> {
self.else_token.as_ref()
}
pub fn else_if(&self) -> Option<&Vec<ElseIf>> {
self.else_if.as_ref()
}
pub fn else_block(&self) -> Option<&Block> {
self.r#else.as_ref()
}
pub fn end_token(&self) -> &TokenReference {
&self.end_token
}
pub fn with_if_token(self, if_token: TokenReference) -> Self {
Self { if_token, ..self }
}
pub fn with_condition(self, condition: Expression) -> Self {
Self { condition, ..self }
}
pub fn with_then_token(self, then_token: TokenReference) -> Self {
Self { then_token, ..self }
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_else_if(self, else_if: Option<Vec<ElseIf>>) -> Self {
Self { else_if, ..self }
}
pub fn with_else_token(self, else_token: Option<TokenReference>) -> Self {
Self { else_token, ..self }
}
pub fn with_else(self, r#else: Option<Block>) -> Self {
Self { r#else, ..self }
}
pub fn with_end_token(self, end_token: TokenReference) -> Self {
Self { end_token, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{else_if_token}{condition}{then_token}{block}")]
pub struct ElseIf {
else_if_token: TokenReference,
condition: Expression,
then_token: TokenReference,
block: Block,
}
impl ElseIf {
pub fn new(condition: Expression) -> Self {
Self {
else_if_token: TokenReference::basic_symbol("elseif "),
condition,
then_token: TokenReference::basic_symbol(" then\n"),
block: Block::new(),
}
}
pub fn else_if_token(&self) -> &TokenReference {
&self.else_if_token
}
pub fn condition(&self) -> &Expression {
&self.condition
}
pub fn then_token(&self) -> &TokenReference {
&self.then_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn with_else_if_token(self, else_if_token: TokenReference) -> Self {
Self {
else_if_token,
..self
}
}
pub fn with_condition(self, condition: Expression) -> Self {
Self { condition, ..self }
}
pub fn with_then_token(self, then_token: TokenReference) -> Self {
Self { then_token, ..self }
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{while_token}{condition}{do_token}{block}{end_token}")]
pub struct While {
while_token: TokenReference,
condition: Expression,
do_token: TokenReference,
block: Block,
end_token: TokenReference,
}
impl While {
pub fn new(condition: Expression) -> Self {
Self {
while_token: TokenReference::basic_symbol("while "),
condition,
do_token: TokenReference::basic_symbol(" do\n"),
block: Block::new(),
end_token: TokenReference::basic_symbol("end\n"),
}
}
pub fn while_token(&self) -> &TokenReference {
&self.while_token
}
pub fn condition(&self) -> &Expression {
&self.condition
}
pub fn do_token(&self) -> &TokenReference {
&self.do_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn end_token(&self) -> &TokenReference {
&self.end_token
}
pub fn with_while_token(self, while_token: TokenReference) -> Self {
Self {
while_token,
..self
}
}
pub fn with_condition(self, condition: Expression) -> Self {
Self { condition, ..self }
}
pub fn with_do_token(self, do_token: TokenReference) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: TokenReference) -> Self {
Self { end_token, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{repeat_token}{block}{until_token}{until}")]
pub struct Repeat {
repeat_token: TokenReference,
block: Block,
until_token: TokenReference,
until: Expression,
}
impl Repeat {
pub fn new(until: Expression) -> Self {
Self {
repeat_token: TokenReference::basic_symbol("repeat\n"),
block: Block::new(),
until_token: TokenReference::basic_symbol("\nuntil "),
until,
}
}
pub fn repeat_token(&self) -> &TokenReference {
&self.repeat_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn until_token(&self) -> &TokenReference {
&self.until_token
}
pub fn until(&self) -> &Expression {
&self.until
}
pub fn with_repeat_token(self, repeat_token: TokenReference) -> Self {
Self {
repeat_token,
..self
}
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_until_token(self, until_token: TokenReference) -> Self {
Self {
until_token,
..self
}
}
pub fn with_until(self, until: Expression) -> Self {
Self { until, ..self }
}
}
#[derive(Clone, Debug, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct MethodCall {
colon_token: TokenReference,
name: TokenReference,
#[cfg(feature = "luau")]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
type_instantiation: Option<TypeInstantiation>,
args: FunctionArgs,
}
impl MethodCall {
pub fn new(name: TokenReference, args: FunctionArgs) -> Self {
Self {
colon_token: TokenReference::basic_symbol(":"),
name,
args,
#[cfg(feature = "luau")]
type_instantiation: None,
}
}
pub fn colon_token(&self) -> &TokenReference {
&self.colon_token
}
pub fn args(&self) -> &FunctionArgs {
&self.args
}
pub fn name(&self) -> &TokenReference {
&self.name
}
#[cfg(feature = "luau")]
pub fn type_instantiation(&self) -> Option<&TypeInstantiation> {
self.type_instantiation.as_ref()
}
pub fn with_colon_token(self, colon_token: TokenReference) -> Self {
Self {
colon_token,
..self
}
}
pub fn with_name(self, name: TokenReference) -> Self {
Self { name, ..self }
}
pub fn with_args(self, args: FunctionArgs) -> Self {
Self { args, ..self }
}
#[cfg(feature = "luau")]
pub fn with_type_instanation(self, type_instantiation: Option<TypeInstantiation>) -> Self {
Self {
type_instantiation,
..self
}
}
}
impl fmt::Display for MethodCall {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "{}{}", self.colon_token, self.name)?;
#[cfg(feature = "luau")]
if let Some(type_instantiation) = self.type_instantiation.as_ref() {
write!(formatter, "{type_instantiation}")?;
}
write!(formatter, "{}", self.args)?;
Ok(())
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Call {
#[display("{_0}")]
AnonymousCall(FunctionArgs),
#[display("{_0}")]
MethodCall(MethodCall),
}
#[derive(Clone, Debug, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct FunctionBody {
#[cfg(feature = "luau")]
generics: Option<GenericDeclaration>,
parameters_parentheses: ContainedSpan,
parameters: Punctuated<Parameter>,
#[cfg(feature = "luau")]
type_specifiers: Vec<Option<TypeSpecifier>>,
#[cfg(feature = "luau")]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
return_type: Option<TypeSpecifier>,
block: Block,
end_token: TokenReference,
}
impl FunctionBody {
pub fn new() -> Self {
Self {
#[cfg(feature = "luau")]
generics: None,
parameters_parentheses: ContainedSpan::new(
TokenReference::basic_symbol("("),
TokenReference::basic_symbol(")"),
),
parameters: Punctuated::new(),
#[cfg(feature = "luau")]
type_specifiers: Vec::new(),
#[cfg(feature = "luau")]
return_type: None,
block: Block::new(),
end_token: TokenReference::basic_symbol("\nend"),
}
}
pub fn parameters_parentheses(&self) -> &ContainedSpan {
&self.parameters_parentheses
}
pub fn parameters(&self) -> &Punctuated<Parameter> {
&self.parameters
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn end_token(&self) -> &TokenReference {
&self.end_token
}
#[cfg(feature = "luau")]
pub fn generics(&self) -> Option<&GenericDeclaration> {
self.generics.as_ref()
}
#[cfg(feature = "luau")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier>> {
self.type_specifiers.iter().map(Option::as_ref)
}
#[cfg(feature = "luau")]
pub fn return_type(&self) -> Option<&TypeSpecifier> {
self.return_type.as_ref()
}
pub fn with_parameters_parentheses(self, parameters_parentheses: ContainedSpan) -> Self {
Self {
parameters_parentheses,
..self
}
}
pub fn with_parameters(self, parameters: Punctuated<Parameter>) -> Self {
Self { parameters, ..self }
}
#[cfg(feature = "luau")]
pub fn with_generics(self, generics: Option<GenericDeclaration>) -> Self {
Self { generics, ..self }
}
#[cfg(feature = "luau")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier>>) -> Self {
Self {
type_specifiers,
..self
}
}
#[cfg(feature = "luau")]
pub fn with_return_type(self, return_type: Option<TypeSpecifier>) -> Self {
Self {
return_type,
..self
}
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: TokenReference) -> Self {
Self { end_token, ..self }
}
}
impl Default for FunctionBody {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for FunctionBody {
#[cfg(feature = "luau")]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}{}{}{}",
display_option(self.generics.as_ref()),
self.parameters_parentheses.tokens().0,
join_type_specifiers(&self.parameters, self.type_specifiers()),
self.parameters_parentheses.tokens().1,
display_option(self.return_type.as_ref()),
self.block,
self.end_token
)
}
#[cfg(not(feature = "luau"))]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}{}",
self.parameters_parentheses.tokens().0,
self.parameters,
self.parameters_parentheses.tokens().1,
self.block,
self.end_token
)
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Parameter {
Ellipsis(TokenReference),
Name(TokenReference),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Suffix {
#[display("{_0}")]
Call(Call),
#[display("{_0}")]
Index(Index),
#[display("{_0}")]
#[cfg(feature = "luau")]
TypeInstantiation(TypeInstantiation),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{}{}", prefix, join_vec(suffixes))]
pub struct VarExpression {
prefix: Prefix,
suffixes: Vec<Suffix>,
}
impl VarExpression {
pub fn new(prefix: Prefix) -> Self {
Self {
prefix,
suffixes: Vec::new(),
}
}
pub fn prefix(&self) -> &Prefix {
&self.prefix
}
pub fn suffixes(&self) -> impl Iterator<Item = &Suffix> {
self.suffixes.iter()
}
pub fn with_prefix(self, prefix: Prefix) -> Self {
Self { prefix, ..self }
}
pub fn with_suffixes(self, suffixes: Vec<Suffix>) -> Self {
Self { suffixes, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Var {
#[display("{_0}")]
Expression(Box<VarExpression>),
#[display("{_0}")]
Name(TokenReference),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{var_list}{equal_token}{expr_list}")]
pub struct Assignment {
var_list: Punctuated<Var>,
equal_token: TokenReference,
expr_list: Punctuated<Expression>,
}
impl Assignment {
pub fn new(var_list: Punctuated<Var>, expr_list: Punctuated<Expression>) -> Self {
Self {
var_list,
equal_token: TokenReference::basic_symbol(" = "),
expr_list,
}
}
pub fn expressions(&self) -> &Punctuated<Expression> {
&self.expr_list
}
pub fn equal_token(&self) -> &TokenReference {
&self.equal_token
}
pub fn variables(&self) -> &Punctuated<Var> {
&self.var_list
}
pub fn with_variables(self, var_list: Punctuated<Var>) -> Self {
Self { var_list, ..self }
}
pub fn with_equal_token(self, equal_token: TokenReference) -> Self {
Self {
equal_token,
..self
}
}
pub fn with_expressions(self, expr_list: Punctuated<Expression>) -> Self {
Self { expr_list, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(
not(feature = "luau"),
display("{local_token}{function_token}{name}{body}")
)]
#[cfg_attr(
feature = "luau",
display(
"{}{}{}{}{}",
join_vec(attributes),
local_token,
function_token,
name,
body
)
)]
pub struct LocalFunction {
#[cfg(feature = "luau")]
attributes: Vec<LuauAttribute>,
local_token: TokenReference,
function_token: TokenReference,
name: TokenReference,
body: FunctionBody,
}
impl LocalFunction {
pub fn new(name: TokenReference) -> Self {
LocalFunction {
#[cfg(feature = "luau")]
attributes: Vec::new(),
local_token: TokenReference::basic_symbol("local "),
function_token: TokenReference::basic_symbol("function "),
name,
body: FunctionBody::new(),
}
}
#[cfg(feature = "luau")]
pub fn attributes(&self) -> impl Iterator<Item = &LuauAttribute> {
self.attributes.iter()
}
pub fn local_token(&self) -> &TokenReference {
&self.local_token
}
pub fn function_token(&self) -> &TokenReference {
&self.function_token
}
pub fn body(&self) -> &FunctionBody {
&self.body
}
pub fn name(&self) -> &TokenReference {
&self.name
}
#[cfg(feature = "luau")]
pub fn with_attributes(self, attributes: Vec<LuauAttribute>) -> Self {
Self { attributes, ..self }
}
pub fn with_local_token(self, local_token: TokenReference) -> Self {
Self {
local_token,
..self
}
}
pub fn with_function_token(self, function_token: TokenReference) -> Self {
Self {
function_token,
..self
}
}
pub fn with_name(self, name: TokenReference) -> Self {
Self { name, ..self }
}
pub fn with_body(self, body: FunctionBody) -> Self {
Self { body, ..self }
}
}
#[derive(Clone, Debug, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct LocalAssignment {
local_token: TokenReference,
#[cfg(feature = "luau")]
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "empty_optional_vector")
)]
type_specifiers: Vec<Option<TypeSpecifier>>,
name_list: Punctuated<TokenReference>,
#[cfg(feature = "lua54")]
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "empty_optional_vector")
)]
attributes: Vec<Option<Attribute>>,
equal_token: Option<TokenReference>,
expr_list: Punctuated<Expression>,
}
impl LocalAssignment {
pub fn new(name_list: Punctuated<TokenReference>) -> Self {
Self {
local_token: TokenReference::basic_symbol("local "),
#[cfg(feature = "luau")]
type_specifiers: Vec::new(),
name_list,
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
attributes: Vec::new(),
equal_token: None,
expr_list: Punctuated::new(),
}
}
pub fn local_token(&self) -> &TokenReference {
&self.local_token
}
pub fn equal_token(&self) -> Option<&TokenReference> {
self.equal_token.as_ref()
}
pub fn expressions(&self) -> &Punctuated<Expression> {
&self.expr_list
}
pub fn names(&self) -> &Punctuated<TokenReference> {
&self.name_list
}
#[cfg(feature = "luau")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier>> {
self.type_specifiers.iter().map(Option::as_ref)
}
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
pub fn attributes(&self) -> impl Iterator<Item = Option<&Attribute>> {
self.attributes.iter().map(Option::as_ref)
}
pub fn with_local_token(self, local_token: TokenReference) -> Self {
Self {
local_token,
..self
}
}
#[cfg(feature = "luau")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier>>) -> Self {
Self {
type_specifiers,
..self
}
}
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
pub fn with_attributes(self, attributes: Vec<Option<Attribute>>) -> Self {
Self { attributes, ..self }
}
pub fn with_names(self, name_list: Punctuated<TokenReference>) -> Self {
Self { name_list, ..self }
}
pub fn with_equal_token(self, equal_token: Option<TokenReference>) -> Self {
Self {
equal_token,
..self
}
}
pub fn with_expressions(self, expr_list: Punctuated<Expression>) -> Self {
Self { expr_list, ..self }
}
}
impl fmt::Display for LocalAssignment {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
#[cfg(any(feature = "lua54", feature = "cfxlua"))]
let attributes = self.attributes().chain(std::iter::repeat(None));
#[cfg(not(feature = "lua54"))]
let attributes = std::iter::repeat_with(|| None::<TokenReference>);
#[cfg(feature = "luau")]
let type_specifiers = self.type_specifiers().chain(std::iter::repeat(None));
#[cfg(not(feature = "luau"))]
let type_specifiers = std::iter::repeat_with(|| None::<TokenReference>);
write!(
formatter,
"{}{}{}{}",
self.local_token,
join_iterators(&self.name_list, attributes, type_specifiers),
display_option(&self.equal_token),
self.expr_list
)
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{do_token}{block}{end_token}")]
pub struct Do {
do_token: TokenReference,
block: Block,
end_token: TokenReference,
}
impl Do {
pub fn new() -> Self {
Self {
do_token: TokenReference::basic_symbol("do\n"),
block: Block::new(),
end_token: TokenReference::basic_symbol("\nend"),
}
}
pub fn do_token(&self) -> &TokenReference {
&self.do_token
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn end_token(&self) -> &TokenReference {
&self.end_token
}
pub fn with_do_token(self, do_token: TokenReference) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: TokenReference) -> Self {
Self { end_token, ..self }
}
}
impl Default for Do {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{}{}", prefix, join_vec(suffixes))]
pub struct FunctionCall {
prefix: Prefix,
suffixes: Vec<Suffix>,
}
impl FunctionCall {
pub fn new(prefix: Prefix) -> Self {
FunctionCall {
prefix,
suffixes: vec![Suffix::Call(Call::AnonymousCall(
FunctionArgs::Parentheses {
arguments: Punctuated::new(),
parentheses: ContainedSpan::new(
TokenReference::basic_symbol("("),
TokenReference::basic_symbol(")"),
),
},
))],
}
}
pub fn prefix(&self) -> &Prefix {
&self.prefix
}
pub fn suffixes(&self) -> impl Iterator<Item = &Suffix> {
self.suffixes.iter()
}
pub fn with_prefix(self, prefix: Prefix) -> Self {
Self { prefix, ..self }
}
pub fn with_suffixes(self, suffixes: Vec<Suffix>) -> Self {
Self { suffixes, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
"{}{}{}",
names,
display_option(self.method_colon()),
display_option(self.method_name())
)]
pub struct FunctionName {
names: Punctuated<TokenReference>,
colon_name: Option<(TokenReference, TokenReference)>,
}
impl FunctionName {
pub fn new(names: Punctuated<TokenReference>) -> Self {
Self {
names,
colon_name: None,
}
}
pub fn method_colon(&self) -> Option<&TokenReference> {
Some(&self.colon_name.as_ref()?.0)
}
pub fn method_name(&self) -> Option<&TokenReference> {
Some(&self.colon_name.as_ref()?.1)
}
pub fn names(&self) -> &Punctuated<TokenReference> {
&self.names
}
pub fn with_names(self, names: Punctuated<TokenReference>) -> Self {
Self { names, ..self }
}
pub fn with_method(self, method: Option<(TokenReference, TokenReference)>) -> Self {
Self {
colon_name: method,
..self
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(not(feature = "luau"), display("{function_token}{name}{body}"))]
#[cfg_attr(
feature = "luau",
display("{}{}{}{}", join_vec(attributes), function_token, name, body)
)]
pub struct FunctionDeclaration {
#[cfg(feature = "luau")]
attributes: Vec<LuauAttribute>,
function_token: TokenReference,
name: FunctionName,
body: FunctionBody,
}
impl FunctionDeclaration {
pub fn new(name: FunctionName) -> Self {
Self {
#[cfg(feature = "luau")]
attributes: Vec::new(),
function_token: TokenReference::basic_symbol("function "),
name,
body: FunctionBody::new(),
}
}
#[cfg(feature = "luau")]
pub fn attributes(&self) -> impl Iterator<Item = &LuauAttribute> {
self.attributes.iter()
}
pub fn function_token(&self) -> &TokenReference {
&self.function_token
}
pub fn body(&self) -> &FunctionBody {
&self.body
}
pub fn name(&self) -> &FunctionName {
&self.name
}
#[cfg(feature = "luau")]
pub fn with_attributes(self, attributes: Vec<LuauAttribute>) -> Self {
Self { attributes, ..self }
}
pub fn with_function_token(self, function_token: TokenReference) -> Self {
Self {
function_token,
..self
}
}
pub fn with_name(self, name: FunctionName) -> Self {
Self { name, ..self }
}
pub fn with_body(self, body: FunctionBody) -> Self {
Self { body, ..self }
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! make_bin_op {
($(#[$outer:meta])* { $(
$([$($version:ident)|+])? $operator:ident = $precedence:expr,
)+ }) => {
paste::paste! {
#[derive(Clone, Debug, Display, PartialEq, Eq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
$(#[$outer])*
#[display("{_0}")]
pub enum BinOp {
$(
#[allow(missing_docs)]
$(
#[cfg(any(
$(feature = "" $version),+
))]
)*
$operator(TokenReference),
)+
}
impl BinOp {
pub fn precedence_of_token(token: &TokenReference) -> Option<u8> {
match token.token_type() {
TokenType::Symbol { symbol } => match symbol {
$(
$(
#[cfg(any(
$(feature = "" $version),+
))]
)*
Symbol::$operator => Some($precedence),
)+
_ => None,
},
_ => None
}
}
pub fn token(&self) -> &TokenReference {
match self {
$(
$(
#[cfg(any(
$(feature = "" $version),+
))]
)*
BinOp::$operator(token) => token,
)+
}
}
pub(crate) fn consume(state: &mut parser_structs::ParserState) -> Option<Self> {
match state.current().unwrap().token_type() {
TokenType::Symbol { symbol } => match symbol {
$(
$(
#[cfg(any(
$(feature = "" $version),+
))]
)*
Symbol::$operator => {
if !$crate::has_version!(state.lua_version(), $($($version,)+)?) {
return None;
}
Some(BinOp::$operator(state.consume().unwrap()))
},
)+
_ => None,
},
_ => None,
}
}
}
}
};
}
make_bin_op!(
#[doc = "Operators that require two operands, such as X + Y or X - Y"]
#[visit(skip_visit_self)]
{
Caret = 12,
Percent = 10,
Slash = 10,
Star = 10,
[luau | lua53] DoubleSlash = 10,
Minus = 9,
Plus = 9,
TwoDots = 8,
[lua53] DoubleLessThan = 7,
[lua53] DoubleGreaterThan = 7,
[lua53] Ampersand = 6,
[lua53] Tilde = 5,
[lua53] Pipe = 4,
GreaterThan = 3,
GreaterThanEqual = 3,
LessThan = 3,
LessThanEqual = 3,
TildeEqual = 3,
TwoEqual = 3,
And = 2,
Or = 1,
}
);
impl BinOp {
pub fn precedence(&self) -> u8 {
BinOp::precedence_of_token(self.token()).expect("invalid token")
}
pub fn is_right_associative(&self) -> bool {
matches!(*self, BinOp::Caret(_) | BinOp::TwoDots(_))
}
pub fn is_right_associative_token(token: &TokenReference) -> bool {
matches!(
token.token_type(),
TokenType::Symbol {
symbol: Symbol::Caret
} | TokenType::Symbol {
symbol: Symbol::TwoDots
}
)
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[allow(missing_docs)]
#[non_exhaustive]
#[display("{_0}")]
pub enum UnOp {
Minus(TokenReference),
Not(TokenReference),
Hash(TokenReference),
#[cfg(feature = "lua53")]
Tilde(TokenReference),
}
impl UnOp {
pub fn token(&self) -> &TokenReference {
match self {
UnOp::Minus(token) | UnOp::Not(token) | UnOp::Hash(token) => token,
#[cfg(feature = "lua53")]
UnOp::Tilde(token) => token,
}
}
pub fn precedence() -> u8 {
11
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct AstError {
token: Token,
additional: Cow<'static, str>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
range: Option<(Position, Position)>,
}
impl AstError {
pub fn token(&self) -> &Token {
&self.token
}
pub fn error_message(&self) -> &str {
self.additional.as_ref()
}
pub fn range(&self) -> (Position, Position) {
self.range
.or_else(|| Some((self.token.start_position(), self.token.end_position())))
.unwrap()
}
}
impl fmt::Display for AstError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let range = self.range();
write!(
formatter,
"unexpected token `{}`. (starting from line {}, character {} and ending on line {}, character {})\nadditional information: {}",
self.token,
range.0.line(),
range.0.character(),
range.1.line(),
range.1.character(),
self.additional,
)
}
}
impl std::error::Error for AstError {}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Ast {
pub(crate) nodes: Block,
pub(crate) eof: TokenReference,
}
impl Ast {
pub fn with_nodes(self, nodes: Block) -> Self {
Self { nodes, ..self }
}
pub fn with_eof(self, eof: TokenReference) -> Self {
Self { eof, ..self }
}
pub fn nodes(&self) -> &Block {
&self.nodes
}
pub fn nodes_mut(&mut self) -> &mut Block {
&mut self.nodes
}
pub fn eof(&self) -> &TokenReference {
&self.eof
}
}
impl fmt::Display for Ast {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.nodes())?;
write!(f, "{}", self.eof())
}
}
#[cfg(test)]
mod tests {
use crate::{parse, visitors::VisitorMut};
use super::*;
#[test]
fn test_with_eof_safety() {
let new_ast = {
let ast = parse("local foo = 1").unwrap();
let eof = ast.eof().clone();
ast.with_eof(eof)
};
assert_eq!("local foo = 1", new_ast.to_string());
}
#[test]
fn test_with_nodes_safety() {
let new_ast = {
let ast = parse("local foo = 1").unwrap();
let nodes = ast.nodes().clone();
ast.with_nodes(nodes)
};
assert_eq!(new_ast.to_string(), "local foo = 1");
}
#[test]
fn test_with_visitor_safety() {
let new_ast = {
let ast = parse("local foo = 1").unwrap();
struct SyntaxRewriter;
impl VisitorMut for SyntaxRewriter {
fn visit_token(&mut self, token: Token) -> Token {
token
}
}
SyntaxRewriter.visit_ast(ast)
};
assert_eq!(new_ast.to_string(), "local foo = 1");
}
#[test]
fn test_new_validity() {
let token: TokenReference = TokenReference::new(
Vec::new(),
Token::new(TokenType::Identifier {
identifier: "foo".into(),
}),
Vec::new(),
);
let expression = Expression::Var(Var::Name(token.clone()));
Assignment::new(Punctuated::new(), Punctuated::new());
Do::new();
ElseIf::new(expression.clone());
FunctionBody::new();
FunctionCall::new(Prefix::Name(token.clone()));
FunctionDeclaration::new(FunctionName::new(Punctuated::new()));
GenericFor::new(Punctuated::new(), Punctuated::new());
If::new(expression.clone());
LocalAssignment::new(Punctuated::new());
LocalFunction::new(token.clone());
MethodCall::new(
token.clone(),
FunctionArgs::Parentheses {
arguments: Punctuated::new(),
parentheses: ContainedSpan::new(token.clone(), token.clone()),
},
);
NumericFor::new(token, expression.clone(), expression.clone());
Repeat::new(expression.clone());
Return::new();
TableConstructor::new();
While::new(expression);
}
#[test]
fn test_local_assignment_print() {
let block = Block::new().with_stmts(vec![(
Stmt::LocalAssignment(
LocalAssignment::new(
std::iter::once(Pair::End(TokenReference::new(
vec![],
Token::new(TokenType::Identifier {
identifier: "variable".into(),
}),
vec![],
)))
.collect(),
)
.with_equal_token(Some(TokenReference::symbol(" = ").unwrap()))
.with_expressions(
std::iter::once(Pair::End(Expression::Number(TokenReference::new(
vec![],
Token::new(TokenType::Number { text: "1".into() }),
vec![],
))))
.collect(),
),
),
None,
)]);
let ast = parse("").unwrap().with_nodes(block);
assert_eq!(ast.to_string(), "local variable = 1");
}
}