///|
fn Operator::text(operator : Operator) -> String {
match operator {
Assignment => "="
Add => "+"
Subtract => "-"
Multiply => "*"
Divide => "/"
Modulo => "%"
Exponent => "**"
ShiftLeft => "<<"
ShiftRight => ">>"
UnsignedShiftRight => ">>>"
LessThan => "<"
GreaterThan => ">"
LessThanOrEqual => "<="
GreaterThanOrEqual => ">="
Equal => "=="
NotEqual => "!="
StrictlyEqual => "==="
StrictlyNotEqual => "!=="
BitwiseAnd => "&"
BitwiseOr => "|"
BitwiseXor => "^"
LogicalAnd => "&&"
LogicalOr => "||"
NullishCoalescing => "??"
In => "in"
InstanceOf => "instanceof"
LogicalNot => "!"
BitwiseNot => "~"
Plus => "+"
Minus => "-"
TypeOf => "typeof"
Void => "void"
Delete => "delete"
Await => "await"
Yield => "yield"
YieldStar => "yield*"
Increment => "++"
Decrement => "--"
}
}
///|
fn Operator::is_word(operator : Operator) -> Bool {
match operator {
In | InstanceOf | TypeOf | Void | Delete | Await | Yield | YieldStar => true
_ => false
}
}
///|
priv struct Printer {
program : Program
output : StringBuilder
mut previous_is_word : Bool
mut last_character : UInt16
mut pending_semicolon : Bool
mut in_for_initializer : Bool
labels : LabelNames
decimal_spellings : Map[StringView, String?]
}
///|
fn Printer::new(program : Program) -> Printer {
{
program,
output: StringBuilder(),
previous_is_word: false,
last_character: 0,
pending_semicolon: false,
in_for_initializer: false,
labels: LabelNames::new(program.alphabet),
decimal_spellings: {},
}
}
///|
fn Printer::flush_semicolon(self : Printer) -> Unit {
guard self.pending_semicolon else { return }
self.output.write_string(";")
self.previous_is_word = false
self.last_character = ';'
self.pending_semicolon = false
}
///|
fn Printer::needs_separator(
self : Printer,
first_character : UInt16,
is_word : Bool,
) -> Bool {
(self.previous_is_word && is_word) ||
(self.last_character == '+' && first_character == '+') ||
(self.last_character == '-' && first_character == '-') ||
(self.last_character == '!' && first_character == '-') ||
(
self.last_character == '/' &&
(first_character == '/' || first_character == '*')
)
}
///|
fn Printer::text(self : Printer, text : String, is_word : Bool) -> Unit {
guard text.length() > 0 else { return }
self.flush_semicolon()
if self.needs_separator(text[0], is_word) {
self.output.write_string(" ")
}
self.output.write_string(text)
self.last_character = text[text.length() - 1]
self.previous_is_word = is_word
}
///|
fn Printer::span(
self : Printer,
start : Int32,
end : Int32,
is_word : Bool,
) -> Unit {
guard end > start else { return }
self.flush_semicolon()
let source = self.program.source
if self.needs_separator(source[start], is_word) {
self.output.write_string(" ")
}
self.output.write_substring(source, start, end - start)
self.last_character = source[end - 1]
self.previous_is_word = is_word
}
///|
fn Printer::reference(self : Printer, id : Int32) -> Unit {
let reference = self.program.references[id]
let slot = if reference.symbol >= 0 {
self.program.symbols[reference.symbol].name_slot
} else {
-1
}
guard slot >= 0 else {
self.span(reference.name_start, reference.name_end, true)
return
}
self.text(self.program.generated_names[slot], true)
}
///|
fn Printer::statements(self : Printer, offset : Int32, count : Int32) -> Unit {
let mut previous_declaration_kind = -1
for i in 0..= 0 &&
!(self.program.nodes[node.c].kind is FunctionDeclaration) &&
self.program.always_exits(node.b) {
self.if_statement(node.a, node.b, -1)
self.statement_body(node.c, false)
} else if node.kind is If &&
node.c >= 0 &&
!(self.program.nodes[node.b].kind is FunctionDeclaration) &&
self.program.always_exits(node.c) {
self.if_statement(self.program.negated_value(node.a), node.c, -1)
self.statement_body(node.b, false)
} else {
self.statement(id)
}
previous_declaration_kind = -1
}
}
}
///|
fn Program::can_flatten_block(self : Program, id : Int32) -> Bool {
let block = self.nodes[id]
for index in 0.. return false
FunctionDeclaration | ClassDeclaration => return false
ExpressionStatement if index == 0 &&
self.nodes[statement.a].kind is StringLiteral => return false
_ => ()
}
}
true
}
///|
fn Printer::expressions(self : Printer, offset : Int32, count : Int32) -> Unit {
for i in 0.. 0 {
self.text(",", false)
}
let argument = self.program.edges[offset + i]
if self.program.nodes[argument].kind is Parenthesized {
let value = self.program.unwrapped_expression(argument)
if !(self.program.nodes[value].kind is Sequence) {
self.expression(value)
continue
}
}
self.expression(argument)
}
}
///|
fn Printer::pattern(self : Printer, id : Int32) -> Unit {
let node = self.program.nodes[id]
match node.kind {
PatternIdentifier | Identifier => self.reference(node.a)
Member | Index => self.expression(id)
PatternArray => self.array_pattern(node.a, node.b)
PatternObject => self.object_pattern(node.a, node.b)
PatternProperty => self.pattern_property(node)
PatternRest => {
self.text("...", false)
self.pattern(node.a)
}
PatternDefault => {
self.pattern(node.a)
self.text("=", false)
self.expression(node.b)
}
_ => abort("bad pattern: \{node.kind.to_repr()}")
}
}
///|
fn Printer::array_pattern(
self : Printer,
offset : Int32,
count : Int32,
) -> Unit {
self.text("[", false)
for index in 0.. 0 {
self.text(",", false)
}
let element = self.program.edges[offset + index]
guard element >= 0 else { continue }
self.pattern(element)
}
self.array_end(offset, count)
}
///|
fn Printer::array_end(self : Printer, offset : Int32, count : Int32) -> Unit {
let ends_with_hole = count > 0 && self.program.edges[offset + count - 1] < 0
if ends_with_hole {
self.text(",", false)
}
self.text("]", false)
}
///|
fn Printer::object_pattern(
self : Printer,
offset : Int32,
count : Int32,
) -> Unit {
self.text("{", false)
for index in 0.. 0 {
self.text(",", false)
}
self.pattern(self.program.edges[offset + index])
}
self.text("}", false)
}
///|
fn Printer::property_key(
self : Printer,
key : Int32,
is_computed : Bool,
) -> Unit {
guard is_computed else {
self.pattern_key(key)
return
}
self.text("[", false)
self.property_expression(key)
self.text("]", false)
}
///|
fn Printer::pattern_property(self : Printer, property : Node) -> Unit {
let key = property.a
let value = property.b
self.property_key(key, property.d == PROPERTY_COMPUTED)
guard property.c == PROPERTY_SHORTHAND else {
self.text(":", false)
self.pattern(value)
return
}
let value_node = self.program.nodes[value]
let (binding, default_value) = if value_node.kind == PatternDefault {
(value_node.a, value_node.b)
} else {
(value, -1)
}
let binding_node = self.program.nodes[binding]
let symbol = if binding_node.kind == Identifier ||
binding_node.kind == PatternIdentifier {
self.program.references[binding_node.a].symbol
} else {
-1
}
let needs_expansion = self.is_renamed(symbol) ||
self.program.property_names.contains(key)
if needs_expansion {
self.text(":", false)
self.pattern(binding)
}
if default_value >= 0 {
self.text("=", false)
self.expression(default_value)
}
}
///|
fn Printer::pattern_key(self : Printer, id : Int32) -> Unit {
guard self.program.property_names.get(id) is Some(name) else {
let node = self.program.nodes[id]
match node.kind {
StringLiteral => self.string(node.a, node.b)
NumberLiteral => self.number(node.a, node.b)
_ => self.expression(id)
}
return
}
self.text(name, true)
}
///|
fn Printer::property(self : Printer, id : Int32) -> Unit {
let node = self.program.nodes[id]
match node.kind {
PatternRest => {
self.text("...", false)
self.expression(node.a)
}
Method => {
let function = self.program.functions[node.b]
self.method_prefix(node.c, function)
self.property_key(node.a, node.d == PROPERTY_COMPUTED)
self.parameters(function)
self.isolated_block(function.body)
}
_ => self.data_property(node)
}
}
///|
fn Printer::method_prefix(
self : Printer,
kind : Int32,
function : Function,
) -> Unit {
guard kind != METHOD_GETTER else { return self.text("get", true) }
guard kind != METHOD_SETTER else { return self.text("set", true) }
if (function.flags & FUNCTION_ASYNC) != 0 {
self.text("async", true)
}
guard (function.flags & FUNCTION_GENERATOR) != 0 else { return }
self.text("*", false)
}
///|
fn Printer::data_property(self : Printer, property : Node) -> Unit {
let key = property.a
let value = property.b
let is_shorthand = property.c == PROPERTY_SHORTHAND
let expanded_proto = if is_shorthand {
let reference = self.program.references[self.program.nodes[value].a]
reference.name == NAME_PROTO && self.shorthand_value_changed(value)
} else {
false
}
if expanded_proto {
self.text("[\"__proto__\"]", false)
} else {
self.property_key(key, property.d == PROPERTY_COMPUTED)
}
let needs_value = if is_shorthand {
self.shorthand_value_changed(value) ||
self.program.property_names.contains(key)
} else {
true
}
guard needs_value else { return }
self.text(":", false)
self.expression(value)
}
///|
fn Printer::shorthand_value_changed(self : Printer, id : Int32) -> Bool {
let reference = self.program.references[self.program.nodes[id].a]
guard reference.symbol >= 0 else { return false }
let symbol = self.program.symbols[reference.symbol]
guard symbol.name_slot >= 0 else { return false }
self.program.generated_names[symbol.name_slot] !=
self.program.names[reference.name]
}
///|
fn Printer::parameters(self : Printer, function : Function) -> Unit {
self.text("(", false)
for i in 0.. 0 {
self.text(",", false)
}
self.pattern(self.program.edges[function.parameters_offset + i])
}
self.text(")", false)
}
///|
fn Printer::function(self : Printer, id : Int32, with_name : Bool) -> Unit {
let function = self.program.functions[id]
if (function.flags & FUNCTION_ASYNC) != 0 {
self.text("async", true)
}
guard (function.flags & FUNCTION_ARROW) == 0 else {
self.arrow_function(function)
return
}
self.text("function", true)
if (function.flags & FUNCTION_GENERATOR) != 0 {
self.text("*", false)
}
if with_name && function.name >= 0 {
self.reference(function.name)
}
self.parameters(function)
self.isolated_block(function.body)
}
///|
fn Printer::arrow_function(self : Printer, function : Function) -> Unit {
let has_single_identifier = function.parameters_count == 1 &&
self.program.nodes[self.program.edges[function.parameters_offset]].kind ==
PatternIdentifier
if has_single_identifier {
self.pattern(self.program.edges[function.parameters_offset])
} else {
self.parameters(function)
}
self.text("=>", false)
guard (function.flags & FUNCTION_EXPRESSION_BODY) != 0 else {
self.isolated_block(function.body)
return
}
self.expression(function.body)
}
///|
fn Printer::class(self : Printer, id : Int32) -> Unit {
let class = self.program.classes[id]
self.text("class", true)
if class.name >= 0 {
self.reference(class.name)
}
if class.superclass >= 0 {
self.text("extends", true)
self.primary(class.superclass)
}
self.text("{", false)
for i in 0.. Unit {
let node = self.program.nodes[id]
match node.kind {
Method => {
if (node.d & CLASS_MEMBER_STATIC) != 0 {
self.text("static", true)
}
let function = self.program.functions[node.b]
self.method_prefix(node.c, function)
self.class_key(node)
self.parameters(function)
self.isolated_block(function.body)
}
ClassField => {
if (node.d & CLASS_MEMBER_STATIC) != 0 {
self.text("static", true)
}
self.class_key(node)
if node.b >= 0 {
self.text("=", false)
self.expression(node.b)
}
self.text(";", false)
}
ClassStaticBlock => {
self.text("static", true)
self.isolated_block(node.a)
}
_ => abort("bad class member: \{node.kind.to_repr()}")
}
}
///|
fn Printer::block(self : Printer, id : Int32) -> Unit {
let node = self.program.nodes[id]
self.text("{", false)
self.statements(node.a, node.b)
self.pending_semicolon = false
self.text("}", false)
}
///|
fn Printer::primary(self : Printer, id : Int32) -> Unit {
guard id >= 0 else { return }
let node = self.program.nodes[id]
let value = if node.kind is Parenthesized {
self.program.nodes[self.program.unwrapped_expression(id)]
} else {
node
}
if value.kind is EvaluatedValue {
self.detached_value(value.a)
return
}
if node.kind is New {
self.new_expression(node, true)
return
}
let needs_parentheses = node.kind is BooleanLiteral ||
(node.kind is Identifier && self.program.is_undefined_value(id))
if needs_parentheses {
self.text("(", false)
}
self.expression(id)
if needs_parentheses {
self.text(")", false)
}
}
///|
fn Printer::expression(self : Printer, id : Int32) -> Unit {
guard id >= 0 else { return }
let node = self.program.nodes[id]
match node.kind {
Identifier =>
if self.program.is_undefined_value(id) {
self.text("void", true)
self.text("0", true)
} else {
self.reference(node.a)
}
NumberLiteral => self.number(node.a, node.b)
ZeroLiteral => self.text("0", true)
StringLiteral => self.string(node.a, node.b)
RegexLiteral => self.span(node.a, node.b, true)
TemplateNoSubstitution => self.span(node.a, node.b, false)
BooleanLiteral => {
let is_true = node.a == 1
self.text("!", false)
self.text(if is_true { "0" } else { "1" }, true)
}
NullLiteral => self.text("null", true)
This => self.text("this", true)
Super => self.text("super", true)
ArrayLiteral => self.array_literal(node.a, node.b)
ObjectLiteral => self.object_literal(node.a, node.b)
FunctionExpression | FunctionDeclaration => self.function(node.a, true)
ArrowExpression => self.function(node.a, false)
ClassExpression | ClassDeclaration => self.class(node.a)
Unary => {
let operator = self.program.operators[node.a]
self.text(operator.text(), operator.is_word())
if operator is Delete {
self.assignment_target(node.b)
} else if operator is TypeOf &&
self.program.nodes[self.program.unwrapped_expression(node.b)].kind
is EvaluatedValue {
self.detached_value(
self.program.nodes[self.program.unwrapped_expression(node.b)].a,
)
} else {
self.expression(node.b)
}
}
Update =>
self.update_expression(
self.program.operators[node.a],
node.c,
node.b == UPDATE_PREFIX,
)
Binary | Logical =>
self.binary_expression(self.program.operators[node.a], node.b, node.c)
Assignment => {
self.assignment_target(node.b)
let operator = self.program.operators[node.a]
if operator != Operator::Assignment {
self.text(operator.text(), false)
}
self.text("=", false)
self.assignment_expression(node.c)
}
Conditional => {
self.expression(node.a)
self.text("?", false)
self.assignment_expression(node.b)
self.text(":", false)
self.assignment_expression(node.c)
}
Call => {
self.primary(node.a)
if node.d == CHAIN_OPTIONAL {
self.text("?.", false)
}
self.text("(", false)
self.expressions(node.b, node.c)
self.text(")", false)
}
New => self.new_expression(node, false)
Member => self.member_expression(id, node)
Index => {
self.primary(node.a)
if node.d == CHAIN_OPTIONAL {
self.text("?.", false)
}
self.text("[", false)
self.property_expression(node.b)
self.text("]", false)
}
Sequence => self.sequence_expression(id)
Spread | PatternRest => {
self.text("...", false)
self.expression(node.a)
}
Parenthesized => {
self.text("(", false)
self.expression(node.a)
self.text(")", false)
}
EvaluatedValue => {
let value = self.program.nodes[self.program.unwrapped_expression(node.a)]
if value.kind is Identifier && !self.program.is_undefined_value(node.a) {
self.reference(value.a)
} else if value.kind
is (ArrowExpression | FunctionExpression | ClassExpression) {
self.detached_value(node.a)
} else {
self.parenthesized_expression(node.a)
}
}
Template => self.template_expression(node.a, node.b)
TaggedTemplate => {
self.primary(node.a)
self.expression(node.b)
}
PatternIdentifier
| PatternArray
| PatternObject
| PatternProperty
| PatternDefault => self.pattern(id)
_ => abort("bad expression: \{node.kind.to_repr()}")
}
}
///|
/// Empty arguments may be omitted only when no surrounding primary operation
/// could attach to the constructor instead of the newly constructed value.
fn Printer::new_expression(
self : Printer,
node : Node,
preserve_empty_arguments : Bool,
) -> Unit {
self.text("new", true)
self.primary(node.a)
if node.b >= 0 && (node.c > 0 || preserve_empty_arguments) {
self.text("(", false)
self.expressions(node.b, node.c)
self.text(")", false)
}
}
///|
fn Printer::array_literal(
self : Printer,
offset : Int32,
count : Int32,
) -> Unit {
self.text("[", false)
for index in 0.. 0 {
self.text(",", false)
}
let element = self.program.edges[offset + index]
guard element >= 0 else { continue }
self.expression(element)
}
self.array_end(offset, count)
}
///|
fn Printer::object_literal(
self : Printer,
offset : Int32,
count : Int32,
) -> Unit {
self.text("{", false)
for index in 0.. 0 {
self.text(",", false)
}
self.property(self.program.edges[offset + index])
}
self.text("}", false)
}
///|
fn Printer::update_expression(
self : Printer,
operator : Operator,
operand : Int32,
is_prefix : Bool,
) -> Unit {
if is_prefix {
self.text(operator.text(), false)
self.assignment_target(operand)
} else {
self.assignment_target(operand)
self.text(operator.text(), false)
}
}
///|
fn Printer::assignment_target(self : Printer, id : Int32) -> Unit {
let node = self.program.nodes[id]
match node.kind {
EvaluatedValue => self.detached_value(node.a)
Identifier | PatternIdentifier => self.reference(node.a)
Parenthesized => {
self.text("(", false)
self.assignment_target(node.a)
self.text(")", false)
}
_ => self.expression(id)
}
}
///|
fn Printer::detached_value(self : Printer, id : Int32) -> Unit {
self.text("(0,", false)
self.expression(id)
self.text(")", false)
}
///|
fn Printer::binary_expression(
self : Printer,
operator : Operator,
left : Int32,
right : Int32,
) -> Unit {
match operator {
Exponent => self.primary(left)
In => self.property_expression(left)
_ => self.expression(left)
}
self.text(operator.text(), operator.is_word())
if operator is (LogicalAnd | LogicalOr | NullishCoalescing) {
let right_node = self.program.nodes[right]
if right_node.kind is Parenthesized {
let inner = self.program.nodes[right_node.a]
if inner.kind is Logical && self.program.operators[inner.a] == operator {
self.expression(right_node.a)
return
}
}
}
self.expression(right)
}
///|
fn Printer::member_expression(
self : Printer,
id : Int32,
member_node : Node,
) -> Unit {
let object = member_node.a
let name_start = member_node.b
let name_end = member_node.c
guard object >= 0 else {
self.text("new", true)
self.text(".", false)
self.span(name_start, name_end, true)
return
}
let object_node = self.program.nodes[object]
let object_kind = object_node.kind
let needs_parentheses = object_kind == NumberLiteral ||
object_kind == BooleanLiteral ||
(object_kind is Identifier && self.program.is_undefined_value(object))
if needs_parentheses {
self.text("(", false)
}
if object_kind is New {
self.new_expression(object_node, true)
} else {
self.expression(object)
}
if needs_parentheses {
self.text(")", false)
}
self.text(if member_node.d == CHAIN_OPTIONAL { "?." } else { "." }, false)
guard self.program.member_names.get(id) is Some(name) else {
self.span(name_start, name_end, true)
return
}
self.text(name, true)
}
///|
fn Printer::template_expression(
self : Printer,
offset : Int32,
count : Int32,
) -> Unit {
self.text("`", false)
// Each literal span has two edges; each following interpolation has one.
for part = 0, edge = offset {
guard part < count else { break }
let literal_start = self.program.edges[edge]
let literal_end = self.program.edges[edge + 1]
self.span(literal_start, literal_end, false)
guard part < count - 1 else { continue part + 1, edge + 2 }
let expression = self.program.edges[edge + 2]
self.text("${", false)
self.expression(expression)
self.text("}", false)
continue part + 1, edge + 3
}
self.text("`", false)
}
///|
fn NodeKind::needs_semicolon(kind : NodeKind) -> Bool {
match kind {
ExpressionStatement
| VariableDeclaration
| Return
| Throw
| Break
| Continue
| Debugger
| DoWhile => true
_ => false
}
}
///|
fn Printer::statement(self : Printer, id : Int32) -> Unit {
let node = self.program.nodes[id]
match node.kind {
ExpressionStatement => self.expression_statement(node.a)
ModuleStatement => self.module_stmt(node.a)
Block => self.block(id)
VariableDeclaration => self.declaration(node)
Declarator => ()
FunctionDeclaration => self.function(node.a, true)
ClassDeclaration => self.class(node.a)
Empty => self.text(";", false)
Elided => ()
Debugger => self.text("debugger", true)
If => self.if_statement(node.a, node.b, node.c)
For => self.for_statement(node)
ForIn | ForOf => self.for_each_statement(node)
While => {
self.text("while", true)
self.parenthesized_expression(node.a)
self.statement_body(node.b, false)
}
DoWhile => {
self.text("do", true)
self.statement_body(node.a, false)
self.text("while", true)
self.parenthesized_expression(node.b)
}
Return => {
self.text("return", true)
if node.a >= 0 {
self.expression(self.program.unwrapped_expression(node.a))
}
}
Throw => {
self.text("throw", true)
self.expression(self.program.unwrapped_expression(node.a))
}
Break | Continue => {
self.text(if node.kind == Break { "break" } else { "continue" }, true)
if node.a >= 0 {
self.label_reference(node.c, node.a, node.b)
}
}
Labeled => self.labeled_statement(node.d, node.c)
Switch => self.switch_statement(node.a, node.b, node.c)
Try => self.try_statement(node.a, node.b, node.c)
With => {
self.text("with", true)
self.parenthesized_expression(node.a)
self.statement(node.b)
}
_ => abort("bad statement: \{node.kind.to_repr()}")
}
guard node.kind.needs_semicolon() else { return }
self.pending_semicolon = true
}
///|
fn Printer::parenthesized_expression(
self : Printer,
expression : Int32,
) -> Unit {
self.text("(", false)
self.expression(self.program.unwrapped_expression(expression))
self.text(")", false)
}
///|
/// An expression can become the start of a statement after value or branch
/// folding. Follow its first printed operand, keeping directive strings bare.
fn Printer::needs_statement_parentheses(self : Printer, id : Int32) -> Bool {
guard id >= 0 else { return false }
let node = self.program.nodes[id]
match node.kind {
ObjectLiteral | FunctionExpression | ClassExpression | PatternObject => true
Identifier | PatternIdentifier => {
let reference = self.program.references[node.a]
self.program.names[reference.name] == "let" &&
(
reference.symbol < 0 ||
self.program.symbols[reference.symbol].name_slot < 0
)
}
Member | Index | Call | Conditional | TaggedTemplate =>
self.needs_statement_parentheses(node.a)
Binary | Logical | Assignment => self.needs_statement_parentheses(node.b)
Sequence if node.b > 0 =>
self.needs_statement_parentheses(self.program.edges[node.a])
Update if node.b != UPDATE_PREFIX =>
self.needs_statement_parentheses(node.c)
_ => false
}
}
///|
fn Printer::expression_statement(self : Printer, id : Int32) -> Unit {
if self.needs_statement_parentheses(id) {
self.parenthesized_expression(id)
} else {
self.expression(id)
}
}
///|
fn Printer::statement_slot_expression(
self : Printer,
id : Int32,
context : SlotOperand,
) -> Unit {
if self.needs_statement_parentheses(id) {
self.parenthesized_expression(id)
} else {
self.slot_expression(id, context)
}
}
///|
fn Printer::if_statement(
self : Printer,
condition : Int32,
consequent : Int32,
alternative : Int32,
) -> Unit {
let consequent_node = self.program.nodes[self.program.single_statement(
consequent,
)]
let consequent_empty = self.program.empty_branch(consequent)
let alternative_empty = self.program.empty_branch(alternative)
if consequent_empty {
if alternative_empty {
// A bare string condition must not become a directive.
self.parenthesized_expression(condition)
self.pending_semicolon = true
} else {
let alternative_node = self.program.nodes[self.program.single_statement(
alternative,
)]
if alternative_node.kind is ExpressionStatement {
self.logical_statement(LogicalOr, condition, alternative_node.a)
} else {
self.if_statement(
self.program.negated_value(condition),
alternative,
-1,
)
}
}
return
}
if alternative >= 0 && alternative_empty {
self.if_statement(condition, consequent, -1)
return
}
if alternative >= 0 &&
self.same_effect_if_statement(condition, consequent, alternative) {
return
}
if consequent_node.kind is ExpressionStatement {
if alternative < 0 {
self.logical_statement(LogicalAnd, condition, consequent_node.a)
return
}
let alternative_node = self.program.nodes[self.program.single_statement(
alternative,
)]
if alternative_node.kind is ExpressionStatement {
let assignment = self.program.nodes[consequent_node.a]
let symbol = if assignment.kind is Assignment {
self.program.identifier_symbol(assignment.b)
} else {
-1
}
if symbol >= 0 && !self.program.has_dynamic_scope {
let yes = self.program.slot_assignment(consequent, symbol)
let no = self.program.slot_assignment(alternative, symbol)
if yes >= 0 &&
no >= 0 &&
self.program.nodes[alternative_node.a].kind is Assignment {
self.expression_statement(assignment.b)
self.text("=", false)
self.slot_expression(condition, ConditionalTest)
self.text("?", false)
self.slot_expression(yes, ConditionalArm)
self.text(":", false)
self.slot_expression(no, ConditionalArm)
self.pending_semicolon = true
return
}
}
self.statement_slot_expression(condition, ConditionalTest)
self.text("?", false)
self.slot_expression(consequent_node.a, ConditionalArm)
self.text(":", false)
self.slot_expression(alternative_node.a, ConditionalArm)
self.pending_semicolon = true
return
}
}
self.text("if", true)
self.parenthesized_expression(condition)
self.statement_body(consequent, alternative >= 0)
guard alternative >= 0 else { return }
self.text("else", true)
self.statement_body(alternative, false)
}
///|
fn Program::empty_branch(self : Program, id : Int32) -> Bool {
id < 0 || self.is_empty_statement(self.single_statement(id))
}
///|
/// Extract one optional guard without moving declarations or allocating nodes.
/// The boolean says whether the effect runs when that guard is false.
fn Program::guarded_statement_effect(
self : Program,
id : Int32,
) -> (Int32, Bool, Int32)? {
let statement = self.nodes[self.single_statement(id)]
match statement.kind {
ExpressionStatement => {
let value = self.nodes[self.unwrapped_expression(statement.a)]
if value.kind is Logical {
let operator = self.operators[value.a]
if operator is (LogicalAnd | LogicalOr) {
return Some((value.b, operator is LogicalOr, value.c))
}
}
Some((-1, false, statement.a))
}
If => {
let (condition, negate, body) = if self.empty_branch(statement.c) {
(statement.a, false, statement.b)
} else if self.empty_branch(statement.b) {
(statement.a, true, statement.c)
} else {
return None
}
let effect = self.nodes[self.single_statement(body)]
guard effect.kind is ExpressionStatement else { return None }
Some((condition, negate, effect.a))
}
_ => None
}
}
///|
fn Printer::same_effect_if_statement(
self : Printer,
condition : Int32,
consequent : Int32,
alternative : Int32,
) -> Bool {
let program = self.program
guard !program.has_dynamic_scope else { return false }
guard program.guarded_statement_effect(consequent)
is Some((yes_guard, negate_yes, effect)) else {
return false
}
guard program.guarded_statement_effect(alternative)
is Some((no_guard, negate_no, other_effect)) &&
program.same_value_expression(effect, other_effect) else {
return false
}
if yes_guard < 0 && no_guard < 0 {
self.expression_statement(program.sequence_pair(condition, effect))
self.pending_semicolon = true
return true
}
if no_guard < 0 && negate_yes {
let skip_effect = program.slot_logical(LogicalAnd, condition, yes_guard)
self.logical_statement(LogicalOr, skip_effect, effect)
return true
}
let yes_condition = if negate_yes {
program.negated_value(yes_guard)
} else {
yes_guard
}
let no_condition = if negate_no {
program.negated_value(no_guard)
} else {
no_guard
}
let guard_condition = if yes_guard < 0 {
program.slot_logical(LogicalOr, condition, no_condition)
} else if no_guard < 0 {
program.slot_logical(
LogicalOr,
program.negated_value(condition),
yes_condition,
)
} else {
program.compact_choice(condition, yes_condition, no_condition)
}
self.logical_statement(LogicalAnd, guard_condition, effect)
true
}
///|
fn Printer::logical_statement(
self : Printer,
operator : Operator,
left : Int32,
right : Int32,
) -> Unit {
self.statement_slot_expression(left, LogicalOperand(operator))
self.text(operator.text(), false)
self.slot_expression(right, LogicalOperand(operator))
self.pending_semicolon = true
}
///|
fn Printer::slot_expression(
self : Printer,
id : Int32,
context : SlotOperand,
) -> Unit {
let value = self.program.unwrapped_expression(id)
if self.program.slot_needs_parentheses(value, context) {
self.parenthesized_expression(value)
} else {
self.expression(value)
}
}
///|
/// Initializers, assignments and conditional arms accept one assignment
/// expression. Only an outer comma expression needs grouping here; grouping
/// within a member, call or optional chain remains attached to that operand.
/// For initializers retain source grouping because their grammar excludes in.
fn Printer::assignment_expression(self : Printer, id : Int32) -> Unit {
guard self.program.nodes[id].kind is Parenthesized && !self.in_for_initializer else {
return self.expression(id)
}
self.slot_expression(id, ConditionalArm)
}
///|
fn Program::always_exits(self : Program, id : Int32) -> Bool {
let node = self.nodes[id]
match node.kind {
Return | Throw | Break | Continue => true
Block if node.b > 0 => self.always_exits(self.edges[node.a + node.b - 1])
If => node.c >= 0 && self.always_exits(node.b) && self.always_exits(node.c)
_ => false
}
}
///|
fn Printer::statement_body(
self : Printer,
id : Int32,
protect_else : Bool,
) -> Unit {
let statement = self.program.single_statement(id)
let node = self.program.nodes[statement]
if protect_else &&
self.program.has_unmatched_if(statement) &&
!(self.program.nodes[id].kind is Block) {
self.text("{", false)
self.statement(statement)
self.pending_semicolon = false
self.text("}", false)
return
}
// Lexical declarations and block functions need their original environment.
// Keep an unmatched inner if enclosed when an outer else follows it.
let needs_block = match node.kind {
VariableDeclaration => node.a != DECLARATION_VAR
FunctionDeclaration | ClassDeclaration => true
_ => protect_else && self.program.has_unmatched_if(statement)
}
self.statement(if needs_block { id } else { statement })
}
///|
fn Program::has_unmatched_if(self : Program, id : Int32) -> Bool {
let node = self.nodes[self.single_statement(id)]
match node.kind {
If =>
self.empty_branch(node.b) ||
self.empty_branch(node.c) ||
self.has_unmatched_if(node.c)
While | With => self.has_unmatched_if(node.b)
For => self.has_unmatched_if(node.d)
ForIn | ForOf => self.has_unmatched_if(node.c)
Labeled => self.has_unmatched_if(node.c)
_ => false
}
}
///|
fn Printer::for_initializer(self : Printer, initializer : Int32) -> Unit {
let previous_context = self.in_for_initializer
self.in_for_initializer = true
let node = self.program.nodes[initializer]
if node.kind is VariableDeclaration {
self.declaration(node)
} else {
self.expression(initializer)
}
self.in_for_initializer = previous_context
}
///|
fn Printer::for_statement(self : Printer, statement : Node) -> Unit {
let initializer = statement.a
let condition = statement.b
let update = statement.c
let body = statement.d
self.text("for", true)
self.text("(", false)
if initializer >= 0 {
self.for_initializer(initializer)
}
self.text(";", false)
self.expression(condition)
self.text(";", false)
self.expression(update)
self.text(")", false)
self.statement_body(body, false)
}
///|
fn Printer::for_each_statement(self : Printer, statement : Node) -> Unit {
let binding = statement.a
let iterable = statement.b
let body = statement.c
let is_await = statement.d == 1
self.text("for", true)
if is_await {
self.text("await", true)
}
self.text("(", false)
if self.program.nodes[binding].kind is VariableDeclaration {
self.for_initializer(binding)
} else {
self.assignment_target(binding)
}
self.text(if statement.kind == ForIn { "in" } else { "of" }, true)
self.expression(iterable)
self.text(")", false)
self.statement_body(body, false)
}
///|
fn Printer::labeled_statement(
self : Printer,
label : Int32,
body : Int32,
) -> Unit {
let name = self.label_name()
self.labels.active.push((label, name))
self.text(name, true)
self.text(":", false)
self.statement_body(body, false)
ignore(self.labels.active.pop())
}
///|
fn Printer::switch_statement(
self : Printer,
discriminant : Int32,
offset : Int32,
count : Int32,
) -> Unit {
self.text("switch", true)
self.text("(", false)
self.expression(discriminant)
self.text("){", false)
for index in 0.. Unit {
self.text("try", true)
self.block(body)
if handler >= 0 {
let catch_clause = self.program.nodes[handler]
self.text("catch", true)
if catch_clause.a >= 0 {
self.text("(", false)
self.pattern(catch_clause.a)
self.text(")", false)
}
self.block(catch_clause.b)
}
guard finalizer >= 0 else { return }
self.text("finally", true)
self.block(finalizer)
}
///|
fn Printer::switch_case(self : Printer, id : Int32) -> Unit {
let node = self.program.nodes[id]
if node.a >= 0 {
self.text("case", true)
self.expression(node.a)
} else {
self.text("default", true)
}
self.text(":", false)
self.statements(node.b, node.c)
}
///|
fn Printer::declaration(self : Printer, node : Node) -> Unit {
self.text(
match node.a {
DECLARATION_VAR => "var"
DECLARATION_LET => "let"
_ => "const"
},
true,
)
self.declarators(node.b, node.c)
}
///|
fn Printer::declarators(self : Printer, offset : Int32, count : Int32) -> Unit {
for i in 0.. 0 {
self.text(",", false)
}
let declarator = self.program.nodes[self.program.edges[offset + i]]
self.pattern(declarator.a)
guard declarator.b >= 0 else { continue }
self.text("=", false)
self.assignment_expression(declarator.b)
}
}
///|
fn Program::print(program : Program) -> String {
let printer = Printer::new(program)
let root = program.nodes[program.root]
printer.statements(root.a, root.b)
printer.flush_semicolon()
printer.output.to_string()
}
///|
fn Printer::class_key(self : Printer, property : Node) -> Unit {
self.property_key(property.a, (property.d & CLASS_MEMBER_COMPUTED) != 0)
}
///|
fn Printer::is_renamed(self : Printer, symbol : Int32) -> Bool {
guard symbol >= 0 else { return false }
let binding = self.program.symbols[symbol]
binding.name_slot >= 0 &&
self.program.generated_names[binding.name_slot] !=
self.program.names[binding.name]
}
///|
/// Statement folding builds a right-hand sequence spine. Print it iteratively
/// so a long run of expression statements does not grow the call stack.
fn Printer::sequence_expression(self : Printer, id : Int32) -> Unit {
for current = id {
let node = self.program.nodes[current]
guard node.b > 0 else { break }
for index in 0..<(node.b - 1) {
self.expression(self.program.edges[node.a + index])
self.text(",", false)
}
let tail = self.program.edges[node.a + node.b - 1]
guard self.program.nodes[tail].kind is Sequence else {
self.expression(tail)
break
}
continue tail
}
}