luaparse 0.2.0

A Lua 5.3 parser
Documentation
//! Syntax tree nodes.

// otherwise half the enums here generate warnings for stupid reasons
#![allow(clippy::large_enum_variant)]

use crate::span::{HasSpan, Span};
use crate::symbol::Symbol;
use crate::token::{Token, TokenKind, TokenValue};
use crate::visitor::{AstDescend, VisitorMut};

macro_rules! impl_span {
    // most generic version
    ($name:ident, |$s:ident| $span:expr) => {
        impl HasSpan for $name<'_> {
            fn span(&self) -> Span {
                let $s = self;
                $span
            }
        }
    };

    // assumes self.0 has span
    ($name:ident) => {
        impl_span!($name, |s| s.0.span());
    };

    ($name:ident, $field:ident) => {
        impl_span!($name, |s| s.$field.span());
    };

    ($name:ident, $start:ident, $end:ident) => {
        impl_span!($name, |s| Span::consecutive(&s.$start, &s.$end));
    };
}

macro_rules! impl_descend {
    ($name:ident: |$s:ident, $v:ident| $descend:expr) => {
        impl<'a> AstDescend<'a> for $name<'a> {
            fn descend_mut<T: VisitorMut<'a>>(&mut self, visitor: &mut T) {
                let $s = self;
                let $v = visitor;
                $descend;
            }
        }
    };
}

/// Holds a significant token and any insignificant tokens (comments, whitespace) around it.
#[derive(Clone, Debug)]
pub struct TokenReference<'a> {
    /// A list of insignificant tokens preceding the `token`.
    pub leading_trivia: Vec<Token<'a>>,
    /// The significant token.
    pub token: Token<'a>,
    /// A list of insignificant tokens following the `token`.
    pub trailing_trivia: Vec<Token<'a>>,
}

impl_span!(TokenReference, token);

/// A `nil` literal.
///
/// Holds a reference to the `Symbol::Nil` token.
#[derive(Clone, Debug)]
pub struct NilLit<'a>(pub TokenReference<'a>);
impl_span!(NilLit);

/// A boolean literal.
///
/// Holds a reference to the `Symbol::True` or `Symbol::False` token.
#[derive(Clone, Debug)]
pub struct BooleanLit<'a>(pub TokenReference<'a>);
impl_span!(BooleanLit);

impl BooleanLit<'_> {
    /// Possible token kinds for this node.
    pub const TOKEN_KINDS: &'static [TokenKind] = &[
        TokenKind::Symbol(Symbol::True),
        TokenKind::Symbol(Symbol::False),
    ];
}

/// A number literal.
///
/// A reference to the number literal token.
#[derive(Clone, Debug)]
pub struct NumberLit<'a>(pub TokenReference<'a>);
impl_span!(NumberLit);

/// A string literal.
///
/// Holds a reference to the string literal token.
#[derive(Clone, Debug)]
pub struct StringLit<'a>(pub TokenReference<'a>);
impl_span!(StringLit);

/// A name (a.k.a. identifier).
///
/// Holds a reference to the identifier token.
#[derive(Clone, Debug)]
pub struct Name<'a>(pub TokenReference<'a>);
impl_span!(Name);

/// A vararg (`...`).
///
/// Holds a reference to the `Symbol::TriplePeriod` token.
#[derive(Clone, Debug)]
pub struct Vararg<'a>(pub TokenReference<'a>);
impl_span!(Vararg);

macro_rules! define_op {
    ($name:ident($kind:ident: $ty:ty); $($op:ident, $power:expr, $symbol:ident,)+) => {
        /// Operator kind.
        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
        pub enum $kind {
            $($op,)+
        }

        impl $kind {
            /// Attempts to parse a symbol as an operator. Returns `Some(op_kind)` on success;
            /// `None` otherwise.
            pub fn from_symbol(symbol: Symbol) -> Option<Self> {
                match symbol {
                    $(Symbol::$symbol => Some(Self::$op),)+
                    _ => None
                }
            }

            /// Returns the `Symbol` corresponding to the operator kind.
            pub fn as_symbol(self) -> Symbol {
                match self {
                    $(Self::$op => Symbol::$symbol,)+
                }
            }

            /// Returns the binding power of the operator. Higher values have higher precedence.
            ///
            /// If the right value is larger than the left value, the operator is
            /// left-associative; otherwise, it is right-associative.
            ///
            /// Unary operators' binding power is represented as `((), u8)`, as they can only bind
            /// to the tokens to the right of them.
            pub fn binding_power(self) -> $ty {
                match self {
                    $(Self::$op => $power,)+
                }
            }
        }

        /// An operator node.
        #[derive(Clone, Debug)]
        pub struct $name<'a>(
            /// A reference to the symbol representing the operator.
            pub TokenReference<'a>
        );
        impl_span!($name);

        impl $name<'_> {
            /// A list of token kinds that can be parsed as an operator.
            pub const TOKEN_KINDS: &'static [TokenKind] =
                &[$(TokenKind::Symbol(Symbol::$symbol),)+];

            /// Return the token kind associated with this operator.
            pub fn kind(&self) -> $kind {
                match self.0.token.value {
                    TokenValue::Symbol(s) => $kind::from_symbol(s).expect("Invalid operator"),
                    _ => panic!("Invalid operator"),
                }
            }
        }
    }
}

