///|
fn print_array_pattern(
  pat : @syntax.ArrayPattern,
  logger : StringBuilder,
) -> Unit {
  match pat {
    Pattern(p) => print_pattern(p, logger)
    // Always treat StringSpread as a plain rest pattern for consistency
    StringSpread(_s) => {
      logger.write_string(".. ")
      logger.write_string("\"")
      logger.write_string(_s)
      logger.write_string("\"")
    }
    // Always treat BytesSpread as a plain rest pattern for consistency
    BytesSpread(_s) => {
      logger.write_string(".. ")
      logger.write_string("\"")
      logger.write_string(_s)
      logger.write_string("\"")
    }
    ConstSpread(binder~, pkg~, ..) =>
      // Handle ConstSpread patterns - any ConstSpread without a package is a plain rest pattern
      if pkg is None {
        logger.write_string("..")
      } else {
        logger.write_string(".. ")
        logger.write_string("@")
        logger.write_string(pkg.unwrap())
        logger.write_string(".")
        print_binder(binder, logger)
      }
  }
}

///|
fn print_array_patterns(
  pats : @syntax.ArrayPatterns,
  logger : StringBuilder,
) -> Unit {
  match pats {
    Closed(patterns) => print_list(patterns, print_array_pattern, ", ", logger)
    Open(head_patterns, tail_patterns, rest_binder) => {
      print_list(head_patterns, print_array_pattern, ", ", logger)
      if not(head_patterns.is_empty()) {
        logger.write_string(", ")
      }
      print_dot_dot_binder(rest_binder, logger)
      if not(tail_patterns.is_empty()) {
        logger.write_string(", ")
        print_list(tail_patterns, print_array_pattern, ", ", logger)
      }
    }
  }
}

///|
fn print_constr_pat_arg(
  arg : @syntax.ConstrPatArg,
  logger : StringBuilder,
) -> Unit {
  match arg.kind {
    Positional => print_pattern(arg.pat, logger)
    Labelled(label) => {
      print_label(label, logger)
      logger.write_string("=")
      print_pattern(arg.pat, logger)
    }
    LabelledPun(label) => {
      print_label(label, logger)
      logger.write_string("~")
    }
    LabelledOption(label~, ..) => {
      print_label(label, logger)
      logger.write_string("?=")
      print_pattern(arg.pat, logger)
    }
    LabelledOptionPun(label~, ..) => {
      print_label(label, logger)
      logger.write_string("?~")
    }
  }
}

///|
fn print_record_field_pat(
  field : @syntax.FieldPat,
  logger : StringBuilder,
) -> Unit {
  if field.is_pun {
    print_label(field.label, logger)
  } else {
    print_label(field.label, logger)
    logger.write_string(":")
    print_pattern(field.pattern, logger)
  }
}

///|
fn print_map_pat_elem(
  elem : @syntax.MapPatElem,
  logger : StringBuilder,
) -> Unit {
  print_constant(elem.key, logger)
  if elem.match_absent {
    logger.write_string("?")
  }
  logger.write_string(" : ")
  print_pattern(elem.pat, logger)
}

///|
fn print_pattern(pat : @syntax.Pattern, logger : StringBuilder) -> Unit {
  match pat {
    Alias(pat~, alias_~, ..) => {
      print_pattern(pat, logger)
      logger.write_string(" as ")
      print_binder(alias_, logger)
    }
    Any(..) => logger.write_string("_")
    Array(pats~, ..) => {
      logger.write_string("[")
      print_array_patterns(pats, logger)
      logger.write_string("]")
    }
    Constant(c~, ..) => print_constant(c, logger)
    Constraint(pat~, ty~, ..) => {
      print_pattern(pat, logger)
      logger.write_string(" : ")
      print_type(ty, logger)
    }
    Constr(constr~, args~, is_open~, ..) => {
      print_constructor(constr, logger)
      match args {
        Some(arguments) => {
          logger.write_string("(")
          print_list(arguments, print_constr_pat_arg, ", ", logger)
          if is_open {
            logger.write_string(", ..")
          }
          logger.write_string(")")
        }
        None => ()
      }
    }
    Or(pat1~, pat2~, ..) => {
      print_pattern(pat1, logger)
      logger.write_string(" | ")
      print_pattern(pat2, logger)
    }
    Tuple(pats~, ..) => {
      logger.write_string("(")
      print_list(pats, print_pattern, ", ", logger)
      logger.write_string(")")
    }
    Var(binder) => print_binder(binder, logger)
    Record(fields~, is_closed~, ..) => {
      logger.write_string("{")
      print_list(fields, print_record_field_pat, ", ", logger)
      if not(is_closed) {
        if not(fields.is_empty()) {
          logger.write_string(", ")
        }
        logger.write_string("..")
      }
      logger.write_string("}")
    }
    Map(elems~, is_closed~, ..) => {
      logger.write_string("{")
      print_list(elems, print_map_pat_elem, ", ", logger)
      if not(is_closed) {
        if not(elems.is_empty()) {
          logger.write_string(", ")
        }
        logger.write_string("..")
      }
      logger.write_string("}")
    }
    Range(lhs~, rhs~, inclusive~, ..) => {
      print_pattern(lhs, logger)
      if inclusive {
        logger.write_string(" ..= ")
      } else {
        logger.write_string(" ..<")
      }
      print_pattern(rhs, logger)
    }
    SpecialConstr(binder~, args~, ..) => {
      print_binder(binder, logger)
      logger.write_string("(")
      print_list(args, print_constr_pat_arg, ", ", logger)
      logger.write_string(")")
    }
  }
}