implement import & scopedImport (WIP, ResolvePath resolves to string)

This commit is contained in:
2026-05-05 15:18:31 +08:00
parent 49392f66f8
commit cfd2df5d0e
11 changed files with 453 additions and 102 deletions
+77 -5
View File
@@ -77,13 +77,25 @@ pub trait VmRuntimeCtx {
pub trait VmCode {
fn bytecode(&self) -> &[u8];
fn compile(
fn compile_with_scope(
&mut self,
source: Source,
extra_scope: Option<ExtraScope>,
ctx: &mut impl VmRuntimeCtx,
) -> fix_error::Result<InstructionPtr>;
}
/// Extra scope passed to a re-entrant compile from inside a running VM.
///
/// Currently only `ScopedImport` is produced (by the `scopedImport` builtin),
/// but the variant is kept open so REPL bindings could later land here too.
pub enum ExtraScope {
ScopedImport {
keys: HashSet<StringId>,
slot_id: u32,
},
}
trait VmRuntimeCtxExt: VmRuntimeCtx {
fn get_string<'a, 'gc: 'a>(&'a self, val: StrictValue<'gc>) -> Option<&'a str>;
fn get_string_id<'a, 'gc: 'a>(
@@ -197,6 +209,7 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
enum Break {
Force,
Done,
LoadFile,
}
type Step = std::ops::ControlFlow<Break>;
@@ -214,6 +227,7 @@ pub struct Vm<'gc> {
env: GcEnv<'gc>,
import_cache: HashMap<PathBuf, Value<'gc>>,
scope_slots: Vec<Value<'gc>>,
builtins: Value<'gc>,
empty_list: Value<'gc>,
@@ -224,9 +238,24 @@ pub struct Vm<'gc> {
#[collect(require_static)]
result: Option<Result<fix_common::Value>>,
#[collect(require_static)]
pending_load: Option<PendingLoad>,
functor_sym: StringId,
}
#[derive(Debug)]
pub(crate) struct PendingLoad {
pub path: PathBuf,
pub scope: Option<PendingScope>,
}
#[derive(Debug)]
pub(crate) struct PendingScope {
pub keys: HashSet<StringId>,
pub slot_id: u32,
}
enum OperandData {
Const(StaticValue),
BigInt(i64),
@@ -234,7 +263,7 @@ enum OperandData {
BuiltinConst(StringId),
Builtins,
ReplBinding(StringId),
ScopedImportBinding(StringId),
ScopedImportBinding { slot_id: u32, name: StringId },
}
impl OperandData {
@@ -260,7 +289,17 @@ impl OperandData {
.unwrap(),
Builtins => root.builtins,
ReplBinding(_id) => todo!(),
ScopedImportBinding(_id) => todo!(),
ScopedImportBinding { slot_id, name } => {
#[allow(clippy::unwrap_used)]
let scope = root
.scope_slots
.get(slot_id as usize)
.expect("invalid scope slot");
#[allow(clippy::unwrap_used)]
let attrs = scope.as_gc::<AttrSet>().expect("scope must be attrset");
#[allow(clippy::unwrap_used)]
attrs.lookup(name).expect("scoped binding not found")
}
}
}
}
@@ -334,6 +373,7 @@ impl<'gc> Vm<'gc> {
env: Gc::new(mc, RefLock::new(Env::empty())),
import_cache: HashMap::new(),
scope_slots: Vec::new(),
builtins,
empty_list: Value::new_gc(Gc::new(mc, List::default())),
@@ -342,6 +382,7 @@ impl<'gc> Vm<'gc> {
force_mode,
result: None,
pending_load: None,
functor_sym: ctx.intern_string("__functor"),
}
@@ -541,6 +582,7 @@ struct CallFrame<'gc> {
enum Action {
Continue { pc: usize },
Done(Result<fix_common::Value>),
LoadFile(PendingLoad),
}
enum NixNum {
@@ -573,6 +615,24 @@ impl Vm<'_> {
}
}
}
Action::LoadFile(load) => {
let source = match Source::new_file(load.path) {
Ok(src) => src,
Err(err) => break Err(Error::eval_error(format!("import failed: {err}"))),
};
let extra_scope = load.scope.map(|s| ExtraScope::ScopedImport {
keys: s.keys,
slot_id: s.slot_id,
});
let new_ip = match code.compile_with_scope(source, extra_scope, runtime) {
Ok(ip) => ip,
Err(err) => break Err(err),
};
pc = new_ip.0;
arena.mutate_root(|mc, root| {
root.env = Gc::new(mc, RefLock::new(Env::empty()));
});
}
Action::Done(done) => break done,
}
}
@@ -604,6 +664,11 @@ impl<'gc> Vm<'gc> {
TailResult::Done => {
Action::Done(self.result.take().expect("TailResult::Done without result"))
}
TailResult::LoadFile => Action::LoadFile(
self.pending_load
.take()
.expect("TailResult::LoadFile without pending_load"),
),
}
}
}
@@ -689,7 +754,7 @@ impl<'gc> Vm<'gc> {
ConcatStrings => self.op_concat_strings(ctx, &mut reader, mc),
CoerceToString => self.op_coerce_to_string(&mut reader, mc),
ResolvePath => self.op_resolve_path(ctx),
ResolvePath => self.op_resolve_path(ctx, &mut reader, mc),
Assert => self.op_assert(ctx, &mut reader, mc),
@@ -699,7 +764,7 @@ impl<'gc> Vm<'gc> {
LoadBuiltin => self.op_load_builtin(&mut reader),
LoadReplBinding => self.op_load_repl_binding(&mut reader),
LoadScopedBinding => self.op_load_scoped_binding(&mut reader),
LoadScopedBinding => self.op_load_scoped_binding(ctx, &mut reader, mc),
Illegal => unreachable!(),
};
@@ -709,6 +774,13 @@ impl<'gc> Vm<'gc> {
Step::Break(Break::Done) => {
return Action::Done(self.result.take().expect("Break::Done without result"));
}
Step::Break(Break::LoadFile) => {
return Action::LoadFile(
self.pending_load
.take()
.expect("Break::LoadFile without pending_load"),
);
}
}
}
}