define_op! {
    BinOp(BinOpKind: (u8, u8));
    // operator     l   r   symbol  (l and r are binding power for left and right side respectively)
    Or,           ( 1,  2), Or,
    And,          ( 3,  4), And,
    Less,         ( 5,  6), Less,
    Greater,      ( 5,  6), Greater,
    LessEqual,    ( 5,  6), LessEqual,
    GreaterEqual, ( 5,  6), GreaterEqual,
    NotEqual,     ( 5,  6), NotEqual,
    Equal,        ( 5,  6), Equal,
    BitOr,        ( 7,  8), BitOr,
    BitXor,       ( 9, 10), BitXor,
    BitAnd,       (11, 12), BitAnd,
    ShiftLeft,    (13, 14), ShiftLeft,
    ShiftRight,   (13, 14), ShiftRight,
    Concat,       (16, 15), DoublePeriod,
    Add,          (17, 18), Add,
    Sub,          (17, 18), Sub,
    Mul,          (19, 20), Mul,
    Div,          (19, 20), Div,
    IntDiv,       (19, 20), IntDiv,
    Rem,          (19, 20), Rem,
    // unary operators lie here
    Pow,          (23, 22), Pow,
}

define_op! {
    UnOp(UnOpKind: ((), u8));
    // operator      l   r   symbol)
    Not,          ( (), 21), Not,
    Len,          ( (), 21), Len,
    Neg,          ( (), 21), Sub,
    BitNot,       ( (), 21), BitXor,
}

/// A binary operation expression.
#[derive(Clone, Debug)]
pub struct BinOpExpr<'a> {
    /// The left expression.
    pub left: Box<Expr<'a>>,
    /// The operator.
    pub op: BinOp<'a>,
    /// The right expression.
    pub right: Box<Expr<'a>>,
}

impl_span!(BinOpExpr, left, right);
impl_descend!(BinOpExpr: |s, v| {
    v.visit_expr(&mut s.left);
    v.visit_expr(&mut s.right);
});

/// An unary operation expression.
#[derive(Clone, Debug)]
pub struct UnOpExpr<'a> {
    /// The operator.
    pub op: UnOp<'a>,
    /// The right expression.
    pub right: Box<Expr<'a>>,
}

impl_span!(UnOpExpr, op, right);
impl_descend!(UnOpExpr: |s, v| {
    v.visit_expr(&mut s.right);
});

/// A node that stores a pair of brackets of some kind (parentheses, braces, brackets).
///
/// The first element is the opening bracket, the second the closing.
#[derive(Clone, Debug)]
pub struct Brackets<'a>(pub TokenReference<'a>, pub TokenReference<'a>);
impl_span!(Brackets, |s| Span::consecutive(&s.0, &s.1));

/// An expression wrapped in parentheses.
#[derive(Clone, Debug)]
pub struct ParenthesizedExpr<'a> {
    /// The brackets wrapping the expression.
    pub brackets: Brackets<'a>,
    /// The inner expression.
    pub expr: Box<Expr<'a>>,
}

impl_span!(ParenthesizedExpr, brackets);

/// A list of nodes separated with a token.
#[derive(Clone, Debug)]
pub struct Punctuated<'a, T> {
    /// A list of pairs of the node and the separator that follows it, if any.
    pub pairs: Vec<(T, Option<TokenReference<'a>>)>,
}

impl<T: HasSpan> HasSpan for Punctuated<'_, T> {
    fn span(&self) -> Span {
        if self.pairs.is_empty() {
            return Span::default();
        }

        let start = self.pairs[0].0.span();
        let last = self.pairs.last().unwrap();
        let end = last.1.as_ref().map(|v| v.span()).unwrap_or_else(|| last.0.span());
        Span::consecutive(start, end)
    }
}

