///|
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
labels : LabelNames
}
///|
fn Printer::new(program : Program) -> Printer {
{
program,
output: StringBuilder(),
previous_is_word: false,
last_character: 0,
pending_semicolon: false,
labels: LabelNames::new(program.alphabet),
}
}
///|
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 : Int,
end : Int,
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 : Int) -> 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 : Int, count : Int) -> Unit {
for i in 0.. Unit {
for i in 0.. 0 {
self.text(",", false)
}
self.expression(self.program.edges[offset + i])
}
}
///|
fn Printer::pattern(self : Printer, id : Int) -> 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 : Int, count : Int) -> 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 : Int, count : Int) -> 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 : Int, count : Int) -> 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 : Int, 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 : Int) -> 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 : Int) -> 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 : Int,
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.is_renamed(reference.symbol)
} else {
false
}
if expanded_proto {
self.text("[\"__proto__\"]", false)
} else {
self.property_key(key, property.d == PROPERTY_COMPUTED)
}
let needs_value = if is_shorthand {
let symbol = self.program.references[self.program.nodes[value].a].symbol
self.is_renamed(symbol) || self.program.property_names.contains(key)
} else {
true
}
guard needs_value else { return }
self.text(":", false)
self.expression(value)
}
///|
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 : Int, 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 : Int) -> 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 : Int) -> 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 : Int) -> Unit {
let needs_parentheses = id >= 0 &&
self.program.nodes[id].kind == BooleanLiteral
if needs_parentheses {
self.text("(", false)
}
self.expression(id)
if needs_parentheses {
self.text(")", false)
}
}
///|
fn Printer::expression(self : Printer, id : Int) -> Unit {
guard id >= 0 else { return }
let node = self.program.nodes[id]
match node.kind {
Identifier => self.reference(node.a)
NumberLiteral => self.number(node.a, node.b)
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())
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.expression(node.b)
let operator = self.program.operators[node.a]
if operator != Operator::Assignment {
self.text(operator.text(), false)
}
self.text("=", false)
self.expression(node.c)
}
Conditional => {
self.expression(node.a)
self.text("?", false)
self.expression(node.b)
self.text(":", false)
self.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.text("new", true)
self.primary(node.a)
if node.b >= 0 {
self.text("(", false)
self.expressions(node.b, node.c)
self.text(")", 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.expressions(node.a, node.b)
Spread | PatternRest => {
self.text("...", false)
self.expression(node.a)
}
Parenthesized => {
self.text("(", false)
self.expression(node.a)
self.text(")", false)
}
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()}")
}
}
///|
fn Printer::array_literal(self : Printer, offset : Int, count : Int) -> 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 : Int, count : Int) -> 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 : Int,
is_prefix : Bool,
) -> Unit {
if is_prefix {
self.text(operator.text(), false)
self.expression(operand)
} else {
self.expression(operand)
self.text(operator.text(), false)
}
}
///|
fn Printer::binary_expression(
self : Printer,
operator : Operator,
left : Int,
right : Int,
) -> Unit {
match operator {
Exponent => self.primary(left)
In => self.property_expression(left)
_ => self.expression(left)
}
self.text(operator.text(), operator.is_word())
self.expression(right)
}
///|
fn Printer::member_expression(
self : Printer,
id : Int,
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_kind = self.program.nodes[object].kind
let needs_parentheses = object_kind == NumberLiteral ||
object_kind == BooleanLiteral
if needs_parentheses {
self.text("(", false)
}
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 : Int,
count : Int,
) -> 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 : Int) -> Unit {
let node = self.program.nodes[id]
match node.kind {
ExpressionStatement => self.expression(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)
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(node.b)
}
DoWhile => {
self.text("do", true)
self.statement(node.a)
self.text("while", true)
self.parenthesized_expression(node.b)
}
Return => {
self.text("return", true)
if node.a >= 0 {
self.expression(node.a)
}
}
Throw => {
self.text("throw", true)
self.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 : Int) -> Unit {
self.text("(", false)
self.expression(expression)
self.text(")", false)
}
///|
fn Printer::if_statement(
self : Printer,
condition : Int,
consequent : Int,
alternative : Int,
) -> Unit {
self.text("if", true)
self.parenthesized_expression(condition)
self.statement(consequent)
guard alternative >= 0 else { return }
self.text("else", true)
self.statement(alternative)
}
///|
fn Printer::for_initializer(self : Printer, initializer : Int) -> Unit {
let node = self.program.nodes[initializer]
guard node.kind == VariableDeclaration else {
return self.expression(initializer)
}
self.declaration(node)
}
///|
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)
}
///|
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)
self.for_initializer(binding)
self.text(if statement.kind == ForIn { "in" } else { "of" }, true)
self.expression(iterable)
self.text(")", false)
self.statement(body)
}
///|
fn Printer::labeled_statement(self : Printer, label : Int, body : Int) -> Unit {
let name = self.label_name()
self.labels.active.push((label, name))
self.text(name, true)
self.text(":", false)
self.statement(body)
ignore(self.labels.active.pop())
}
///|
fn Printer::switch_statement(
self : Printer,
discriminant : Int,
offset : Int,
count : Int,
) -> 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 : Int) -> 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,
)
for i in 0.. 0 {
self.text(",", false)
}
let declarator = self.program.nodes[self.program.edges[node.b + i]]
self.pattern(declarator.a)
guard declarator.b >= 0 else { continue }
self.text("=", false)
self.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 : Int) -> 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]
}