///|
/// Expression printing - declaring function for recursion
fn print_expr(expr : @syntax.Expr, logger : StringBuilder) -> Unit {
match expr {
Constant(c~, ..) => print_constant(c, logger)
Ident(id~, ..) => print_var(id, logger)
Infix(op~, lhs~, rhs~, ..) => {
print_expr(lhs, logger)
logger.write_string(" ")
print_var(op, logger)
logger.write_string(" ")
print_expr(rhs, logger)
}
Let(pattern~, expr~, body~, ..) => {
logger.write_string("let ")
print_pattern(pattern, logger)
logger.write_string(" = ")
print_expr(expr, logger)
logger.write_string("; ")
print_expr(body, logger)
}
Apply(func~, args~, attr~, ..) => {
print_expr(func, logger)
match attr {
Question => logger.write_string("?")
Exclamation => logger.write_string("!")
NoAttr => ()
}
// Special case: if we have try operator (? or !) with empty args on an identifier,
// don't print empty parentheses since this is likely a try operator on a value
let should_print_parens = match (func, attr, args.is_empty()) {
(Ident(..), Question, true) | (Ident(..), Exclamation, true) => false
_ => true
}
if should_print_parens {
logger.write_string("(")
print_list(args, print_argument, ", ", logger)
logger.write_string(")")
}
}
Unary(op~, expr~, ..) => {
print_var(op, logger)
print_expr(expr, logger)
}
Array(exprs~, ..) => {
logger.write_string("[")
print_list(exprs, print_expr, ", ", logger)
logger.write_string("]")
}
ArraySpread(elems~, ..) => {
logger.write_string("[")
print_list(elems, print_spreadable_elem, ", ", logger)
logger.write_string("]")
}
ArrayGet(array~, index~, ..) => {
print_expr(array, logger)
logger.write_string("[")
print_expr(index, logger)
logger.write_string("]")
}
ArrayGetSlice(array~, start_index~, end_index~, ..) => {
print_expr(array, logger)
logger.write_string("[")
match start_index {
Some(start) => print_expr(start, logger)
None => ()
}
logger.write_string(":")
match end_index {
Some(end) => print_expr(end, logger)
None => ()
}
logger.write_string("]")
}
ArraySet(array~, index~, value~, ..) => {
print_expr(array, logger)
logger.write_string("[")
print_expr(index, logger)
logger.write_string("] = ")
print_expr(value, logger)
}
Tuple(exprs~, ..) => {
logger.write_string("(")
print_list(exprs, print_expr, ", ", logger)
logger.write_string(")")
}
Record(type_name~, fields~, trailing~, ..) => {
match type_name {
Some(tn) => {
print_type_name(tn, logger)
logger.write_string("::")
}
None => ()
}
logger.write_string("{")
print_list(fields, print_field_def, ", ", logger)
match trailing {
Comma => logger.write_string(",")
Semi => logger.write_string(";")
None => ()
}
logger.write_string("}")
}
Field(record~, accessor~, ..) => {
print_expr(record, logger)
print_accessor(accessor, logger)
}
If(cond~, ifso~, ifnot~, ..) => {
logger.write_string("if ")
print_expr(cond, logger)
logger.write_string(" { ")
print_expr(ifso, logger)
logger.write_string(" }")
match ifnot {
Some(else_expr) => {
logger.write_string(" else { ")
print_expr(else_expr, logger)
logger.write_string(" }")
}
None => ()
}
}
Match(expr~, cases~, ..) => {
logger.write_string("match ")
print_expr(expr, logger)
logger.write_string(" { ")
for case in cases {
print_pattern(case.pattern, logger)
match case.guard_ {
Some(guard_expr) => {
logger.write_string(" if ")
print_expr(guard_expr, logger)
}
None => ()
}
logger.write_string(" => ")
print_expr(case.body, logger)
logger.write_string("; ")
}
logger.write_string("}")
}
Sequence(exprs~, last_expr~, ..) => {
for expr_item in exprs {
print_expr(expr_item, logger)
logger.write_string("; ")
}
print_expr(last_expr, logger)
}
Unit(faked~, ..) => if not(faked) { logger.write_string("()") }
Hole(kind=@syntax.Hole::Synthesized | Incomplete, ..) =>
logger.write_string("_")
Hole(kind=@syntax.Hole::Todo, ..) => logger.write_string("...")
Group(expr~, group~, ..) =>
match group {
Brace => {
logger.write_string("{ ")
print_expr(expr, logger)
logger.write_string(" }")
}
Paren => {
logger.write_string("(")
print_expr(expr, logger)
logger.write_string(")")
}
}
ArrayAugmentedSet(op~, array~, index~, value~, ..) => {
print_expr(array, logger)
logger.write_string("[")
print_expr(index, logger)
logger.write_string("] ")
print_var(op, logger)
logger.write_string("= ")
print_expr(value, logger)
}
MultilineString(elems~, ..) => print_multiline_string(elems, logger)
Interp(elems~, ..) => {
logger.write_string("\"")
for elem in elems {
match elem {
Literal(repr~, ..) => logger.write_string(repr)
Expr(expr~, ..) => {
logger.write_string("\\{")
print_expr(expr, logger)
logger.write_string("}")
}
Source(_) => logger.write_string("\\{...}")
}
}
logger.write_string("\"")
}
Constraint(expr~, ty~, ..) => {
logger.write_string("(")
print_expr(expr, logger)
logger.write_string(" : ")
print_type(ty, logger)
logger.write_string(")")
}
Constr(constr~, ..) => print_constructor(constr, logger)
While(loop_cond~, loop_body~, while_else~, label~, ..) => {
match label {
Some(l) => {
print_label(l, logger)
logger.write_string("~: ")
}
None => ()
}
logger.write_string("while ")
print_expr(loop_cond, logger)
logger.write_string(" { ")
print_expr(loop_body, logger)
logger.write_string(" }")
match while_else {
Some(else_expr) => {
logger.write_string(" else { ")
print_expr(else_expr, logger)
logger.write_string(" }")
}
None => ()
}
}
Function(func~, ..) => print_func(func, logger)
Guard(cond~, otherwise~, body~, ..) => {
logger.write_string("guard ")
print_expr(cond, logger)
match otherwise {
Some(else_expr) => {
logger.write_string(" else { ")
print_expr(else_expr, logger)
logger.write_string(" }; ")
print_expr(body, logger)
}
None => {
// Guard statement without else clause - needs semicolon for statement separation
logger.write_string("; ")
print_expr(body, logger)
}
}
}
Is(expr~, pat~, ..) => {
print_expr(expr, logger)
logger.write_string(" is ")
logger.write_string("(")
print_pattern(pat, logger)
logger.write_string(")")
}
Defer(expr~, body~, ..) => {
logger.write_string("defer ")
print_expr(expr, logger)
logger.write_string("; ")
print_expr(body, logger)
}
LetFn(name~, func~, body~, ..) =>
// Print function declaration syntax: fn name(params) { ... }
match func {
Lambda(parameters~, body=func_body, return_type~, error_type~, ..) => {
logger.write_string("fn ")
print_binder(name, logger)
logger.write_string("(")
print_list(parameters, print_parameter, ", ", logger)
logger.write_string(")")
match return_type {
Some(ret_ty) => {
logger.write_string(" -> ")
print_type(ret_ty, logger)
}
None => ()
}
print_error_type(error_type, logger)
logger.write_string(" { ")
print_expr(func_body, logger)
logger.write_string(" }; ")
print_expr(body, logger)
}
Match(cases~, ..) => {
logger.write_string("fn ")
print_binder(name, logger)
logger.write_string(" { ")
for case in cases {
print_list(case.patterns, print_pattern, ", ", logger)
match case.guard_ {
Some(guard_expr) => {
logger.write_string(" if ")
print_expr(guard_expr, logger)
}
None => ()
}
logger.write_string(" => ")
print_expr(case.body, logger)
logger.write_string("; ")
}
logger.write_string("}; ")
print_expr(body, logger)
}
}
LetRec(bindings~, body~, ..) => {
logger.write_string("letrec ")
let mut first = true
for binding in bindings {
if not(first) {
logger.write_string(" and ")
}
first = false
print_binder(binding.0, logger)
logger.write_string(" = ")
print_func(binding.1, logger)
}
logger.write_string("; ")
print_expr(body, logger)
}
LetAnd(bindings~, body~, ..) => {
logger.write_string("letrec ")
let mut first = true
for binding in bindings {
if not(first) {
logger.write_string(" and ")
}
first = false
print_binder(binding.0, logger)
match binding.1 {
Some(t) => {
logger.write_string(" : ")
print_type(t, logger)
}
None => ()
}
logger.write_string(" = ")
print_func(binding.2, logger)
}
logger.write_string("; ")
print_expr(body, logger)
}
RecordUpdate(type_name~, record~, fields~, ..) => {
match type_name {
Some(tn) => {
print_type_name(tn, logger)
logger.write_string("::")
}
None => ()
}
logger.write_string("{ ")
logger.write_string("..")
print_expr(record, logger)
logger.write_string(", ")
print_list(fields, print_field_def, ", ", logger)
logger.write_string(" }")
}
Method(type_name~, method_name~, ..) => {
print_type_name(type_name, logger)
logger.write_string("::")
print_label(method_name, logger)
}
DotApply(self~, method_name~, args~, return_self~, attr~, ..) => {
print_expr(self, logger)
if return_self {
logger.write_string("..")
} else {
logger.write_string(".")
}
print_label(method_name, logger)
match attr {
Question => logger.write_string("?")
Exclamation => logger.write_string("!")
NoAttr => ()
}
logger.write_string("(")
print_list(args, print_argument, ", ", logger)
logger.write_string(")")
}
As(expr~, trait_~, ..) => {
print_expr(expr, logger)
logger.write_string(" as ")
print_type_name(trait_, logger)
}
Mutate(record~, accessor~, field~, augmented_by~, ..) => {
print_expr(record, logger)
print_accessor(accessor, logger)
logger.write_string(" ")
match augmented_by {
Some(op) => {
print_var(op, logger)
logger.write_string("= ")
}
None => logger.write_string("= ")
}
print_expr(field, logger)
}
LetMut(binder~, ty~, expr~, body~, ..) => {
logger.write_string("let mut ")
print_binder(binder, logger)
match ty {
Some(t) => {
logger.write_string(" : ")
print_type(t, logger)
}
None => ()
}
logger.write_string(" = ")
print_expr(expr, logger)
logger.write_string("; ")
print_expr(body, logger)
}
Pipe(lhs~, rhs~, ..) => {
print_expr(lhs, logger)
logger.write_string(" |> ")
print_expr(rhs, logger)
}
Assign(var_~, expr~, augmented_by~, ..) => {
print_var(var_, logger)
logger.write_string(" ")
match augmented_by {
Some(op) => {
print_var(op, logger)
logger.write_string("= ")
}
None => logger.write_string("= ")
}
print_expr(expr, logger)
}
Return(return_value~, ..) => {
logger.write_string("return")
match return_value {
Some(value) => {
logger.write_string(" ")
print_expr(value, logger)
}
None => ()
}
}
Raise(err_value~, ..) => {
logger.write_string("raise ")
print_expr(err_value, logger)
}
Break(arg~, label~, ..) => {
logger.write_string("break")
match label {
Some(l) => {
logger.write_string(" ")
print_label(l, logger)
logger.write_string("~")
}
None => ()
}
match arg {
Some(a) => {
logger.write_string(" ")
print_expr(a, logger)
}
None => ()
}
}
Continue(args~, label~, ..) => {
logger.write_string("continue")
match label {
Some(l) => {
logger.write_string(" ")
print_label(l, logger)
logger.write_string("~")
}
None => ()
}
if not(args.is_empty()) {
logger.write_string(" ")
print_list(args, print_expr, ", ", logger)
}
}
Loop(args~, body~, label~, ..) => {
match label {
Some(l) => {
print_label(l, logger)
logger.write_string("~: ")
}
None => ()
}
logger.write_string("loop ")
if not(args.is_empty()) {
if args.length() == 1 {
// Single argument: "loop arg { ... }"
print_expr(args.unsafe_head(), logger)
logger.write_string(" ")
} else {
// Multiple arguments: "loop (arg1, arg2) { ... }"
logger.write_string("(")
print_list(args, print_expr, ", ", logger)
logger.write_string(") ")
}
}
logger.write_string("{ ")
for case in body {
print_list(case.patterns, print_pattern, ", ", logger)
match case.guard_ {
Some(guard_expr) => {
logger.write_string(" if ")
print_expr(guard_expr, logger)
}
None => ()
}
logger.write_string(" => ")
print_expr(case.body, logger)
logger.write_string("; ")
}
logger.write_string("}")
}
For(binders~, condition~, continue_block~, body~, for_else~, label~, ..) => {
match label {
Some(l) => {
print_label(l, logger)
logger.write_string("~: ")
}
None => ()
}
logger.write_string("for ")
let mut first = true
for binder_pair in binders {
if not(first) {
logger.write_string(", ")
}
first = false
print_binder(binder_pair.0, logger)
logger.write_string(" = ")
print_expr(binder_pair.1, logger)
}
match condition {
Some(cond) => {
logger.write_string("; ")
print_expr(cond, logger)
}
None => ()
}
if not(continue_block.is_empty()) {
logger.write_string("; ")
first = true
for update_pair in continue_block {
if not(first) {
logger.write_string(", ")
}
first = false
print_binder(update_pair.0, logger)
logger.write_string(" = ")
print_expr(update_pair.1, logger)
}
}
logger.write_string(" { ")
print_expr(body, logger)
logger.write_string(" }")
match for_else {
Some(else_expr) => {
logger.write_string(" else { ")
print_expr(else_expr, logger)
logger.write_string(" }")
}
None => ()
}
}
ForEach(binders~, expr~, body~, else_block~, label~, ..) => {
match label {
Some(l) => {
print_label(l, logger)
logger.write_string("~: ")
}
None => ()
}
logger.write_string("for ")
let mut first = true
for binder in binders {
if not(first) {
logger.write_string(", ")
}
first = false
match binder {
Some(b) => print_binder(b, logger)
None => logger.write_string("_")
}
}
logger.write_string(" in ")
print_expr(expr, logger)
logger.write_string(" { ")
print_expr(body, logger)
logger.write_string(" }")
match else_block {
Some(else_expr) => {
logger.write_string(" else { ")
print_expr(else_expr, logger)
logger.write_string(" }")
}
None => ()
}
}
Try(body~, catch_~, catch_all~, try_else~, has_try~, ..) => {
if has_try {
logger.write_string("try ")
}
loop body {
Group(expr~, ..) => continue expr
_ as body => {
logger.write_string("{ ")
print_expr(body, logger)
logger.write_string(" }")
}
}
if not(catch_.is_empty()) || catch_all {
logger.write_string(" catch { ")
for case in catch_ {
print_pattern(case.pattern, logger)
match case.guard_ {
Some(guard_expr) => {
logger.write_string(" if ")
print_expr(guard_expr, logger)
}
None => ()
}
logger.write_string(" => ")
print_expr(case.body, logger)
logger.write_string("; ")
}
logger.write_string("}")
}
match try_else {
Some(else_cases) => {
logger.write_string(" else { ")
for case in else_cases {
print_pattern(case.pattern, logger)
match case.guard_ {
Some(guard_expr) => {
logger.write_string(" if ")
print_expr(guard_expr, logger)
}
None => ()
}
logger.write_string(" => ")
print_expr(case.body, logger)
logger.write_string("; ")
}
logger.write_string("}")
}
None => ()
}
}
TryOperator(body~, kind~, ..) => {
logger.write_string("try")
match kind {
Question => logger.write_string("? ")
Exclamation => logger.write_string("! ")
}
print_expr(body, logger)
}
Map(elems~, ..) => {
logger.write_string("{")
let mut first = true
for elem in elems {
if not(first) {
logger.write_string(", ")
}
first = false
print_constant(elem.key, logger)
logger.write_string(": ")
print_expr(elem.expr, logger)
}
logger.write_string("}")
}
StaticAssert(asserts~, body~) => {
logger.write_string("assert ")
let mut first = true
for assertion in asserts {
if not(first) {
logger.write_string(", ")
}
first = false
print_type(assertion.ty, logger)
logger.write_string(" : ")
print_long_ident(assertion.trait_, logger)
if not(assertion.msg.is_empty()) {
logger.write_string(" \"")
logger.write_string(assertion.msg)
logger.write_string("\"")
}
}
logger.write_string("; ")
print_expr(body, logger)
}
}
}
///|
fn print_multiline_string_inner(s : String, logger : StringBuilder) -> Unit {
fn flush_segment(seg : Int, i : Int) {
if i > seg {
logger.write_substring(s, seg, i - seg)
}
}
// The loop keeps two pieces of state:
// i : the current scanning position
// seg : the beginning index of the current "plain" segment that has
// no escaping requirements. Whenever we meet a character that
// needs escaping, we flush the segment [seg, i) and reset seg.
let len = s.length()
for i = 0, seg = 0 {
if i >= len {
// If we reached the end of the string, flush any remaining segment
// and break out of the loop.
flush_segment(seg, i)
break
}
let code = s.unsafe_charcode_at(i)
match code {
'"' | '\\' as c => {
flush_segment(seg, i)
logger..write_char('\\')..write_char(c.unsafe_to_char())
// Advance both pointers: continue with next index, new segment starts after current char
continue i + 1, i + 1
}
'\n' => {
flush_segment(seg, i)
logger.write_string("\\n")
continue i + 1, i + 1
}
'\r' => {
flush_segment(seg, i)
logger.write_string("\\r")
continue i + 1, i + 1
}
'\b' => {
flush_segment(seg, i)
logger.write_string("\\b")
continue i + 1, i + 1
}
'\t' => {
flush_segment(seg, i)
logger.write_string("\\t")
continue i + 1, i + 1
}
code =>
if code < ' ' {
flush_segment(seg, i)
logger
..write_string("\\u{")
..write_char(to_hex_digit(code / 16))
..write_char(to_hex_digit(code % 16))
..write_char('}')
continue i + 1, i + 1
} else {
// Normal character, keep scanning; only advance index.
continue i + 1, seg
}
}
}
}
///|
fn to_hex_digit(i : Int) -> Char {
if i < 10 {
(i + '0').unsafe_to_char()
} else {
(i + 'a' - 10).unsafe_to_char()
}
}
///|
fn print_multiline_string(
elems : @list.List[@syntax.MultilineStringElem],
logger : StringBuilder,
) -> Unit {
logger.write_string("\"")
for elem in elems {
match elem {
String(s) => print_multiline_string_inner(s, logger)
Interp(interp_elems) =>
for interp_elem in interp_elems {
match interp_elem {
Literal(repr~, ..) => print_multiline_string_inner(repr, logger)
Expr(expr~, ..) => {
logger.write_string("\\{")
print_expr(expr, logger)
logger.write_string("}")
}
Source(elem) => logger.write_string(elem.source)
}
}
}
}
logger.write_string("\"")
}