/// A punctuated list wrapped in brackets.
#[derive(Clone, Debug)]
pub struct ParenthesizedList<'a, T> {
    pub brackets: Brackets<'a>,
    pub list: Punctuated<'a, T>,
}

impl<T> HasSpan for ParenthesizedList<'_, T> {
    fn span(&self) -> Span {
        self.brackets.span()
    }
}

/// A node that represents table indexing.
#[derive(Clone, Debug)]
pub enum VarField<'a> {
    /// `[expr]`
    Expr {
        /// A reference to the square brackets that enclose the key.
        brackets: Brackets<'a>,
        /// The expression that evaluates to the table key.
        key: Box<Expr<'a>>,
    },
    /// `.name`
    Name {
        /// A reference to the `Symbol::Period` token.
        period: TokenReference<'a>,
        /// The key.
        key: Name<'a>,
    },
}

impl_span!(VarField, |s| match s {
    VarField::Expr { brackets, .. } => brackets.span(),
    VarField::Name { period, key } => Span::consecutive(&period, &key),
});

impl_descend!(VarField: |s, v| match s {
    VarField::Expr { ref mut key, .. } => v.visit_expr(key),
    VarField::Name { ref mut key, .. } => v.visit_name(key),
});

/// A variable.
#[derive(Clone, Debug)]
pub enum Var<'a> {
    /// A plain name (`name`).
    Name(Name<'a>),
    /// A table field (`prefixExpr.key`).
    Field(Box<PrefixExpr<'a>>, VarField<'a>),
}

impl_span!(Var, |s| match s {
    Var::Name(inner) => inner.span(),
    Var::Field(k, v) => Span::consecutive(&k, &v),
});

impl_descend!(Var: |s, v| match s {
    Var::Name(ref mut name) => v.visit_name(name),
    Var::Field(ref mut k, ref mut value) => {
        v.visit_prefix_expr(k);
        v.visit_var_field(value);
    }
});

/// A callee object.
#[derive(Clone, Debug)]
pub enum FunctionCallee<'a> {
    /// An arbitrary expression.
    Expr(Box<PrefixExpr<'a>>),
    /// A method invocation (`object:name`).
    Method {
        /// The expression to the left of the colon, used as the object.
        object: Box<PrefixExpr<'a>>,
        /// A reference to the `Symbol::Colon` token.
        colon: TokenReference<'a>,
        /// The method name.
        name: Name<'a>,
    },
}

impl_span!(FunctionCallee, |s| match s {
    FunctionCallee::Expr(inner) => inner.span(),
    FunctionCallee::Method { object, name, .. } => Span::consecutive(&object, &name),
});

impl_descend!(FunctionCallee: |s, v| match s {
    FunctionCallee::Expr(ref mut expr) => v.visit_prefix_expr(expr),
    FunctionCallee::Method { ref mut object, ref mut name, .. } => {
        v.visit_prefix_expr(object);
        v.visit_name(name);
    }
});

/// Function call arguments.
#[derive(Clone, Debug)]
pub enum FunctionArgs<'a> {
    /// A table constructor with parentheses omitted (`func {tbl}`).
    TableConstructor(TableConstructor<'a>),
    /// A string literal with parentheses omitted (`func "string"`).
    StringLit(StringLit<'a>),
    /// The usual call syntax (`func(arg1, arg2, ...)`).
    ParenthesizedList(ParenthesizedList<'a, Box<Expr<'a>>>),
}

impl_span!(FunctionArgs, |s| match s {
    FunctionArgs::TableConstructor(inner) => inner.span(),
    FunctionArgs::StringLit(inner) => inner.span(),
    FunctionArgs::ParenthesizedList(inner) => inner.span(),
});

impl_descend!(FunctionArgs: |s, v| match s {
    FunctionArgs::TableConstructor(ref mut tcons) => v.visit_table_constructor(tcons),
    FunctionArgs::StringLit(ref mut string_lit) => v.visit_string_lit(string_lit),
    FunctionArgs::ParenthesizedList(ref mut list) => for (item, _) in list.list.pairs.iter_mut() {
        v.visit_expr(item);
    }
});

/// A function call.
#[derive(Clone, Debug)]
pub struct FunctionCall<'a> {
    pub callee: FunctionCallee<'a>,
    pub args: FunctionArgs<'a>,
}

impl_span!(FunctionCall, callee, args);

impl_descend!(FunctionCall: |s, v| {
    v.visit_callee(&mut s.callee);
    v.visit_args(&mut s.args);
});

