pub mod owned;
#[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, Owned, 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;
#[derive(Clone, Debug, Default, Display, PartialEq, Owned, 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<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
stmts: Vec<(Stmt<'a>, Option<Cow<'a, TokenReference<'a>>>)>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
last_stmt: Option<(LastStmt<'a>, Option<Cow<'a, TokenReference<'a>>>)>,
}
impl<'a> Block<'a> {
pub fn new() -> Self {
Self {
stmts: Vec::new(),
last_stmt: None,
}
}
pub fn iter_stmts(&self) -> impl Iterator<Item = &Stmt<'a>> {
self.stmts.iter().map(|(stmt, _)| stmt)
}
#[deprecated(since = "0.5.0", note = "Use last_stmt instead")]
pub fn last_stmts(&self) -> Option<&LastStmt<'a>> {
self.last_stmt()
}
pub fn last_stmt(&self) -> Option<&LastStmt<'a>> {
Some(&self.last_stmt.as_ref()?.0)
}
pub fn with_stmts(self, stmts: Vec<(Stmt<'a>, Option<Cow<'a, TokenReference<'a>>>)>) -> Self {
Self { stmts, ..self }
}
pub fn with_last_stmt(
self,
last_stmt: Option<(LastStmt<'a>, Option<Cow<'a, TokenReference<'a>>>)>,
) -> Self {
Self { last_stmt, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum LastStmt<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
Break(Cow<'a, TokenReference<'a>>),
Return(Return<'a>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}", token, returns)]
pub struct Return<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
token: Cow<'a, TokenReference<'a>>,
returns: Punctuated<'a, Expression<'a>>,
}
impl<'a> Return<'a> {
pub fn new() -> Self {
Self {
token: Cow::Owned(TokenReference::symbol("return ").unwrap()),
returns: Punctuated::new(),
}
}
pub fn token(&self) -> &TokenReference<'a> {
&self.token
}
pub fn returns(&self) -> &Punctuated<'a, Expression<'a>> {
&self.returns
}
pub fn with_token(self, token: Cow<'a, TokenReference<'a>>) -> Self {
Self { token, ..self }
}
pub fn with_returns(self, returns: Punctuated<'a, Expression<'a>>) -> Self {
Self { returns, ..self }
}
}
impl Default for Return<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Field<'a> {
#[display(
fmt = "{}{}{}{}{}",
"brackets.tokens().0",
"key",
"brackets.tokens().1",
"equal",
"value"
)]
ExpressionKey {
#[cfg_attr(feature = "serde", serde(borrow))]
brackets: ContainedSpan<'a>,
key: Expression<'a>,
equal: Cow<'a, TokenReference<'a>>,
value: Expression<'a>,
},
#[display(fmt = "{}{}{}", "key", "equal", "value")]
NameKey {
#[cfg_attr(feature = "serde", serde(borrow))]
key: Cow<'a, TokenReference<'a>>,
equal: Cow<'a, TokenReference<'a>>,
value: Expression<'a>,
},
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", "_0")]
NoKey(Expression<'a>),
}
pub type TableConstructorField<'a> = (Field<'a>, Option<Cow<'a, TokenReference<'a>>>);
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
fmt = "{}{}{}",
"braces.tokens().0",
"display_optional_punctuated_vec(fields)",
"braces.tokens().1"
)]
pub struct TableConstructor<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[node(full_range)]
#[visit(contains = "fields")]
braces: ContainedSpan<'a>,
fields: Vec<TableConstructorField<'a>>,
}
impl<'a> TableConstructor<'a> {
pub fn new() -> Self {
Self {
braces: ContainedSpan::new(
Cow::Owned(TokenReference::symbol("{ ").unwrap()),
Cow::Owned(TokenReference::symbol(" }").unwrap()),
),
fields: Vec::new(),
}
}
pub fn braces(&self) -> &ContainedSpan<'a> {
&self.braces
}
pub fn iter_fields(&self) -> impl Iterator<Item = &TableConstructorField<'a>> {
self.fields.iter()
}
pub fn with_braces(self, braces: ContainedSpan<'a>) -> Self {
Self { braces, ..self }
}
pub fn with_fields(self, fields: Vec<TableConstructorField<'a>>) -> Self {
Self { fields, ..self }
}
}
impl Default for TableConstructor<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}", bin_op, rhs)]
#[visit(visit_as = "bin_op")]
pub struct BinOpRhs<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
bin_op: BinOp<'a>,
rhs: Box<Expression<'a>>,
}
impl<'a> BinOpRhs<'a> {
pub fn new(bin_op: BinOp<'a>, rhs: Box<Expression<'a>>) -> Self {
Self { bin_op, rhs }
}
pub fn bin_op(&self) -> &BinOp<'a> {
&self.bin_op
}
pub fn rhs(&self) -> &Expression<'a> {
self.rhs.as_ref()
}
pub fn with_bin_op(self, bin_op: BinOp<'a>) -> Self {
Self { bin_op, ..self }
}
pub fn with_rhs(self, rhs: Box<Expression<'a>>) -> Self {
Self { rhs, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Expression<'a> {
#[display(
fmt = "{}{}{}",
"contained.tokens().0",
"expression",
"contained.tokens().1"
)]
Parentheses {
#[cfg_attr(feature = "serde", serde(borrow))]
#[node(full_range)]
contained: ContainedSpan<'a>,
expression: Box<Expression<'a>>,
},
#[display(fmt = "{}{}", "unop", "expression")]
UnaryOperator {
#[cfg_attr(feature = "serde", serde(borrow))]
unop: UnOp<'a>,
expression: Box<Expression<'a>>,
},
#[cfg_attr(
not(feature = "roblox"),
display(fmt = "{}{}", value, "display_option(binop)")
)]
#[cfg_attr(
feature = "roblox",
display(
fmt = "{}{}{}",
value,
"display_option(binop)",
"display_option(as_assertion)"
)
)]
Value {
#[cfg_attr(feature = "serde", serde(borrow))]
value: Box<Value<'a>>,
binop: Option<BinOpRhs<'a>>,
#[cfg(feature = "roblox")]
#[cfg_attr(feature = "serde", serde(borrow))]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
as_assertion: Option<AsAssertion<'a>>,
},
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Value<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}{}", "_0.0", "_0.1")]
Function((Cow<'a, TokenReference<'a>>, FunctionBody<'a>)),
#[display(fmt = "{}", "_0")]
FunctionCall(FunctionCall<'a>),
#[display(fmt = "{}", "_0")]
TableConstructor(TableConstructor<'a>),
#[display(fmt = "{}", "_0")]
Number(Cow<'a, TokenReference<'a>>),
#[display(fmt = "{}", "_0")]
ParseExpression(Expression<'a>),
#[display(fmt = "{}", "_0")]
String(Cow<'a, TokenReference<'a>>),
#[display(fmt = "{}", "_0")]
Symbol(Cow<'a, TokenReference<'a>>),
#[display(fmt = "{}", "_0")]
Var(Var<'a>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Stmt<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", _0)]
Assignment(Assignment<'a>),
#[display(fmt = "{}", _0)]
Do(Do<'a>),
#[display(fmt = "{}", _0)]
FunctionCall(FunctionCall<'a>),
#[display(fmt = "{}", _0)]
FunctionDeclaration(FunctionDeclaration<'a>),
#[display(fmt = "{}", _0)]
GenericFor(GenericFor<'a>),
#[display(fmt = "{}", _0)]
If(If<'a>),
#[display(fmt = "{}", _0)]
LocalAssignment(LocalAssignment<'a>),
#[display(fmt = "{}", _0)]
LocalFunction(LocalFunction<'a>),
#[display(fmt = "{}", _0)]
NumericFor(NumericFor<'a>),
#[display(fmt = "{}", _0)]
Repeat(Repeat<'a>),
#[display(fmt = "{}", _0)]
While(While<'a>),
#[cfg(feature = "roblox")]
#[display(fmt = "{}", _0)]
CompoundAssignment(CompoundAssignment<'a>),
#[cfg(feature = "roblox")]
Continue(Cow<'a, TokenReference<'a>>),
#[cfg(feature = "roblox")]
TypeDeclaration(TypeDeclaration<'a>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Prefix<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", _0)]
Expression(Expression<'a>),
#[display(fmt = "{}", _0)]
Name(Cow<'a, TokenReference<'a>>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Index<'a> {
#[display(
fmt = "{}{}{}",
"brackets.tokens().0",
"expression",
"brackets.tokens().1"
)]
Brackets {
#[cfg_attr(feature = "serde", serde(borrow))]
brackets: ContainedSpan<'a>,
expression: Expression<'a>,
},
#[display(fmt = "{}{}", "dot", "name")]
Dot {
#[cfg_attr(feature = "serde", serde(borrow))]
dot: Cow<'a, TokenReference<'a>>,
name: Cow<'a, TokenReference<'a>>,
},
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum FunctionArgs<'a> {
#[display(
fmt = "{}{}{}",
"parentheses.tokens().0",
"arguments",
"parentheses.tokens().1"
)]
Parentheses {
#[node(full_range)]
parentheses: ContainedSpan<'a>,
#[cfg_attr(feature = "serde", serde(borrow))]
arguments: Punctuated<'a, Expression<'a>>,
},
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", "_0")]
String(Cow<'a, TokenReference<'a>>),
#[display(fmt = "{}", "_0")]
TableConstructor(TableConstructor<'a>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
fmt = "{}{}{}{}{}{}{}{}{}{}{}",
"for_token",
"index_variable",
"equal_token",
"start",
"start_end_comma",
"end",
"display_option(end_step_comma)",
"display_option(step)",
"do_token",
"block",
"end_token"
)]
pub struct NumericFor<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
for_token: Cow<'a, TokenReference<'a>>,
index_variable: Cow<'a, TokenReference<'a>>,
equal_token: Cow<'a, TokenReference<'a>>,
start: Expression<'a>,
start_end_comma: Cow<'a, TokenReference<'a>>,
end: Expression<'a>,
end_step_comma: Option<Cow<'a, TokenReference<'a>>>,
step: Option<Expression<'a>>,
do_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
end_token: Cow<'a, TokenReference<'a>>,
}
impl<'a> NumericFor<'a> {
pub fn new(
index_variable: Cow<'a, TokenReference<'a>>,
start: Expression<'a>,
end: Expression<'a>,
) -> Self {
Self {
for_token: Cow::Owned(TokenReference::symbol("for ").unwrap()),
index_variable,
equal_token: Cow::Owned(TokenReference::symbol(" = ").unwrap()),
start,
start_end_comma: Cow::Owned(TokenReference::symbol(", ").unwrap()),
end,
end_step_comma: None,
step: None,
do_token: Cow::Owned(TokenReference::symbol(" do\n").unwrap()),
block: Block::new(),
end_token: Cow::Owned(TokenReference::symbol("\nend").unwrap()),
}
}
pub fn for_token(&self) -> &TokenReference<'a> {
&self.for_token
}
pub fn index_variable(&self) -> &TokenReference<'a> {
&self.index_variable
}
pub fn equal_token(&self) -> &TokenReference<'a> {
&self.equal_token
}
pub fn start(&self) -> &Expression<'a> {
&self.start
}
pub fn start_end_comma(&self) -> &TokenReference<'a> {
&self.start_end_comma
}
pub fn end(&self) -> &Expression<'a> {
&self.end
}
pub fn end_step_comma(&self) -> Option<&TokenReference<'a>> {
self.end_step_comma.as_deref()
}
pub fn step(&self) -> Option<&Expression<'a>> {
self.step.as_ref()
}
pub fn do_token(&self) -> &TokenReference<'a> {
&self.do_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn end_token(&self) -> &TokenReference<'a> {
&self.end_token
}
pub fn with_for_token(self, for_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { for_token, ..self }
}
pub fn with_index_variable(self, index_variable: Cow<'a, TokenReference<'a>>) -> Self {
Self {
index_variable,
..self
}
}
pub fn with_equal_token(self, equal_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
equal_token,
..self
}
}
pub fn with_start(self, start: Expression<'a>) -> Self {
Self { start, ..self }
}
pub fn with_start_end_comma(self, start_end_comma: Cow<'a, TokenReference<'a>>) -> Self {
Self {
start_end_comma,
..self
}
}
pub fn with_end(self, end: Expression<'a>) -> Self {
Self { end, ..self }
}
pub fn with_end_step_comma(self, end_step_comma: Option<Cow<'a, TokenReference<'a>>>) -> Self {
Self {
end_step_comma,
..self
}
}
pub fn with_step(self, step: Option<Expression<'a>>) -> Self {
Self { step, ..self }
}
pub fn with_do_token(self, do_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { end_token, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
fmt = "{}{}{}{}{}{}{}",
"for_token",
"names",
"in_token",
"expr_list",
"do_token",
"block",
"end_token"
)]
pub struct GenericFor<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
for_token: Cow<'a, TokenReference<'a>>,
names: Punctuated<'a, Cow<'a, TokenReference<'a>>>,
in_token: Cow<'a, TokenReference<'a>>,
expr_list: Punctuated<'a, Expression<'a>>,
do_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
end_token: Cow<'a, TokenReference<'a>>,
}
impl<'a> GenericFor<'a> {
pub fn new(
names: Punctuated<'a, Cow<'a, TokenReference<'a>>>,
expr_list: Punctuated<'a, Expression<'a>>,
) -> Self {
Self {
for_token: Cow::Owned(TokenReference::symbol("for ").unwrap()),
names,
in_token: Cow::Owned(TokenReference::symbol(" in ").unwrap()),
expr_list,
do_token: Cow::Owned(TokenReference::symbol(" do\n").unwrap()),
block: Block::new(),
end_token: Cow::Owned(TokenReference::symbol("\nend").unwrap()),
}
}
pub fn for_token(&self) -> &TokenReference<'a> {
&self.for_token
}
pub fn names(&self) -> &Punctuated<'a, Cow<'a, TokenReference<'a>>> {
&self.names
}
pub fn in_token(&self) -> &TokenReference<'a> {
&self.in_token
}
pub fn expr_list(&self) -> &Punctuated<'a, Expression<'a>> {
&self.expr_list
}
pub fn do_token(&self) -> &TokenReference<'a> {
&self.do_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn end_token(&self) -> &TokenReference<'a> {
&self.end_token
}
pub fn with_for_token(self, for_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { for_token, ..self }
}
pub fn with_names(self, names: Punctuated<'a, Cow<'a, TokenReference<'a>>>) -> Self {
Self { names, ..self }
}
pub fn with_in_token(self, in_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { in_token, ..self }
}
pub fn with_expr_list(self, expr_list: Punctuated<'a, Expression<'a>>) -> Self {
Self { expr_list, ..self }
}
pub fn with_do_token(self, do_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { end_token, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, 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<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
if_token: Cow<'a, TokenReference<'a>>,
condition: Expression<'a>,
then_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
else_if: Option<Vec<ElseIf<'a>>>,
else_token: Option<Cow<'a, TokenReference<'a>>>,
#[cfg_attr(feature = "serde", serde(rename = "else"))]
r#else: Option<Block<'a>>,
end_token: Cow<'a, TokenReference<'a>>,
}
impl<'a> If<'a> {
pub fn new(condition: Expression<'a>) -> Self {
Self {
if_token: Cow::Owned(TokenReference::symbol("if ").unwrap()),
condition,
then_token: Cow::Owned(TokenReference::symbol(" then").unwrap()),
block: Block::new(),
else_if: None,
else_token: None,
r#else: None,
end_token: Cow::Owned(TokenReference::symbol("\nend").unwrap()),
}
}
pub fn if_token(&self) -> &TokenReference<'a> {
&self.if_token
}
pub fn condition(&self) -> &Expression<'a> {
&self.condition
}
pub fn then_token(&self) -> &TokenReference<'a> {
&self.then_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn else_token(&self) -> Option<&TokenReference<'a>> {
self.else_token.as_deref()
}
pub fn else_if(&self) -> Option<&Vec<ElseIf<'a>>> {
self.else_if.as_ref()
}
pub fn else_block(&self) -> Option<&Block<'a>> {
self.r#else.as_ref()
}
pub fn end_token(&self) -> &TokenReference<'a> {
&self.end_token
}
pub fn with_if_token(self, if_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { if_token, ..self }
}
pub fn with_condition(self, condition: Expression<'a>) -> Self {
Self { condition, ..self }
}
pub fn with_then_token(self, then_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { then_token, ..self }
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_else_if(self, else_if: Option<Vec<ElseIf<'a>>>) -> Self {
Self { else_if, ..self }
}
pub fn with_else_token(self, else_token: Option<Cow<'a, TokenReference<'a>>>) -> Self {
Self { else_token, ..self }
}
pub fn with_else(self, r#else: Option<Block<'a>>) -> Self {
Self { r#else, ..self }
}
pub fn with_end_token(self, end_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { end_token, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}{}", "else_if_token", "condition", "then_token", "block")]
pub struct ElseIf<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
else_if_token: Cow<'a, TokenReference<'a>>,
condition: Expression<'a>,
then_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
}
impl<'a> ElseIf<'a> {
pub fn new(condition: Expression<'a>) -> Self {
Self {
else_if_token: Cow::Owned(TokenReference::symbol("elseif ").unwrap()),
condition,
then_token: Cow::Owned(TokenReference::symbol(" then\n").unwrap()),
block: Block::new(),
}
}
pub fn else_if_token(&self) -> &TokenReference<'a> {
&self.else_if_token
}
pub fn condition(&self) -> &Expression<'a> {
&self.condition
}
pub fn then_token(&self) -> &TokenReference<'a> {
&self.then_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn with_else_if_token(self, else_if_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
else_if_token,
..self
}
}
pub fn with_condition(self, condition: Expression<'a>) -> Self {
Self { condition, ..self }
}
pub fn with_then_token(self, then_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { then_token, ..self }
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(
fmt = "{}{}{}{}{}",
"while_token",
"condition",
"do_token",
"block",
"end_token"
)]
pub struct While<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
while_token: Cow<'a, TokenReference<'a>>,
condition: Expression<'a>,
do_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
end_token: Cow<'a, TokenReference<'a>>,
}
impl<'a> While<'a> {
pub fn new(condition: Expression<'a>) -> Self {
Self {
while_token: Cow::Owned(TokenReference::symbol("while ").unwrap()),
condition,
do_token: Cow::Owned(TokenReference::symbol(" do\n").unwrap()),
block: Block::new(),
end_token: Cow::Owned(TokenReference::symbol("end\n").unwrap()),
}
}
pub fn while_token(&self) -> &TokenReference<'a> {
&self.while_token
}
pub fn condition(&self) -> &Expression<'a> {
&self.condition
}
pub fn do_token(&self) -> &TokenReference<'a> {
&self.do_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn end_token(&self) -> &TokenReference<'a> {
&self.end_token
}
pub fn with_while_token(self, while_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
while_token,
..self
}
}
pub fn with_condition(self, condition: Expression<'a>) -> Self {
Self { condition, ..self }
}
pub fn with_do_token(self, do_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { end_token, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}{}", "repeat_token", "block", "until_token", "until")]
pub struct Repeat<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
repeat_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
until_token: Cow<'a, TokenReference<'a>>,
until: Expression<'a>,
}
impl<'a> Repeat<'a> {
pub fn new(until: Expression<'a>) -> Self {
Self {
repeat_token: Cow::Owned(TokenReference::symbol("repeat\n").unwrap()),
block: Block::new(),
until_token: Cow::Owned(TokenReference::symbol("\nuntil ").unwrap()),
until,
}
}
pub fn repeat_token(&self) -> &TokenReference<'a> {
&self.repeat_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn until_token(&self) -> &TokenReference<'a> {
&self.until_token
}
pub fn until(&self) -> &Expression<'a> {
&self.until
}
pub fn with_repeat_token(self, repeat_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
repeat_token,
..self
}
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_until_token(self, until_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
until_token,
..self
}
}
pub fn with_until(self, until: Expression<'a>) -> Self {
Self { until, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "colon_token", "name", "args")]
pub struct MethodCall<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
colon_token: Cow<'a, TokenReference<'a>>,
name: Cow<'a, TokenReference<'a>>,
args: FunctionArgs<'a>,
}
impl<'a> MethodCall<'a> {
pub fn new(name: Cow<'a, TokenReference<'a>>, args: FunctionArgs<'a>) -> Self {
Self {
colon_token: Cow::Owned(TokenReference::symbol(":").unwrap()),
name,
args,
}
}
pub fn colon_token(&self) -> &TokenReference<'a> {
&self.colon_token
}
pub fn args(&self) -> &FunctionArgs<'a> {
&self.args
}
pub fn name(&self) -> &TokenReference<'a> {
&self.name
}
pub fn with_colon_token(self, colon_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
colon_token,
..self
}
}
pub fn with_name(self, name: Cow<'a, TokenReference<'a>>) -> Self {
Self { name, ..self }
}
pub fn with_args(self, args: FunctionArgs<'a>) -> Self {
Self { args, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Call<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", "_0")]
AnonymousCall(FunctionArgs<'a>),
#[display(fmt = "{}", "_0")]
MethodCall(MethodCall<'a>),
}
#[derive(Clone, Debug, PartialEq, Owned, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct FunctionBody<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
parameters_parentheses: ContainedSpan<'a>,
parameters: Punctuated<'a, Parameter<'a>>,
#[cfg(feature = "roblox")]
#[cfg_attr(feature = "serde", serde(borrow))]
type_specifiers: Vec<Option<TypeSpecifier<'a>>>,
#[cfg(feature = "roblox")]
#[cfg_attr(feature = "serde", serde(borrow))]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
return_type: Option<TypeSpecifier<'a>>,
block: Block<'a>,
end_token: Cow<'a, TokenReference<'a>>,
}
impl<'a> FunctionBody<'a> {
pub fn new() -> Self {
Self {
parameters_parentheses: ContainedSpan::new(
Cow::Owned(TokenReference::symbol("(").unwrap()),
Cow::Owned(TokenReference::symbol(")").unwrap()),
),
parameters: Punctuated::new(),
#[cfg(feature = "roblox")]
type_specifiers: Vec::new(),
#[cfg(feature = "roblox")]
return_type: None,
block: Block::new(),
end_token: Cow::Owned(TokenReference::symbol("\nend").unwrap()),
}
}
pub fn parameters_parentheses(&self) -> &ContainedSpan<'a> {
&self.parameters_parentheses
}
pub fn iter_parameters(&self) -> impl Iterator<Item = &Parameter<'a>> {
self.parameters.iter()
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn end_token(&self) -> &TokenReference<'a> {
&self.end_token
}
#[cfg(feature = "roblox")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier<'a>>> {
self.type_specifiers.iter().map(Option::as_ref)
}
#[cfg(feature = "roblox")]
pub fn return_type(&self) -> Option<&TypeSpecifier<'a>> {
self.return_type.as_ref()
}
pub fn with_parameters_parentheses(self, parameters_parentheses: ContainedSpan<'a>) -> Self {
Self {
parameters_parentheses,
..self
}
}
pub fn with_parameters(self, parameters: Punctuated<'a, Parameter<'a>>) -> Self {
Self { parameters, ..self }
}
#[cfg(feature = "roblox")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier<'a>>>) -> Self {
Self {
type_specifiers,
..self
}
}
#[cfg(feature = "roblox")]
pub fn with_return_type(self, return_type: Option<TypeSpecifier<'a>>) -> Self {
Self {
return_type,
..self
}
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: Cow<'a, TokenReference<'a>>) -> 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,
"{}{}{}{}{}{}",
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, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Parameter<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
Ellipse(Cow<'a, TokenReference<'a>>),
Name(Cow<'a, TokenReference<'a>>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Suffix<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", "_0")]
Call(Call<'a>),
#[display(fmt = "{}", "_0")]
Index(Index<'a>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}", "prefix", "join_vec(suffixes)")]
pub struct VarExpression<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
prefix: Prefix<'a>,
suffixes: Vec<Suffix<'a>>,
}
impl<'a> VarExpression<'a> {
pub fn new(prefix: Prefix<'a>) -> Self {
Self {
prefix,
suffixes: Vec::new(),
}
}
pub fn prefix(&self) -> &Prefix<'a> {
&self.prefix
}
pub fn iter_suffixes(&self) -> impl Iterator<Item = &Suffix<'a>> {
self.suffixes.iter()
}
pub fn with_prefix(self, prefix: Prefix<'a>) -> Self {
Self { prefix, ..self }
}
pub fn with_suffixes(self, suffixes: Vec<Suffix<'a>>) -> Self {
Self { suffixes, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Var<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[display(fmt = "{}", "_0")]
Expression(VarExpression<'a>),
#[display(fmt = "{}", "_0")]
Name(Cow<'a, TokenReference<'a>>),
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "var_list", "equal_token", "expr_list")]
pub struct Assignment<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
var_list: Punctuated<'a, Var<'a>>,
equal_token: Cow<'a, TokenReference<'a>>,
expr_list: Punctuated<'a, Expression<'a>>,
}
impl<'a> Assignment<'a> {
pub fn new(
var_list: Punctuated<'a, Var<'a>>,
expr_list: Punctuated<'a, Expression<'a>>,
) -> Self {
Self {
var_list,
equal_token: Cow::Owned(TokenReference::symbol(" = ").unwrap()),
expr_list,
}
}
pub fn expr_list(&self) -> &Punctuated<'a, Expression<'a>> {
&self.expr_list
}
pub fn equal_token(&self) -> &TokenReference<'a> {
&self.equal_token
}
pub fn var_list(&self) -> &Punctuated<'a, Var<'a>> {
&self.var_list
}
pub fn with_var_list(self, var_list: Punctuated<'a, Var<'a>>) -> Self {
Self { var_list, ..self }
}
pub fn with_equal_token(self, equal_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
equal_token,
..self
}
}
pub fn with_expr_list(self, expr_list: Punctuated<'a, Expression<'a>>) -> Self {
Self { expr_list, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}{}", "local_token", "function_token", "name", "func_body")]
pub struct LocalFunction<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
local_token: Cow<'a, TokenReference<'a>>,
function_token: Cow<'a, TokenReference<'a>>,
name: Cow<'a, TokenReference<'a>>,
func_body: FunctionBody<'a>,
}
impl<'a> LocalFunction<'a> {
pub fn new(name: Cow<'a, TokenReference<'a>>) -> Self {
LocalFunction {
local_token: Cow::Owned(TokenReference::symbol("local ").unwrap()),
function_token: Cow::Owned(TokenReference::symbol("function ").unwrap()),
name,
func_body: FunctionBody::new(),
}
}
pub fn local_token(&self) -> &TokenReference<'a> {
&self.local_token
}
pub fn function_token(&self) -> &TokenReference<'a> {
&self.function_token
}
pub fn func_body(&self) -> &FunctionBody<'a> {
&self.func_body
}
pub fn name(&self) -> &TokenReference<'a> {
&self.name
}
pub fn with_local_token(self, local_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
local_token,
..self
}
}
pub fn with_function_token(self, function_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
function_token,
..self
}
}
pub fn with_name(self, name: Cow<'a, TokenReference<'a>>) -> Self {
Self { name, ..self }
}
pub fn with_func_body(self, func_body: FunctionBody<'a>) -> Self {
Self { func_body, ..self }
}
}
#[derive(Clone, Debug, PartialEq, Owned, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct LocalAssignment<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
local_token: Cow<'a, TokenReference<'a>>,
#[cfg(feature = "roblox")]
#[cfg_attr(feature = "serde", serde(borrow))]
type_specifiers: Vec<Option<TypeSpecifier<'a>>>,
name_list: Punctuated<'a, Cow<'a, TokenReference<'a>>>,
equal_token: Option<Cow<'a, TokenReference<'a>>>,
expr_list: Punctuated<'a, Expression<'a>>,
}
impl<'a> LocalAssignment<'a> {
pub fn new(name_list: Punctuated<'a, Cow<'a, TokenReference<'a>>>) -> Self {
Self {
local_token: Cow::Owned(TokenReference::symbol("local ").unwrap()),
#[cfg(feature = "roblox")]
type_specifiers: Vec::new(),
name_list,
equal_token: None,
expr_list: Punctuated::new(),
}
}
pub fn local_token(&self) -> &TokenReference<'a> {
&self.local_token
}
pub fn equal_token(&self) -> Option<&TokenReference<'a>> {
self.equal_token.as_deref()
}
pub fn expr_list(&self) -> &Punctuated<'a, Expression<'a>> {
&self.expr_list
}
pub fn name_list(&self) -> &Punctuated<'a, Cow<'a, TokenReference<'a>>> {
&self.name_list
}
#[cfg(feature = "roblox")]
pub fn type_specifiers(&self) -> impl Iterator<Item = Option<&TypeSpecifier<'a>>> {
self.type_specifiers.iter().map(Option::as_ref)
}
pub fn with_local_token(self, local_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
local_token,
..self
}
}
#[cfg(feature = "roblox")]
pub fn with_type_specifiers(self, type_specifiers: Vec<Option<TypeSpecifier<'a>>>) -> Self {
Self {
type_specifiers,
..self
}
}
pub fn with_name_list(self, name_list: Punctuated<'a, Cow<'a, TokenReference<'a>>>) -> Self {
Self { name_list, ..self }
}
pub fn with_equal_token(self, equal_token: Option<Cow<'a, TokenReference<'a>>>) -> Self {
Self {
equal_token,
..self
}
}
pub fn with_expr_list(self, expr_list: Punctuated<'a, Expression<'a>>) -> Self {
Self { expr_list, ..self }
}
}
impl fmt::Display for LocalAssignment<'_> {
#[cfg(feature = "roblox")]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}",
self.local_token,
join_type_specifiers(&self.name_list, self.type_specifiers()),
display_option(&self.equal_token),
self.expr_list
)
}
#[cfg(not(feature = "roblox"))]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{}{}{}{}",
self.local_token,
self.name_list,
display_option(&self.equal_token),
self.expr_list
)
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "do_token", "block", "end_token")]
pub struct Do<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
do_token: Cow<'a, TokenReference<'a>>,
block: Block<'a>,
end_token: Cow<'a, TokenReference<'a>>,
}
impl<'a> Do<'a> {
pub fn new() -> Self {
Self {
do_token: Cow::Owned(TokenReference::symbol("do\n").unwrap()),
block: Block::new(),
end_token: Cow::Owned(TokenReference::symbol("\nend").unwrap()),
}
}
pub fn do_token(&self) -> &TokenReference<'a> {
&self.do_token
}
pub fn block(&self) -> &Block<'a> {
&self.block
}
pub fn end_token(&self) -> &TokenReference<'a> {
&self.end_token
}
pub fn with_do_token(self, do_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { do_token, ..self }
}
pub fn with_block(self, block: Block<'a>) -> Self {
Self { block, ..self }
}
pub fn with_end_token(self, end_token: Cow<'a, TokenReference<'a>>) -> Self {
Self { end_token, ..self }
}
}
impl Default for Do<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}", "prefix", "join_vec(suffixes)")]
pub struct FunctionCall<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
prefix: Prefix<'a>,
suffixes: Vec<Suffix<'a>>,
}
impl<'a> FunctionCall<'a> {
pub fn new(prefix: Prefix<'a>) -> Self {
FunctionCall {
prefix,
suffixes: vec![Suffix::Call(Call::AnonymousCall(
FunctionArgs::Parentheses {
arguments: Punctuated::new(),
parentheses: ContainedSpan::new(
Cow::Owned(TokenReference::symbol("(").unwrap()),
Cow::Owned(TokenReference::symbol(")").unwrap()),
),
},
))],
}
}
pub fn prefix(&self) -> &Prefix<'a> {
&self.prefix
}
pub fn iter_suffixes(&self) -> impl Iterator<Item = &Suffix<'a>> {
self.suffixes.iter()
}
pub fn with_prefix(self, prefix: Prefix<'a>) -> Self {
Self { prefix, ..self }
}
pub fn with_suffixes(self, suffixes: Vec<Suffix<'a>>) -> Self {
Self { suffixes, ..self }
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, 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<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
names: Punctuated<'a, Cow<'a, TokenReference<'a>>>,
colon_name: Option<(Cow<'a, TokenReference<'a>>, Cow<'a, TokenReference<'a>>)>,
}
impl<'a> FunctionName<'a> {
pub fn new(names: Punctuated<'a, Cow<'a, TokenReference<'a>>>) -> Self {
Self {
names,
colon_name: None,
}
}
pub fn method_colon(&self) -> Option<&TokenReference<'a>> {
Some(&self.colon_name.as_ref()?.0)
}
pub fn method_name(&self) -> Option<&TokenReference<'a>> {
Some(&self.colon_name.as_ref()?.1)
}
pub fn names(&self) -> &Punctuated<'a, Cow<'a, TokenReference<'a>>> {
&self.names
}
pub fn with_names(self, names: Punctuated<'a, Cow<'a, TokenReference<'a>>>) -> Self {
Self { names, ..self }
}
pub fn with_method(
self,
method: Option<(Cow<'a, TokenReference<'a>>, Cow<'a, TokenReference<'a>>)>,
) -> Self {
Self {
colon_name: method,
..self
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Owned, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{}{}", "function_token", "name", "body")]
pub struct FunctionDeclaration<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
function_token: Cow<'a, TokenReference<'a>>,
name: FunctionName<'a>,
body: FunctionBody<'a>,
}
impl<'a> FunctionDeclaration<'a> {
pub fn new(name: FunctionName<'a>) -> Self {
Self {
function_token: Cow::Owned(TokenReference::symbol("function ").unwrap()),
name,
body: FunctionBody::new(),
}
}
pub fn function_token(&self) -> &TokenReference<'a> {
&self.function_token
}
pub fn body(&self) -> &FunctionBody<'a> {
&self.body
}
pub fn name(&self) -> &FunctionName<'a> {
&self.name
}
pub fn with_function_token(self, function_token: Cow<'a, TokenReference<'a>>) -> Self {
Self {
function_token,
..self
}
}
pub fn with_name(self, name: FunctionName<'a>) -> Self {
Self { name, ..self }
}
pub fn with_body(self, body: FunctionBody<'a>) -> 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,
}
);
make_op!(UnOp,
#[doc = "Operators that require just one operand, such as #X"]
{
Minus,
Not,
Hash,
}
);
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum AstError<'a> {
Empty,
NoEof,
UnexpectedToken {
#[cfg_attr(feature = "serde", serde(borrow))]
token: Token<'a>,
additional: Option<Cow<'a, str>>,
},
}
impl<'a> fmt::Display for AstError<'a> {
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<'a> std::error::Error for AstError<'a> {}
#[derive(Clone, Debug, Owned)]
pub struct Ast<'a> {
pub(crate) nodes: Block<'a>,
pub(crate) tokens: Vec<TokenReference<'a>>,
}
impl<'a> Ast<'a> {
pub fn from_tokens(tokens: Vec<Token<'a>>) -> Result<Ast<'a>, AstError<'a>> {
if *tokens.last().ok_or(AstError::Empty)?.token_type() != TokenType::Eof {
Err(AstError::NoEof)
} else {
let 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,
},
tokens,
});
}
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,
tokens,
})
} else {
Err(AstError::UnexpectedToken {
token: (*state.peek()).to_owned().token,
additional: Some(Cow::Borrowed("leftover token")),
})
}
}
Err(InternalAstError::NoMatch) => Err(AstError::UnexpectedToken {
token: (*state.peek()).to_owned().token,
additional: None,
}),
Err(InternalAstError::UnexpectedToken { token, additional }) => {
Err(AstError::UnexpectedToken {
token: (*token).to_owned(),
additional: additional.map(Cow::Borrowed),
})
}
}
}
}
pub fn with_nodes(self, nodes: Block<'a>) -> Self {
Self { nodes, ..self }
}
pub fn with_eof(mut self, eof: TokenReference<'a>) -> Self {
self.tokens.pop();
self.tokens.push(eof);
Self {
tokens: self.tokens,
..self
}
}
pub fn nodes(&self) -> &Block<'a> {
&self.nodes
}
pub fn nodes_mut(&mut self) -> &mut Block<'a> {
&mut self.nodes
}
pub fn eof(&self) -> &TokenReference<'a> {
self.tokens.last().expect("no eof token, somehow?")
}
}
pub(crate) fn extract_token_references<'a>(mut tokens: Vec<Token<'a>>) -> Vec<TokenReference<'a>> {
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() {
if let TokenType::Whitespace { ref characters } = &*token.token_type() {
if characters.contains('\n') {
break;
}
}
trailing_trivia.push(tokens.next().unwrap());
} 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[4].leading_trivia[0].to_string(), "\n");
assert_eq!(
references[4].leading_trivia[1].to_string(),
"-- hello world",
);
assert_eq!(references[4].leading_trivia[2].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<'ast> VisitorMut<'ast> for SyntaxRewriter {
fn visit_token(&mut self, token: Token<'ast>) -> Token<'ast> {
token
}
}
SyntaxRewriter.visit_ast(ast)
};
print(&new_ast);
}
#[test]
fn test_new_validity() {
let token: Cow<TokenReference> = Cow::Owned(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()))),
binop: None,
#[cfg(feature = "roblox")]
as_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.clone(), expression.clone(), expression.clone());
Repeat::new(expression.clone());
Return::new();
TableConstructor::new();
While::new(expression.clone());
}
}