///|
priv struct Program {
  bytecodes : Array[Int]
  classes : Array[CharClass]
  capture_num : Int
  capture_name_mapping : Map[String, Int]
}

///|
fn op_code(self : Program, pos : Int) -> OpCode {
  OpCode::from_bytecode(self.bytecodes[pos])
}

///|
fn Program::operand(self : Program, pos : Int) -> Int {
  self.bytecodes[pos]
}

///|
impl Show for Program with output(self, logger) -> Unit {
  let mut pos = 0
  while pos < self.bytecodes.length() {
    let size = self.output_op(pos, logger)
    logger.write_char('\n')
    pos += size
  }
}

///|
fn output_op(self : Program, pos : Int, logger : &Logger) -> Int {
  let mut size = 0
  logger.write_string("\{pos} ")
  let opcode = OpCode::from_bytecode(self.bytecodes[pos])
  match opcode {
    Nop | Stop | BeginOfInput | EndOfInput | NullMark | SetMark | GetMark => {
      logger.write_string("\{opcode}")
      size = 1
    }
    Goto
    | BranchLazy
    | BranchMark
    | BranchMarkLazy
    | NullCount
    | SetCount
    | CaptureMark => {
      let opd = self.bytecodes[pos + 1]
      logger.write_string("\{opcode} \{opd}")
      size = 2
    }
    Single | NotSingle => {
      let char = self.bytecodes[pos + 1]
      logger.write_string("\{opcode} \{char}")
      size = 2
    }
    Class => {
      let class_idx = self.bytecodes[pos + 1]
      let clazz = self.classes[class_idx]
      logger.write_string("\{opcode} \{clazz}")
      size = 2
    }
    SingleRepeat
    | NotSingleRepeat
    | SingleLoop
    | NotSingleLoop
    | SingleLazy
    | NotSingleLazy => {
      let char = self.bytecodes[pos + 1]
      let opd2 = self.bytecodes[pos + 2]
      logger.write_string("\{opcode} \{char} \{opd2}")
      size = 3
    }
    BranchCount | BranchCountLazy => {
      let opd1 = self.bytecodes[pos + 1]
      let opd2 = self.bytecodes[pos + 2]
      logger.write_string("\{opcode} \{opd1} \{opd2}")
      size = 3
    }
    ClassRepeat | ClassLoop | ClassLazy => {
      let class_idx = self.bytecodes[pos + 1]
      let clazz = self.classes[class_idx]
      let opd2 = self.bytecodes[pos + 2]
      logger.write_string("\{opcode} \{clazz} \{opd2}")
      size = 3
    }
  }
  size
}