/// A prefix expression (an expression that can be used as a callee).
#[derive(Clone, Debug)]
pub enum PrefixExpr<'a> {
    Parenthesized(ParenthesizedExpr<'a>),
    Var(Var<'a>),
    Call(FunctionCall<'a>),
}

impl_span!(PrefixExpr, |s| match s {
    PrefixExpr::Parenthesized(inner) => inner.span(),
    PrefixExpr::Var(inner) => inner.span(),
    PrefixExpr::Call(inner) => inner.span(),
});

impl_descend!(PrefixExpr: |s, v| match s {
    PrefixExpr::Parenthesized(ParenthesizedExpr { ref mut expr, .. }) => v.visit_expr(expr),
    PrefixExpr::Var(ref mut var) => v.visit_var(var),
    PrefixExpr::Call(ref mut call) => v.visit_function_call(call),
});

/// An expression.
#[derive(Clone, Debug)]
pub enum Expr<'a> {
    Nil(NilLit<'a>),
    Boolean(BooleanLit<'a>),
    Number(NumberLit<'a>),
    String(StringLit<'a>),
    Vararg(Vararg<'a>),
    UnOp(UnOpExpr<'a>),
    BinOp(BinOpExpr<'a>),
    TableConstructor(TableConstructor<'a>),
    Prefix(PrefixExpr<'a>),
    Function(FunctionExpr<'a>),
}

impl_span!(Expr, |s| match s {
    Expr::Nil(inner) => inner.span(),
    Expr::Boolean(inner) => inner.span(),
    Expr::Number(inner) => inner.span(),
    Expr::String(inner) => inner.span(),
    Expr::Vararg(inner) => inner.span(),
    Expr::UnOp(inner) => inner.span(),
    Expr::BinOp(inner) => inner.span(),
    Expr::TableConstructor(inner) => inner.span(),
    Expr::Prefix(inner) => inner.span(),
    Expr::Function(inner) => inner.span(),
});

impl_descend!(Expr: |s, v| match s {
    Expr::Nil(ref mut nil_lit) => v.visit_nil_lit(nil_lit),
    Expr::Boolean(ref mut boolean_lit) => v.visit_boolean_lit(boolean_lit),
    Expr::Number(ref mut number_lit) => v.visit_number_lit(number_lit),
    Expr::String(ref mut string_lit) => v.visit_string_lit(string_lit),
    Expr::Vararg(ref mut vararg) => v.visit_vararg(vararg),
    Expr::UnOp(ref mut un_op) => v.visit_un_op(un_op),
    Expr::BinOp(ref mut bin_op) => v.visit_bin_op(bin_op),
    Expr::TableConstructor(ref mut tcons) => v.visit_table_constructor(tcons),
    Expr::Prefix(ref mut prefix_expr) => v.visit_prefix_expr(prefix_expr),
    Expr::Function(ref mut function) => v.visit_function_expr(function),
});

/// A table constructor.
#[derive(Clone, Debug)]
pub struct TableConstructor<'a> {
    /// The braces (`{}`).
    pub brackets: Brackets<'a>,
    pub fields: Vec<TableField<'a>>,
}

impl_span!(TableConstructor, brackets);

impl_descend!(TableConstructor: |s, v| for item in s.fields.iter_mut() {
    v.visit_table_field(item);
});

/// A table key.
#[derive(Clone, Debug)]
pub enum TableKey<'a> {
    /// An arbitrary expression enclosed in square brackets.
    Expr {
        brackets: Brackets<'a>,
        key: Box<Expr<'a>>,
    },
    /// A plain name.
    Name {
        key: Name<'a>,
    },
}

impl_span!(TableKey, |s| match s {
    TableKey::Expr { brackets, .. } => brackets.span(),
    TableKey::Name { key } => key.span(),
});

impl_descend!(TableKey: |s, v| match s {
    TableKey::Expr { ref mut key, .. } => v.visit_expr(key),
    TableKey::Name { ref mut key } => v.visit_name(key),
});

/// A table field.
#[derive(Clone, Debug)]
pub struct TableField<'a> {
    /// An explicit table key and a reference to the `Symbol::Assign` token.
    pub key: Option<(TableKey<'a>, TokenReference<'a>)>,
    /// The value stored in the table.
    pub value: Box<Expr<'a>>,
    /// A `Symbol::Comma` or `Symbol::Semicolon` that may follow the field.
    pub separator: Option<TokenReference<'a>>,
}

impl_span!(TableField, |s| {
    let start = s.key.as_ref().map(|v| v.0.span()).unwrap_or_else(|| s.value.span());
    let end = s
        .separator
        .as_ref()
        .map(|v| v.span())
        .unwrap_or_else(|| s.value.span());
    Span::consecutive(start, end)
});

impl_descend!(TableField: |s, v| {
    if let Some((ref mut key, _)) = s.key {
        v.visit_table_key(key);
    }

    v.visit_expr(&mut s.value);
});

/// A block, which is a list of statements.
#[derive(Clone, Debug)]
pub struct Block<'a> {
    pub statements: Vec<Statement<'a>>,
}

impl_span!(Block, |s| {
    if s.statements.is_empty() {
        return Span::default();
    }
    Span::consecutive(&s.statements[0], s.statements.last().unwrap())
});

impl_descend!(Block: |s, v| for stmt in s.statements.iter_mut() {
    v.visit_stmt(stmt);
});

/// A statement.
#[derive(Clone, Debug)]
pub enum Statement<'a> {
    Empty(EmptyStat<'a>),
    Block(BlockStat<'a>),
    Return(ReturnStat<'a>),
    Break(BreakStat<'a>),
    Assignment(AssignmentStat<'a>),
    FunctionCall(FunctionCall<'a>),
    While(WhileStat<'a>),
    For(ForStat<'a>),
    Repeat(RepeatStat<'a>),
    LocalDeclaration(LocalDeclarationStat<'a>),
    FunctionDeclaration(FunctionDeclarationStat<'a>),
    Label(LabelStat<'a>),
    Goto(GotoStat<'a>),
    If(IfStat<'a>),
}

impl_span!(Statement, |s| match s {
    Statement::Empty(inner) => inner.span(),
    Statement::Block(inner) => inner.span(),
    Statement::Return(inner) => inner.span(),
    Statement::Break(inner) => inner.span(),
    Statement::Assignment(inner) => inner.span(),
    Statement::FunctionCall(inner) => inner.span(),
    Statement::While(inner) => inner.span(),
    Statement::For(inner) => inner.span(),
    Statement::Repeat(inner) => inner.span(),
    Statement::LocalDeclaration(inner) => inner.span(),
    Statement::FunctionDeclaration(inner) => inner.span(),
    Statement::Label(inner) => inner.span(),
    Statement::Goto(inner) => inner.span(),
    Statement::If(inner) => inner.span(),
});

impl_descend!(Statement: |s, v| match s {
    Statement::Empty(ref mut stmt) => v.visit_empty_stmt(stmt),
    Statement::Block(BlockStat { ref mut block, .. }) => v.visit_block(block),
    Statement::Return(ref mut stmt) => v.visit_return_stmt(stmt),
    Statement::Break(ref mut stmt) => v.visit_break_stmt(stmt),
    Statement::Assignment(ref mut stmt) => v.visit_assignment_stmt(stmt),
    Statement::FunctionCall(ref mut stmt) => v.visit_function_call(stmt),
    Statement::While(ref mut stmt) => v.visit_while_stmt(stmt),
    Statement::For(ref mut stmt) => v.visit_for_stmt(stmt),
    Statement::Repeat(ref mut stmt) => v.visit_repeat_stmt(stmt),
    Statement::LocalDeclaration(ref mut stmt) => v.visit_local_decl(stmt),
    Statement::FunctionDeclaration(ref mut stmt) => v.visit_function_decl(stmt),
    Statement::Label(ref mut stmt) => v.visit_label_stmt(stmt),
    Statement::Goto(ref mut stmt) => v.visit_goto_stmt(stmt),
    Statement::If(ref mut stmt) => v.visit_if_stmt(stmt),
});

/// A no-op statement `;`.
#[derive(Clone, Debug)]
pub struct EmptyStat<'a>(
    /// A reference to the `Symbol::Semicolon` token.
    pub TokenReference<'a>
);
impl_span!(EmptyStat);

/// A block statement (`do ... end`).
#[derive(Clone, Debug)]
pub struct BlockStat<'a> {
    /// A reference to the `Symbol::Do` token.
    pub do_: TokenReference<'a>,
    /// The contents of the block.
    pub block: Box<Block<'a>>,
    /// A reference to the `Symbol::End` token.
    pub end: TokenReference<'a>,
}

impl_span!(BlockStat, do_, end);

/// A return statement.
#[derive(Clone, Debug)]
pub struct ReturnStat<'a> {
    /// A reference to the `Symbol::Return` token.
    pub return_: TokenReference<'a>,
    /// A list of returned expressions.
    pub exprs: Punctuated<'a, Box<Expr<'a>>>,
    /// The semicolon that may follow the return statement.
    pub semi: Option<TokenReference<'a>>,
}

impl_span!(ReturnStat, |s| {
    let end = s.semi.as_ref().map(|v| v.span()).unwrap_or_else(|| s.exprs.span());
    Span::consecutive(&s.return_, end)
});

impl_descend!(ReturnStat: |s, v| for (item, _) in s.exprs.pairs.iter_mut() {
    v.visit_expr(item);
});

/// A break statement.
#[derive(Clone, Debug)]
pub struct BreakStat<'a>(
    /// A reference to the `Symbol::Break` token.
    pub TokenReference<'a>
);
impl_span!(BreakStat);

/// An assignment statement.
#[derive(Clone, Debug)]
pub struct AssignmentStat<'a> {
    /// The variables to assign to.
    pub vars: Punctuated<'a, Var<'a>>,
    /// A reference to the `Symbol::Assign` token.
    pub assign: TokenReference<'a>,
    /// The expressions to assign.
    pub exprs: Punctuated<'a, Box<Expr<'a>>>,
}

impl_span!(AssignmentStat, vars, exprs);

impl_descend!(AssignmentStat: |s, v| {
    for (item, _) in &mut s.vars.pairs {
        v.visit_var(item);
    }

    for (item, _) in &mut s.exprs.pairs {
        v.visit_expr(item);
    }
});

/// A while statement.
#[derive(Clone, Debug)]
pub struct WhileStat<'a> {
    /// A reference to the `Symbol::While` token.
    pub while_: TokenReference<'a>,
    /// The loop condition.
    pub condition: Box<Expr<'a>>,
    /// A reference to the `Symbol::Do` token.
    pub do_: TokenReference<'a>,
    /// The loop body.
    pub block: Box<Block<'a>>,
    /// A reference to the `Symbol::End` token.
    pub end: TokenReference<'a>,
}

impl_span!(WhileStat, while_, end);

impl_descend!(WhileStat: |s, v| {
    v.visit_expr(&mut s.condition);
    v.visit_block(&mut s.block);
});

/// A for statement.
#[derive(Clone, Debug)]
pub enum ForStat<'a> {
    Generic(GenericFor<'a>),
    Numerical(NumericalFor<'a>),
}

impl_span!(ForStat, |s| match s {
    ForStat::Generic(inner) => inner.span(),
    ForStat::Numerical(inner) => inner.span(),
});

impl_descend!(ForStat: |s, v| match s {
    ForStat::Generic(ref mut inner) => v.visit_generic_for(inner),
    ForStat::Numerical(ref mut inner) => v.visit_numerical_for(inner),
});

/// A generic for loop:
///
/// ```lua
/// for a, b in iter, state, init do
///   ...
/// end
/// ```
#[derive(Clone, Debug)]
pub struct GenericFor<'a> {
    /// A reference to the `Symbol::For` token.
    pub for_: TokenReference<'a>,
    /// The loop variable names.
    pub names: Punctuated<'a, Name<'a>>,
    /// A reference to the `Symbol::In` token.
    pub in_: TokenReference<'a>,
    /// The iterator expressions.
    pub exprs: Punctuated<'a, Box<Expr<'a>>>,
    /// A reference to the `Symbol::Do` token.
    pub do_: TokenReference<'a>,
    /// The loop body.
    pub block: Box<Block<'a>>,
    /// A reference to the `Symbol::End` token.
    pub end: TokenReference<'a>,
}

impl_span!(GenericFor, for_, end);

impl_descend!(GenericFor: |s, v| {
    for (item, _) in &mut s.names.pairs {
        v.visit_name(item);
    }

    for (item, _) in &mut s.exprs.pairs {
        v.visit_expr(item);
    }

    v.visit_block(&mut s.block);
});

/// A numerical for loop:
///
/// ```lua
/// for name = from, to, step do
///   ...
/// end
/// ```
#[derive(Clone, Debug)]
pub struct NumericalFor<'a> {
    /// A reference to the `Symbol::For` token.
    pub for_: TokenReference<'a>,
    /// The loop variable name.
    pub name: Name<'a>,
    /// A reference to the `Symbol::Assign` token.
    pub assign: TokenReference<'a>,
    /// The initial value.
    pub from: Box<Expr<'a>>,
    /// A reference to the `Symbol::Comma` token that follows the initial value expression.
    pub comma: TokenReference<'a>,
    /// The end value.
    pub to: Box<Expr<'a>>,
    /// A reference to the `Symbol::Comma` token that follows the end value expression, as well as
    /// the expression that evaluates to the increment step.
    pub step: Option<(TokenReference<'a>, Box<Expr<'a>>)>,
    /// A reference to the `Symbol::Do` token.
    pub do_: TokenReference<'a>,
    /// The body loop.
    pub block: Box<Block<'a>>,
    /// A reference to the `Symbol::End` token.
    pub end: TokenReference<'a>,
}

impl_span!(NumericalFor, for_, end);

impl_descend!(NumericalFor: |s, v| {
    v.visit_name(&mut s.name);
    v.visit_expr(&mut s.from);
    v.visit_expr(&mut s.to);

    if let Some((_, ref mut expr)) = s.step {
        v.visit_expr(expr);
    }

    v.visit_block(&mut s.block);
});

/// A repeat-until loop statement.
#[derive(Clone, Debug)]
pub struct RepeatStat<'a> {
    /// A reference to the `Symbol::Repeat` token.
    pub repeat: TokenReference<'a>,
    /// The loop body.
    pub block: Box<Block<'a>>,
    /// A reference to the `Symbol::Until` token.
    pub until: TokenReference<'a>,
    /// The loop termination condition.
    pub condition: Expr<'a>,
}

impl_span!(RepeatStat, repeat, condition);

impl_descend!(RepeatStat: |s, v| {
    v.visit_block(&mut s.block);
    v.visit_expr(&mut s.condition);
});

/// A local variable declaration statement.
#[derive(Clone, Debug)]
pub struct LocalDeclarationStat<'a> {
    /// A reference to the `Symbol::Local` token.
    pub local: TokenReference<'a>,
    /// The names of the declared local variables.
    pub names: Punctuated<'a, Name<'a>>,
    /// The values the variables are initialized with.
    pub definition: Option<LocalDefinition<'a>>,
}

impl_span!(LocalDeclarationStat, |s| {
    let end = s
        .definition
        .as_ref()
        .map(|v| v.span())
        .unwrap_or_else(|| s.names.span());
    Span::consecutive(&s.local, end)
});

impl_descend!(LocalDeclarationStat: |s, v| {
    for (item, _) in &mut s.names.pairs {
        v.visit_name(item);
    }

    if let Some(ref mut def) = s.definition {
        for (item, _) in &mut def.exprs.pairs {
            v.visit_expr(item);
        }
    }
});

/// A local variable initialization.
#[derive(Clone, Debug)]
pub struct LocalDefinition<'a> {
    /// A reference to the `Symbol::Assign` token.
    pub assign: TokenReference<'a>,
    /// A punctuated list of initializer expressions.
    pub exprs: Punctuated<'a, Box<Expr<'a>>>,
}

impl_span!(LocalDefinition, assign, exprs);

/// A function declaration statement.
#[derive(Clone, Debug)]
pub enum FunctionDeclarationStat<'a> {
    /// A local function declaration.
    Local {
        /// A reference to the `Symbol::Local` token.
        local: TokenReference<'a>,
        /// A reference to the `Symbol::Function` token.
        function: TokenReference<'a>,
        /// The function name.
        name: Name<'a>,
        /// The function body.
        body: FunctionBody<'a>,
    },
    /// A non-local function declaration.
    Nonlocal {
        /// A reference to the `Symbol::Function` token.
        function: TokenReference<'a>,
        /// The function name.
        name: FunctionName<'a>,
        /// The function body.
        body: FunctionBody<'a>,
    },
}

impl_span!(FunctionDeclarationStat, |s| match s {
    FunctionDeclarationStat::Local { local, body, .. } => Span::consecutive(&local, &body),
    FunctionDeclarationStat::Nonlocal { function, body, .. } => Span::consecutive(&function, &body),
});

impl_descend!(FunctionDeclarationStat: |s, v| match s {
    FunctionDeclarationStat::Local { ref mut name, ref mut body, .. } => {
        v.visit_name(name);
        v.visit_function_body(body);
    }
    FunctionDeclarationStat::Nonlocal { ref mut name, ref mut body, .. } => {
        name.descend_mut(v);
        v.visit_function_body(body);
    }
});

/// A function body.
#[derive(Clone, Debug)]
pub struct FunctionBody<'a> {
    /// A parenthesized list of function parameters, excluding the vararg specifier (`...`).
    ///
    /// If the function has a vararg specifier, the comma that precedes it will be stored in the
    /// last name-token pair.
    pub params: ParenthesizedList<'a, Name<'a>>,
    /// The vararg specifier.
    pub vararg: Option<Vararg<'a>>,
    /// The actual contents of the function.
    pub block: Box<Block<'a>>,
    /// A reference to the `Symbol::End` token.
    pub end: TokenReference<'a>,
}

impl_span!(FunctionBody, params, end);

impl_descend!(FunctionBody: |s, v| {
    for (item, _) in &mut s.params.list.pairs {
        v.visit_name(item);
    }

    if let Some(ref mut vararg) = s.vararg {
        v.visit_vararg(vararg);
    }

    v.visit_block(&mut s.block);
});

/// A non-local function name.
#[derive(Clone, Debug)]
pub enum FunctionName<'a> {
    /// A plain name (`function name(...) ... end`).
    PlainName(Name<'a>),
    /// A table field (`function tbl.field.subfield(...) ... end`).
    Indexed(Punctuated<'a, Name<'a>>),
    /// A table method (`function tbl.field.subfield:method(...) ... end`).
    Method {
        /// The method receiver (the value preceding the colon).
        receiver: Punctuated<'a, Name<'a>>,
        /// A reference to the `Symbol::Colon` token.
        colon: TokenReference<'a>,
        /// The method name.
        method: Name<'a>,
    },
}

impl_descend!(FunctionName: |s, v| match s {
    FunctionName::PlainName(ref mut name) => v.visit_name(name),
    FunctionName::Indexed(ref mut p) => for (item, _) in &mut p.pairs {
        v.visit_name(item);
    }
    FunctionName::Method { ref mut receiver, ref mut method, .. } => {
        for (item, _) in &mut receiver.pairs {
            v.visit_name(item);
        }

        v.visit_name(method);
    }
});

/// An anonymous function expression.
#[derive(Clone, Debug)]
pub struct FunctionExpr<'a> {
    /// A reference to the `Symbol::Function` token`.
    pub function: TokenReference<'a>,
    /// The function body.
    pub body: FunctionBody<'a>,
}

impl_span!(FunctionExpr, function, body);
impl_descend!(FunctionExpr: |s, v| v.visit_function_body(&mut s.body));

/// A label statement.
#[derive(Clone, Debug)]
pub struct LabelStat<'a> {
    /// A reference to the preceding `Symbol::DoubleColon` token.
    pub preceding: TokenReference<'a>,
    /// The label name.
    pub name: Name<'a>,
    /// A reference to the following `Symbol::DoubleColon` token.
    pub following: TokenReference<'a>,
}

impl_span!(LabelStat, preceding, following);
impl_descend!(LabelStat: |s, v| v.visit_name(&mut s.name));

/// A goto statement.
#[derive(Clone, Debug)]
pub struct GotoStat<'a> {
    /// A reference to the `Symbol::Goto` token.
    pub goto: TokenReference<'a>,
    /// The label name.
    pub label: Name<'a>,
}

impl_span!(GotoStat, goto, label);
impl_descend!(GotoStat: |s, v| v.visit_name(&mut s.label));

/// An if statement.
#[derive(Clone, Debug)]
pub struct IfStat<'a> {
    /// A reference to the `Symbol::If` token.
    pub if_: TokenReference<'a>,
    /// The condition expression.
    pub condition: Expr<'a>,
    /// A reference to the `Symbol::Then` token.
    pub then: TokenReference<'a>,
    /// The body of the main branch of the conditional.
    pub block: Box<Block<'a>>,
    /// A list of `elseif` branches.
    pub elseifs: Vec<ElseIf<'a>>,
    /// An `else` branch.
    pub else_: Option<Else<'a>>,
    /// A reference to the `Symbol::End` token.
    pub end: TokenReference<'a>,
}

impl_span!(IfStat, if_, end);

impl_descend!(IfStat: |s, v| {
    v.visit_expr(&mut s.condition);
    v.visit_block(&mut s.block);

    for else_if in &mut s.elseifs {
        else_if.descend_mut(v);
    }

    if let Some(ref mut else_) = s.else_ {
        else_.descend_mut(v);
    }
});

/// An `elseif` branch.
#[derive(Clone, Debug)]
pub struct ElseIf<'a> {
    /// A reference to the `Symbol::ElseIf` token.
    pub elseif: TokenReference<'a>,
    /// The branch condition.
    pub condition: Expr<'a>,
    /// A reference to the `Symbol::Then` token.
    pub then: TokenReference<'a>,
    /// The body of the branch.
    pub block: Block<'a>,
}

impl_span!(ElseIf, elseif, block);

impl_descend!(ElseIf: |s, v| {
    v.visit_expr(&mut s.condition);
    v.visit_block(&mut s.block);
});

/// An `else` branch.
#[derive(Clone, Debug)]
pub struct Else<'a> {
    /// A reference to the `Symbol::Else` token.
    pub else_: TokenReference<'a>,
    /// The body of the branch.
    pub block: Block<'a>,
}

impl_span!(Else, else_, block);

impl_descend!(Else: |s, v| v.visit_block(&mut s.block));