#[macro_use]
mod parser_util;
mod parsers;
pub mod punctuated;
pub mod span;
mod update_positions;
mod visitors;
use crate::{
tokenizer::{Symbol, Token, TokenReference, TokenType},
util::*,
};
use derive_more::Display;
use full_moon_derive::{Node, Visit};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt};
use parser_util::{
InternalAstError, OneOrMore, Parser, ParserState, ZeroOrMore, ZeroOrMoreDelimited,
};
use punctuated::{Pair, Punctuated};
use span::ContainedSpan;
#[cfg(feature = "roblox")]
pub mod types;
#[cfg(feature = "roblox")]
use types::*;
#[cfg(feature = "roblox")]
mod type_visitors;
#[cfg(feature = "lua52")]
pub mod lua52;
#[cfg(feature = "lua52")]
use lua52::*;
#[cfg(feature = "lua54")]
pub mod lua54;
#[cfg(feature = "lua54")]
use lua54::*;
#[derive(Clone, Debug, Default, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
fmt = "{}{}",
"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 }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum LastStmt {
Break(TokenReference),
#[cfg(feature = "roblox")]
Continue(TokenReference),
Return(Return),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}", token, returns)]
pub struct Return {
token: TokenReference,
returns: Punctuated<Expression>,
}
impl Return {
pub fn new() -> Self {
Self {
token: TokenReference::symbol("return ").unwrap(),
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(
fmt = "{}{}{}{}{}",
"brackets.tokens().0",
"key",
"brackets.tokens().1",
"equal",
"value"
)]
ExpressionKey {
brackets: ContainedSpan,
key: Expression,
equal: TokenReference,
value: Expression,
},
#[display(fmt = "{}{}{}", "key", "equal", "value")]
NameKey {
key: TokenReference,
equal: TokenReference,
value: Expression,
},
#[display(fmt = "{}", "_0")]
NoKey(Expression),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "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::symbol("{ ").unwrap(),
TokenReference::symbol(" }").unwrap(),
),
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)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
#[non_exhaustive]
pub enum Expression {
#[display(fmt = "{}{}{}", "lhs", "binop", "rhs")]
BinaryOperator {
lhs: Box<Expression>,
binop: BinOp,
rhs: Box<Expression>,
},
#[display(
fmt = "{}{}{}",
"contained.tokens().0",
"expression",
"contained.tokens().1"
)]
Parentheses {
#[node(full_range)]
contained: ContainedSpan,
expression: Box<Expression>,
},
#[display(fmt = "{}{}", "unop", "expression")]
UnaryOperator {
unop: UnOp,
expression: Box<Expression>,
},
#[cfg_attr(not(feature = "roblox"), display(fmt = "{}", value))]
#[cfg_attr(
feature = "roblox",
display(fmt = "{}{}", value, "display_option(type_assertion)")
)]
Value {
value: Box<Value>,
#[cfg(feature = "roblox")]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
type_assertion: Option<TypeAssertion>,
},
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Value {
#[display(fmt = "{}{}", "_0.0", "_0.1")]
Function((TokenReference, FunctionBody)),
#[display(fmt = "{}", "_0")]
FunctionCall(FunctionCall),
#[cfg(feature = "roblox")]
#[display(fmt = "{}", "_0")]
IfExpression(IfExpression),
#[cfg(feature = "roblox")]
#[display(fmt = "{}", "_0")]
InterpolatedString(InterpolatedString),
#[display(fmt = "{}", "_0")]
TableConstructor(TableConstructor),
#[display(fmt = "{}", "_0")]
Number(TokenReference),
#[display(fmt = "{}", "_0")]
ParenthesesExpression(Expression),
#[display(fmt = "{}", "_0")]
String(TokenReference),
#[display(fmt = "{}", "_0")]
Symbol(TokenReference),
#[display(fmt = "{}", "_0")]
Var(Var),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Stmt {
#[display(fmt = "{}", _0)]
Assignment(Assignment),
#[display(fmt = "{}", _0)]
Do(Do),
#[display(fmt = "{}", _0)]
FunctionCall(FunctionCall),
#[display(fmt = "{}", _0)]
FunctionDeclaration(FunctionDeclaration),
#[display(fmt = "{}", _0)]
GenericFor(GenericFor),
#[display(fmt = "{}", _0)]
If(If),
#[display(fmt = "{}", _0)]
LocalAssignment(LocalAssignment),
#[display(fmt = "{}", _0)]
LocalFunction(LocalFunction),
#[display(fmt = "{}", _0)]
NumericFor(NumericFor),
#[display(fmt = "{}", _0)]
Repeat(Repeat),
#[display(fmt = "{}", _0)]
While(While),
#[cfg(feature = "roblox")]
#[display(fmt = "{}", _0)]
CompoundAssignment(CompoundAssignment),
#[cfg(feature = "roblox")]
ExportedTypeDeclaration(ExportedTypeDeclaration),
#[cfg(feature = "roblox")]
TypeDeclaration(TypeDeclaration),
#[cfg(feature = "lua52")]
Goto(Goto),
#[cfg(feature = "lua52")]
Label(Label),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Prefix {
#[display(fmt = "{}", _0)]
Expression(Expression),
#[display(fmt = "{}", _0)]
Name(TokenReference),
}
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Index {
#[display(
fmt = "{}{}{}",
"brackets.tokens().0",
"expression",
"brackets.tokens().1"
)]
Brackets {
brackets: ContainedSpan,
expression: Expression,
},
#[display(fmt = "{}{}", "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(
fmt = "{}{}{}",
"parentheses.tokens().0",
"arguments",
"parentheses.tokens().1"
)]
Parentheses {
#[node(full_range)]
parentheses: ContainedSpan,
arguments: Punctuated<Expression>,
},
#[display(fmt = "{}", "_0")]
String(TokenReference),
#[display(fmt = "{}", "_0")]
TableConstructor(TableConstructor),
}
#[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 = "roblox")]
#[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::symbol("for ").unwrap(),
index_variable,
equal_token: TokenReference::symbol(" = ").unwrap(),
start,
start_end_comma: TokenReference::symbol(", ").unwrap(),
end,
end_step_comma: None,
step: None,
do_token: TokenReference::symbol(" do\n").unwrap(),
block: Block::new(),
end_token: TokenReference::symbol("\nend").unwrap(),
#[cfg(feature = "roblox")]
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 = "roblox")]
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 = "roblox")]
pub fn with_type_specifier(self, type_specifier: Option<TypeSpecifier>) -> Self {
Self {
type_specifier,
..self
}
}
}
impl fmt::Display for NumericFor {
#[cfg(feature = "roblox")]
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 = "roblox"))]
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 = "roblox")]
type_specifiers: Vec<Option<TypeSpecifier>>,
}
impl GenericFor {
pub fn new(names: Punctuated<TokenReference>, expr_list: Punctuated<Expression>) -> Self {
Self {
for_token: TokenReference::symbol("for ").unwrap(),
names,
in_token: TokenReference::symbol(" in ").unwrap(),
expr_list,
do_token: TokenReference::symbol(" do\n").unwrap(),
block: Block::new(),
end_token: TokenReference::symbol("\nend").unwrap(),
#[cfg(feature = "roblox")]
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 = "roblox")]
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 = "roblox")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier>>) -> Self {
Self {
type_specifiers,
..self
}
}
}
impl fmt::Display for GenericFor {
#[cfg(feature = "roblox")]
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 = "roblox"))]
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(
fmt = "{}{}{}{}{}{}{}{}",
"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::symbol("if ").unwrap(),
condition,
then_token: TokenReference::symbol(" then").unwrap(),
block: Block::new(),
else_if: None,
else_token: None,
r#else: None,
end_token: TokenReference::symbol("\nend").unwrap(),
}
}
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(fmt = "{}{}{}{}", "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::symbol("elseif ").unwrap(),
condition,
then_token: TokenReference::symbol(" then\n").unwrap(),
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(
fmt = "{}{}{}{}{}",
"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::symbol("while ").unwrap(),
condition,
do_token: TokenReference::symbol(" do\n").unwrap(),
block: Block::new(),
end_token: TokenReference::symbol("end\n").unwrap(),
}
}
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(fmt = "{}{}{}{}", "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::symbol("repeat\n").unwrap(),
block: Block::new(),
until_token: TokenReference::symbol("\nuntil ").unwrap(),
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, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "colon_token", "name", "args")]
pub struct MethodCall {
colon_token: TokenReference,
name: TokenReference,
args: FunctionArgs,
}
impl MethodCall {
pub fn new(name: TokenReference, args: FunctionArgs) -> Self {
Self {
colon_token: TokenReference::symbol(":").unwrap(),
name,
args,
}
}
pub fn colon_token(&self) -> &TokenReference {
&self.colon_token
}
pub fn args(&self) -> &FunctionArgs {
&self.args
}
pub fn name(&self) -> &TokenReference {
&self.name
}
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 }
}
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Call {
#[display(fmt = "{}", "_0")]
AnonymousCall(FunctionArgs),
#[display(fmt = "{}", "_0")]
MethodCall(MethodCall),
}
#[derive(Clone, Debug, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct FunctionBody {
#[cfg(feature = "roblox")]
generics: Option<GenericDeclaration>,
parameters_parentheses: ContainedSpan,
parameters: Punctuated<Parameter>,
#[cfg(feature = "roblox")]
type_specifiers: Vec<Option<TypeSpecifier>>,
#[cfg(feature = "roblox")]
#[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 = "roblox")]
generics: None,
parameters_parentheses: ContainedSpan::new(
TokenReference::symbol("(").unwrap(),
TokenReference::symbol(")").unwrap(),
),
parameters: Punctuated::new(),
#[cfg(feature = "roblox")]
type_specifiers: Vec::new(),
#[cfg(feature = "roblox")]
return_type: None,
block: Block::new(),
end_token: TokenReference::symbol("\nend").unwrap(),
}
}
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 = "roblox")]
pub fn generics(&self) -> Option<&GenericDeclaration> {
self.generics.as_ref()
}
#[cfg(feature = "roblox")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier>> {
self.type_specifiers.iter().map(Option::as_ref)
}
#[cfg(feature = "roblox")]
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 = "roblox")]
pub fn with_generics(self, generics: Option<GenericDeclaration>) -> Self {
Self { generics, ..self }
}
#[cfg(feature = "roblox")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier>>) -> Self {
Self {
type_specifiers,
..self
}
}
#[cfg(feature = "roblox")]
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 = "roblox")]
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 = "roblox"))]
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 {
Ellipse(TokenReference),
Name(TokenReference),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum Suffix {
#[display(fmt = "{}", "_0")]
Call(Call),
#[display(fmt = "{}", "_0")]
Index(Index),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}", "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(fmt = "{}", "_0")]
Expression(VarExpression),
#[display(fmt = "{}", "_0")]
Name(TokenReference),
}
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "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::symbol(" = ").unwrap(),
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 = "roblox"),
display(fmt = "{}{}{}{}", "local_token", "function_token", "name", "body")
)]
#[cfg_attr(
feature = "roblox",
display(fmt = "{}{}{}{}", "local_token", "function_token", "name", "body")
)]
pub struct LocalFunction {
local_token: TokenReference,
function_token: TokenReference,
name: TokenReference,
body: FunctionBody,
}
impl LocalFunction {
pub fn new(name: TokenReference) -> Self {
LocalFunction {
local_token: TokenReference::symbol("local ").unwrap(),
function_token: TokenReference::symbol("function ").unwrap(),
name,
body: FunctionBody::new(),
}
}
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
}
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 = "roblox")]
#[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::symbol("local ").unwrap(),
#[cfg(feature = "roblox")]
type_specifiers: Vec::new(),
name_list,
#[cfg(feature = "lua54")]
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 = "roblox")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier>> {
self.type_specifiers.iter().map(Option::as_ref)
}
#[cfg(feature = "lua54")]
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 = "roblox")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier>>) -> Self {
Self {
type_specifiers,
..self
}
}
#[cfg(feature = "lua54")]
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(feature = "lua54")]
let attributes = self.attributes();
#[cfg(not(feature = "lua54"))]
let attributes = std::iter::repeat_with(|| None::<TokenReference>);
#[cfg(feature = "roblox")]
let type_specifiers = self.type_specifiers();
#[cfg(not(feature = "roblox"))]
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(fmt = "{}{}{}", "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::symbol("do\n").unwrap(),
block: Block::new(),
end_token: TokenReference::symbol("\nend").unwrap(),
}
}
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(fmt = "{}{}", "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::symbol("(").unwrap(),
TokenReference::symbol(")").unwrap(),
),
},
))],
}
}
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(
fmt = "{}{}{}",
"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 = "roblox"),
display(fmt = "{}{}{}", "function_token", "name", "body")
)]
#[cfg_attr(
feature = "roblox",
display(fmt = "{}{}{}", "function_token", "name", "body")
)]
pub struct FunctionDeclaration {
function_token: TokenReference,
name: FunctionName,
body: FunctionBody,
}
impl FunctionDeclaration {
pub fn new(name: FunctionName) -> Self {
Self {
function_token: TokenReference::symbol("function ").unwrap(),
name,
body: FunctionBody::new(),
}
}
pub fn function_token(&self) -> &TokenReference {
&self.function_token
}
pub fn body(&self) -> &FunctionBody {
&self.body
}
pub fn name(&self) -> &FunctionName {
&self.name
}
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 }
}
}
make_op!(BinOp,
#[doc = "Operators that require two operands, such as X + Y or X - Y"]
#[visit(skip_visit_self)]
{
And,
Caret,
GreaterThan,
GreaterThanEqual,
LessThan,
LessThanEqual,
Minus,
Or,
Percent,
Plus,
Slash,
Star,
TildeEqual,
TwoDots,
TwoEqual,
#[cfg(feature = "lua53")]
Ampersand,
#[cfg(feature = "lua53")]
DoubleSlash,
#[cfg(feature = "lua53")]
DoubleLessThan,
#[cfg(feature = "lua53")]
Pipe,
#[cfg(feature = "lua53")]
DoubleGreaterThan,
#[cfg(feature = "lua53")]
Tilde,
}
);
impl BinOp {
#[cfg(not(feature = "lua53"))]
pub fn precedence(&self) -> u8 {
match *self {
BinOp::Caret(_) => 8,
BinOp::Star(_) | BinOp::Slash(_) | BinOp::Percent(_) => 6,
BinOp::Plus(_) | BinOp::Minus(_) => 5,
BinOp::TwoDots(_) => 4,
BinOp::GreaterThan(_)
| BinOp::LessThan(_)
| BinOp::GreaterThanEqual(_)
| BinOp::LessThanEqual(_)
| BinOp::TildeEqual(_)
| BinOp::TwoEqual(_) => 3,
BinOp::And(_) => 2,
BinOp::Or(_) => 1,
}
}
#[cfg(feature = "lua53")]
pub fn precedence(&self) -> u8 {
match *self {
BinOp::Caret(_) => 12,
BinOp::Star(_) | BinOp::Slash(_) | BinOp::DoubleSlash(_) | BinOp::Percent(_) => 10,
BinOp::Plus(_) | BinOp::Minus(_) => 9,
BinOp::TwoDots(_) => 8,
BinOp::DoubleLessThan(_) | BinOp::DoubleGreaterThan(_) => 7,
BinOp::Ampersand(_) => 6,
BinOp::Tilde(_) => 5,
BinOp::Pipe(_) => 4,
BinOp::GreaterThan(_)
| BinOp::LessThan(_)
| BinOp::GreaterThanEqual(_)
| BinOp::LessThanEqual(_)
| BinOp::TildeEqual(_)
| BinOp::TwoEqual(_) => 3,
BinOp::And(_) => 2,
BinOp::Or(_) => 1,
}
}
pub fn is_right_associative(&self) -> bool {
matches!(*self, BinOp::Caret(_) | BinOp::TwoDots(_))
}
pub fn token(&self) -> &TokenReference {
match self {
BinOp::And(token)
| BinOp::Caret(token)
| BinOp::GreaterThan(token)
| BinOp::GreaterThanEqual(token)
| BinOp::LessThan(token)
| BinOp::LessThanEqual(token)
| BinOp::Minus(token)
| BinOp::Or(token)
| BinOp::Percent(token)
| BinOp::Plus(token)
| BinOp::Slash(token)
| BinOp::Star(token)
| BinOp::TildeEqual(token)
| BinOp::TwoDots(token)
| BinOp::TwoEqual(token) => token,
#[cfg(feature = "lua53")]
BinOp::Ampersand(token)
| BinOp::DoubleSlash(token)
| BinOp::DoubleLessThan(token)
| BinOp::Pipe(token)
| BinOp::DoubleGreaterThan(token)
| BinOp::Tilde(token) => token,
}
}
}
make_op!(UnOp,
#[doc = "Operators that require just one operand, such as #X"]
{
Minus,
Not,
Hash,
#[cfg(feature = "lua53")]
Tilde,
}
);
impl UnOp {
#[cfg(not(feature = "lua53"))]
pub fn precedence(&self) -> u8 {
7
}
#[cfg(feature = "lua53")]
pub fn precedence(&self) -> u8 {
11
}
pub fn token(&self) -> &TokenReference {
match self {
UnOp::Minus(token) | UnOp::Not(token) | UnOp::Hash(token) => token,
#[cfg(feature = "lua53")]
UnOp::Tilde(token) => token,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum AstError {
Empty,
NoEof,
UnexpectedToken {
token: Token,
additional: Option<Cow<'static, str>>,
},
}
impl fmt::Display for AstError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
AstError::Empty => write!(formatter, "tokens passed was empty, which shouldn't happen normally"),
AstError::NoEof => write!(formatter, "tokens passed had no eof token, which shouldn't happen normally"),
AstError::UnexpectedToken { token, additional } => write!(
formatter,
"unexpected token `{}`. (starting from line {}, character {} and ending on line {}, character {}){}",
token,
token.start_position().line(),
token.start_position().character(),
token.end_position().line(),
token.end_position().character(),
match additional {
Some(additional) => format!("\nadditional information: {}", additional),
None => String::new(),
}
)
}
}
}
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 {
#[allow(clippy::result_large_err)]
pub fn from_tokens(tokens: Vec<Token>) -> Result<Ast, AstError> {
if *tokens.last().ok_or(AstError::Empty)?.token_type() != TokenType::Eof {
return Err(AstError::NoEof);
}
let mut tokens = extract_token_references(tokens);
let mut state = ParserState::new(&tokens);
if tokens
.iter()
.filter(|token| !token.token_type().is_trivia())
.count()
== 1
{
return Ok(Ast {
nodes: Block {
stmts: Vec::new(),
last_stmt: None,
},
eof: tokens
.pop()
.expect("(internal full-moon error) No EOF in tokens after checking for EOF."),
});
}
if state.peek().token_type().is_trivia() {
state = state.advance().unwrap();
}
match parsers::ParseBlock.parse(state) {
Ok((state, block)) => {
if state.index == tokens.len() - 1 {
Ok(Ast {
nodes: block,
eof: tokens.pop().expect(
"(internal full-moon error) No EOF in tokens after checking for EOF.",
),
})
} else {
Err(AstError::UnexpectedToken {
token: state.peek().token.clone(),
additional: Some(Cow::Borrowed("leftover token")),
})
}
}
Err(InternalAstError::NoMatch) => Err(AstError::UnexpectedToken {
token: state.peek().token.clone(),
additional: None,
}),
Err(InternalAstError::UnexpectedToken { token, additional }) => {
Err(AstError::UnexpectedToken {
token: token.token,
additional,
})
}
}
}
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
}
}
pub(crate) fn extract_token_references(mut tokens: Vec<Token>) -> Vec<TokenReference> {
let mut references = Vec::new();
let (mut leading_trivia, mut trailing_trivia) = (Vec::new(), Vec::new());
let mut tokens = tokens.drain(..).peekable();
while let Some(token) = tokens.next() {
if token.token_type().is_trivia() {
leading_trivia.push(token);
} else {
while let Some(token) = tokens.peek() {
if token.token_type().is_trivia() {
let should_break =
if let TokenType::Whitespace { ref characters } = token.token_type() {
characters.contains('\n')
} else {
false
};
trailing_trivia.push(tokens.next().unwrap());
if should_break {
break;
}
} else {
break;
}
}
references.push(TokenReference {
leading_trivia: leading_trivia.drain(..).collect(),
trailing_trivia: trailing_trivia.drain(..).collect(),
token,
});
}
}
references
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{parse, print, tokenizer::tokens, visitors::VisitorMut};
#[test]
fn test_extract_token_references() {
let tokens = tokens("print(1)\n-- hello world\nlocal foo -- this is the word foo").unwrap();
let references = extract_token_references(tokens);
assert_eq!(references.len(), 7);
assert!(references[0].trailing_trivia.is_empty());
assert_eq!(references[0].token.to_string(), "print");
assert!(references[0].leading_trivia.is_empty());
assert!(references[1].trailing_trivia.is_empty());
assert_eq!(references[1].token.to_string(), "(");
assert!(references[1].leading_trivia.is_empty());
assert!(references[2].trailing_trivia.is_empty());
assert_eq!(references[2].token.to_string(), "1");
assert!(references[2].leading_trivia.is_empty());
assert_eq!(references[3].trailing_trivia[0].to_string(), "\n");
assert_eq!(references[3].token.to_string(), ")");
assert!(references[3].leading_trivia.is_empty());
assert_eq!(
references[4].leading_trivia[0].to_string(),
"-- hello world",
);
assert_eq!(references[4].leading_trivia[1].to_string(), "\n");
assert_eq!(references[4].token.to_string(), "local");
assert_eq!(references[4].trailing_trivia[0].to_string(), " ");
}
#[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)
};
print(&new_ast);
}
#[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)
};
print(&new_ast);
}
#[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)
};
print(&new_ast);
}
#[test]
fn test_new_validity() {
let token: TokenReference = TokenReference::new(
Vec::new(),
Token::new(TokenType::Identifier {
identifier: "foo".into(),
}),
Vec::new(),
);
let expression = Expression::Value {
value: Box::new(Value::Var(Var::Name(token.clone()))),
#[cfg(feature = "roblox")]
type_assertion: None,
};
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);
}
}