implement import & scopedImport (WIP, ResolvePath resolves to string)
This commit is contained in:
+77
-5
@@ -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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user