(** {1 VM extension interface}

    Type definitions for VM bytecode extensions. See
    [plans/sx-vm-opcode-extension.md].

    An extension is a first-class module of type [EXTENSION]: it has a
    stable [name], an [init] that returns its private state, and an
    [opcodes] function that lists the opcodes it provides.

    Opcode handlers receive the live [vm] and the active [frame]. They
    read operands via [Sx_vm.read_u8] / [read_u16], manipulate the stack
    via [push] / [pop] / [peek], and update the frame's [ip] as needed. *)

(** A handler for an extension opcode. Reads operands from bytecode,
    manipulates the VM stack, updates the frame's instruction pointer.
    May raise exceptions (which propagate via the existing VM error path). *)
type handler = Sx_vm.vm -> Sx_vm.frame -> unit

(** State an extension carries alongside the VM. Opaque to the VM core;
    extensions extend this with their own constructor and cast as needed.

    Extensible variant — extensions add cases:
    {[
      type Sx_vm_extension.extension_state +=
        | ErlangState of erlang_scheduler
    ]} *)
type extension_state = ..

(** An extension is a first-class module of this signature. *)
module type EXTENSION = sig
  (** Stable name for this extension (e.g. ["erlang"], ["guest_vm"]).
      Used as the lookup key in the registry and as the prefix for opcode
      names ([erlang.OP_PATTERN_TUPLE_2] etc). *)
  val name : string

  (** Initialize per-instance state. Called once when [register] is
      invoked on this extension. *)
  val init : unit -> extension_state

  (** Opcodes this extension provides. Each is
      [(opcode_id, opcode_name, handler)].

      [opcode_id] must be in the range 200-247 (the extension partition;
      see the partition comment at the top of [Sx_vm]'s dispatch loop).
      Conflicts with already-registered opcodes cause [register] to
      fail. *)
  val opcodes : extension_state -> (int * string * handler) list
end
