luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::*;
use crate::ast::AstString;

impl<'cst, 'ast> Printer<'cst, 'ast> {
    pub(super) fn write_expression(&mut self, expression: Expression) {
        self.advance(expression.location.begin);
        let cst_nodes = self.cst_nodes;

        match expression.kind() {
            ExpressionKind::Nil => self.keyword("nil"),
            ExpressionKind::Boolean(value) => self.keyword(if value { "true" } else { "false" }),
            ExpressionKind::Integer { value, .. } => {
                let source: Option<AstString<'ast>> = match expression_cst(cst_nodes, expression) {
                    Some(CstNode::ExprConstantInteger(CstExprConstantInteger {
                        value: source,
                    })) => Some(*source),
                    _ => None,
                };
                if let Some(source) = source {
                    self.write_bytes(source.as_bytes());
                } else {
                    self.write_bytes(value.to_string().as_bytes());
                }
            }
            ExpressionKind::Number { value, .. } => {
                let source: Option<AstString<'ast>> = match expression_cst(cst_nodes, expression) {
                    Some(CstNode::ExprConstantNumber(CstExprConstantNumber { value: source })) => {
                        Some(*source)
                    }
                    _ => None,
                };
                if let Some(source) = source {
                    self.write_bytes(source.as_bytes());
                } else if value.is_infinite() {
                    self.write_bytes(if value > 0.0 { b"1e500" } else { b"-1e500" });
                } else if value.is_nan() {
                    self.write_bytes(b"0/0");
                } else if value.fract() == 0.0
                    && value >= i32::MIN as f64
                    && value <= i32::MAX as f64
                {
                    self.write_bytes((value as i32).to_string().as_bytes());
                } else {
                    self.write_bytes(value.to_string().as_bytes());
                }
            }
            ExpressionKind::String {
                value,
                quote_style: _,
            } => {
                let string_cst: Option<(AstString<'ast>, CstStringQuoteStyle, u32)> =
                    match expression_cst(cst_nodes, expression) {
                        Some(CstNode::ExprConstantString(CstExprConstantString {
                            source_string,
                            quote_style,
                            block_depth,
                            ..
                        })) => Some((*source_string, *quote_style, *block_depth)),
                        _ => None,
                    };
                if let Some((source_string, quote_style, block_depth)) = string_cst {
                    self.write_source_string(source_string.as_bytes(), quote_style, block_depth);
                } else {
                    self.write_string(value.as_bytes());
                }
            }
            ExpressionKind::Local { local, .. } => self.identifier(local.name.bytes()),
            ExpressionKind::Global(name) => self.identifier(name.bytes()),
            ExpressionKind::Varargs => self.symbol("..."),
            ExpressionKind::Grouped(inner) => {
                self.symbol("(");
                self.write_expression(inner);
                let group_cst = match expression_cst(cst_nodes, expression) {
                    Some(CstNode::ExprGroup(CstExprGroup { close_position })) => {
                        Some(*close_position)
                    }
                    _ => None,
                };
                if let Some(close_position) = group_cst {
                    self.maybe_advance_and_write(Some(close_position), ")", false);
                } else {
                    self.advance_before(expression.location.end, 1);
                    self.symbol(")");
                }
            }
            ExpressionKind::Call {
                func,
                type_args,
                args,
                ..
            } => {
                let call_cst =
                    if let Some(CstNode::ExprCall(cst)) = expression_cst(cst_nodes, expression) {
                        Some(cst)
                    } else {
                        None
                    };
                self.write_expression(func);
                if self.write_types && !type_args.is_empty() {
                    self.write_type_instantiation(
                        type_args,
                        call_cst
                            .as_ref()
                            .and_then(|cst| cst.explicit_types.as_ref()),
                    );
                }
                self.maybe_advance_and_write(
                    call_cst.as_ref().and_then(|cst| cst.open_parens),
                    "(",
                    call_cst.is_none(),
                );
                self.write_expression_list_with_commas(
                    args,
                    call_cst.as_ref().map(|cst| cst.comma_positions.as_slice()),
                );
                self.maybe_advance_and_write(
                    call_cst.as_ref().and_then(|cst| cst.close_parens),
                    ")",
                    call_cst.is_none(),
                );
            }
            ExpressionKind::IndexName {
                expr,
                index,
                index_location,
                op_position,
                op,
            } => {
                self.write_expression(expr);
                self.advance(op_position);
                self.symbol(op.symbol());
                self.advance(index_location.begin);
                self.identifier(index.bytes());
            }
            ExpressionKind::IndexExpr { expr, index } => {
                let index_cst = if let Some(CstNode::ExprIndexExpr(CstExprIndexExpr {
                    open_bracket,
                    close_bracket,
                })) = expression_cst(cst_nodes, expression)
                {
                    Some((*open_bracket, *close_bracket))
                } else {
                    None
                };
                self.write_expression(expr);
                if let Some((open, _)) = index_cst {
                    self.maybe_advance_and_write(Some(open), "[", false);
                } else {
                    self.advance(Position::new(
                        index.location.begin.line,
                        index.location.begin.column.saturating_sub(1),
                    ));
                    self.symbol("[");
                }
                self.write_expression(index);
                if let Some((_, close)) = index_cst {
                    self.maybe_advance_and_write(Some(close), "]", false);
                } else {
                    self.symbol("]");
                }
            }
            ExpressionKind::If {
                condition,
                then_expression,
                else_expression,
                ..
            } => {
                self.keyword("if");
                let cst = expression_cst(cst_nodes, expression).and_then(|node| match node {
                    CstNode::ExprIfElse(cst) => Some(cst),
                    _ => None,
                });
                self.write_else_if_expression(condition, then_expression, else_expression, cst);
            }
            ExpressionKind::Unary { op, rhs } => {
                self.symbol(op.symbol());
                self.write_expression(rhs);
            }
            ExpressionKind::Binary { lhs, op, rhs } => {
                self.write_expression(lhs);
                if let Some(CstNode::ExprOp(cst)) = expression_cst(cst_nodes, expression) {
                    self.advance(cst.op);
                } else {
                    self.maybe_space(rhs.location.begin, op.symbol().len() as u32);
                }
                self.symbol(op.symbol());
                self.write_expression(rhs);
            }
            ExpressionKind::TypeAssertion { expr, annotation } => {
                self.write_expression(expr);
                if self.write_types {
                    if let Some(CstNode::ExprTypeAssertion(CstExprTypeAssertion { op })) =
                        expression_cst(cst_nodes, expression)
                    {
                        self.advance(*op);
                    } else {
                        self.maybe_space(annotation.location.begin, 2);
                    }
                    self.symbol("::");
                    self.write_type(annotation);
                }
            }
            ExpressionKind::Error { expressions, .. } => {
                self.symbol("(error-expr");
                for (index, expression) in expressions.iter().enumerate() {
                    self.symbol(if index == 0 { ": " } else { ", " });
                    self.write_expression(*expression);
                }
                self.symbol(")");
            }
            ExpressionKind::Table { items } => {
                self.symbol("{");
                let table_cst = match expression_cst(cst_nodes, expression) {
                    Some(CstNode::ExprTable(cst)) => Some(cst),
                    _ => None,
                };
                if let Some(cst) = table_cst {
                    for (index, item) in items.iter().enumerate() {
                        let cst_item = cst.items.get(index);
                        match item {
                            TableItem::List { value } => self.write_expression(*value),
                            TableItem::Record { key, value } => {
                                self.write_table_record_key(*key);
                                if let Some(position) = cst_item.and_then(|item| item.equals) {
                                    self.advance(position);
                                } else {
                                    self.maybe_space(value.location.begin, 1);
                                }
                                self.symbol("=");
                                self.write_expression(*value);
                            }
                            TableItem::General { key, value } => {
                                self.maybe_advance_and_write(
                                    cst_item.and_then(|item| item.indexer_open),
                                    "[",
                                    true,
                                );
                                self.write_expression(*key);
                                self.maybe_advance_and_write(
                                    cst_item.and_then(|item| item.indexer_close),
                                    "]",
                                    false,
                                );
                                self.maybe_advance_and_write(
                                    cst_item.and_then(|item| item.equals),
                                    "=",
                                    false,
                                );
                                self.write_expression(*value);
                            }
                        }

                        if let Some(cst_item) = cst_item
                            && let Some(separator) = cst_item.separator
                        {
                            if let Some(position) = cst_item.separator_position {
                                self.advance(position);
                            }
                            self.symbol(match separator {
                                TableSeparator::Comma => ",",
                                TableSeparator::Semicolon => ";",
                            });
                        }
                    }

                    self.advance_before(expression.location.end, 1);
                    self.symbol("}");
                } else {
                    for (index, item) in items.iter().enumerate() {
                        if index > 0 {
                            self.symbol(",");
                        }
                        match item {
                            TableItem::List { value } => self.write_expression(*value),
                            TableItem::Record { key, value } => {
                                self.write_table_record_key(*key);
                                self.maybe_space(value.location.begin, 1);
                                self.symbol("=");
                                self.write_expression(*value);
                            }
                            TableItem::General { key, value } => {
                                self.symbol("[");
                                self.write_expression(*key);
                                self.symbol("]=");
                                self.write_expression(*value);
                            }
                        }
                    }
                    self.maybe_space(expression.location.end, 1);
                    self.symbol("}");
                }
            }
            ExpressionKind::FunctionLiteral(function) => {
                let function_cst = function_cst(cst_nodes, function);
                let attr_lists = match function_cst {
                    Some(CstNode::ExprFunction(cst)) => Some(cst.attr_lists.as_slice()),
                    _ => None,
                };
                self.write_attributes(function.attributes, attr_lists);
                if let Some(CstNode::ExprFunction(cst)) = function_cst
                    && cst.function_keyword.has_value()
                {
                    self.advance(cst.function_keyword);
                }
                self.keyword("function");
                self.write_function_body(function);
            }
            ExpressionKind::InterpString {
                strings,
                expressions,
            } => {
                let interp_cst = match expression_cst(cst_nodes, expression) {
                    Some(CstNode::ExprInterpString(cst)) => Some(cst),
                    _ => None,
                };
                if let Some(cst) = interp_cst {
                    self.symbol("`");
                    for (index, string) in strings.iter().enumerate() {
                        if index > 0 {
                            if let Some(position) = cst.string_positions.get(index) {
                                self.advance(*position);
                            }
                            self.symbol("}");
                        }

                        if let Some(source_string) = cst.source_strings.get(index) {
                            self.write_source_string_content(source_string.as_bytes());
                        } else {
                            self.write_multiline(string.as_bytes());
                        }

                        if let Some(expression) = expressions.get(index) {
                            self.symbol("{");
                            self.write_expression(*expression);
                        }
                    }
                    self.symbol("`");
                    return;
                }

                self.symbol("`");
                for (index, string) in strings.iter().enumerate() {
                    if index > 0 {
                        self.symbol("}");
                    }
                    self.write_multiline(string.as_bytes());
                    if let Some(expression) = expressions.get(index) {
                        self.symbol("{");
                        self.write_expression(*expression);
                    }
                }
                self.symbol("`");
            }
            ExpressionKind::Instantiate { expr, type_args } => {
                self.write_expression(expr);
                if self.write_types {
                    let instantiate_cst = if let Some(CstNode::ExprExplicitTypeInstantiation(
                        CstExprExplicitTypeInstantiation { instantiation },
                    )) = expression_cst(cst_nodes, expression)
                    {
                        Some(instantiation)
                    } else {
                        None
                    };
                    self.write_type_instantiation(type_args, instantiate_cst);
                }
            }
        }
    }
}