Máquina Virtual (VM) de Forja
Archivo: src/vm.rs
La VM es el intérprete stack‑based que ejecuta el bytecode generado por el compilador. Está diseñada para ser sencilla, segura y extensible, permitiendo que nuevos back‑ends (ASM, LLVM, WASM) reutilicen la misma lógica de ejecución.
Diseño general
struct VM: Contiene el estado de ejecución:pc: usize– contador de programa (puntero a la siguiente opcode).stack: Vec<Valor>– pila de valores operandos.globals: HashMap<String, Valor>– variables globales accesibles por nombre.locals: Vec<Valor>– almacenamiento de variables locales por índice (acceso O(1)).functions: HashMap<String, Vec<Opcode>>– tabla de funciones definidas por el usuario.builtins: HashMap<BuiltinKind, fn(&mut VM, usize)>– despacho de built‑ins.error_handler: Option<fn(&mut VM, ErrorForja)>– manejo de errores de tiempo de ejecución.
enum Valor: Representa los tipos de datos soportados en tiempo de ejecución (Entero, Decimal, Texto, Booleano, Nulo, etc.). Cada variante almacena el dato en unRc<str>o tipos primitivos.
Loop principal de ejecución
Ejemplo: la función run itera sobre el bytecode con un bucle while, usa match para cada Opcode y actualiza la pila y el contador del programa.
El match cubre todos los opcodes definidos en src/bytecode.rs. Cada rama realiza la operación correspondiente sobre la pila o el entorno.
Gestión de funciones
fn call_function(&mut self, name: &str, argc: usize) -> Result<(), ErrorForja>: Prepara un nuevo frame de pila, copia los argumentos y salta al cuerpo de la función.- Al terminar una función,
retrestaurapcal punto de llamada y destruye el frame local.
Built‑ins y extensibilidad
Los built‑ins están registrados en VM::register_builtin. Cada builtin recibe una referencia mutable al VM y el número de argumentos que ya están en la pila.
El builtin builtin_escribir escribe en la salida estándar el texto pasado como argumento cuando argc == 1. Se extrae el valor del stack, se verifica que sea Valor::Texto y se imprime usando print!.
Los usuarios pueden añadir nuevos built‑ins mediante la API pública VM::add_builtin que inserta la función en el hashmap.
Errores y depuración
- El opcode
Error(ErrorForja)permite propagar errores de tiempo de compilación a la VM. - El campo
error_handlerpermite establecer una callback personalizada (por ejemplo, para imprimir trazas de pila).
Relación con otros módulos
bytecode.rs: Define losOpcodeque la VM interpreta.compiler_asm.rsycompiler_llvm.rs: Generan bytecode como una de sus salidas.uops.rs: Opcionalmente, la VM puede ejecutar directamente una lista deUop(modo JIT) medianteVM::execute_uops(no documentado aquí).