refactor: reorganize crate hierarchy
This commit is contained in:
Generated
+31
-61
@@ -445,22 +445,18 @@ name = "fix"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bumpalo",
|
|
||||||
"clap",
|
"clap",
|
||||||
"criterion",
|
"criterion",
|
||||||
"ere",
|
"ere",
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
"fix-compiler",
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
"fix-ir",
|
"fix-lang",
|
||||||
|
"fix-runtime",
|
||||||
"fix-vm",
|
"fix-vm",
|
||||||
"ghost-cell",
|
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"miette",
|
"miette",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"rnix",
|
|
||||||
"rustyline",
|
"rustyline",
|
||||||
"string-interner",
|
"string-interner",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@@ -471,51 +467,33 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-abstract-vm"
|
name = "fix-bytecode"
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"fix-builtins",
|
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
|
||||||
"gc-arena",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"likely_stable",
|
|
||||||
"num_enum",
|
|
||||||
"smallvec",
|
|
||||||
"sptr",
|
|
||||||
"string-interner",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fix-builtins"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"gc-arena",
|
|
||||||
"num_enum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fix-codegen"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
"fix-builtins",
|
"fix-lang",
|
||||||
"fix-common",
|
"likely_stable",
|
||||||
"fix-ir",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"rnix",
|
|
||||||
"string-interner",
|
"string-interner",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-common"
|
name = "fix-compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ere",
|
"bumpalo",
|
||||||
"gc-arena",
|
"colored",
|
||||||
|
"fix-bytecode",
|
||||||
|
"fix-error",
|
||||||
|
"fix-lang",
|
||||||
|
"fix-runtime",
|
||||||
|
"ghost-cell",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"num_enum",
|
||||||
|
"rnix",
|
||||||
|
"rowan",
|
||||||
"string-interner",
|
"string-interner",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -528,34 +506,28 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-ir"
|
name = "fix-lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"ere",
|
||||||
"fix-builtins",
|
"gc-arena",
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
|
||||||
"ghost-cell",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"rnix",
|
|
||||||
"rowan",
|
|
||||||
"string-interner",
|
"string-interner",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-primops"
|
name = "fix-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
|
"fix-lang",
|
||||||
"gc-arena",
|
"gc-arena",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
|
"likely_stable",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"sptr",
|
||||||
"string-interner",
|
"string-interner",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -563,12 +535,10 @@ dependencies = [
|
|||||||
name = "fix-vm"
|
name = "fix-vm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
"fix-primops",
|
"fix-lang",
|
||||||
|
"fix-runtime",
|
||||||
"gc-arena",
|
"gc-arena",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"likely_stable",
|
"likely_stable",
|
||||||
|
|||||||
+4
-6
@@ -2,13 +2,11 @@
|
|||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = [
|
members = [
|
||||||
"fix",
|
"fix",
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
"fix-compiler",
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
"fix-ir",
|
"fix-lang",
|
||||||
"fix-primops",
|
"fix-runtime",
|
||||||
"fix-vm",
|
"fix-vm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use fix_codegen::OperandType;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use string_interner::Symbol as _;
|
|
||||||
|
|
||||||
use crate::{OperandData, VmRuntimeCtx};
|
|
||||||
|
|
||||||
pub struct BytecodeReader<'a> {
|
|
||||||
bytecode: &'a [u8],
|
|
||||||
pc: usize,
|
|
||||||
inst_start_pc: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BytecodeReader<'a> {
|
|
||||||
pub fn new(bytecode: &'a [u8], pc: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
bytecode,
|
|
||||||
pc,
|
|
||||||
inst_start_pc: pc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
bytecode,
|
|
||||||
pc: inst_start_pc + 1,
|
|
||||||
inst_start_pc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg_attr(debug_assertions, track_caller)]
|
|
||||||
fn read_array<const N: usize>(&mut self) -> [u8; N] {
|
|
||||||
let ret = self.bytecode[self.pc..self.pc + N]
|
|
||||||
.try_into()
|
|
||||||
.expect("read_array failed");
|
|
||||||
self.pc += N;
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_op(&mut self) -> fix_codegen::Op {
|
|
||||||
use fix_codegen::Op;
|
|
||||||
self.inst_start_pc = self.pc;
|
|
||||||
let byte = self.bytecode[self.pc];
|
|
||||||
if !likely_stable::likely((0..Op::Illegal as u8).contains(&byte)) {
|
|
||||||
panic!("unknown opcode: {byte:#04x}")
|
|
||||||
}
|
|
||||||
self.pc += 1;
|
|
||||||
unsafe { std::mem::transmute::<u8, Op>(byte) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_u8(&mut self) -> u8 {
|
|
||||||
let val = self.bytecode[self.pc];
|
|
||||||
self.pc += 1;
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_u16(&mut self) -> u16 {
|
|
||||||
u16::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_u32(&mut self) -> u32 {
|
|
||||||
u32::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_i32(&mut self) -> i32 {
|
|
||||||
i32::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_i64(&mut self) -> i64 {
|
|
||||||
i64::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_f64(&mut self) -> f64 {
|
|
||||||
f64::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_string_id(&mut self) -> StringId {
|
|
||||||
let raw = self.read_u32();
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
StringId(string_interner::symbol::SymbolU32::try_from_usize(raw as usize).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_operand_data<C: VmRuntimeCtx>(&mut self, ctx: &C) -> OperandData {
|
|
||||||
let tag = self.read_u8();
|
|
||||||
let Ok(ty) = OperandType::try_from_primitive(tag)
|
|
||||||
.map_err(|err| panic!("unknown operand tag: {:#04x}", err.number));
|
|
||||||
match ty {
|
|
||||||
OperandType::Const => {
|
|
||||||
let id = self.read_u32();
|
|
||||||
OperandData::Const(ctx.get_const(id))
|
|
||||||
}
|
|
||||||
OperandType::BigInt => {
|
|
||||||
let val = self.read_i64();
|
|
||||||
OperandData::BigInt(val)
|
|
||||||
}
|
|
||||||
OperandType::Local => {
|
|
||||||
let layer = self.read_u8();
|
|
||||||
let idx = self.read_u32();
|
|
||||||
OperandData::Local { layer, idx }
|
|
||||||
}
|
|
||||||
OperandType::BuiltinConst => {
|
|
||||||
let id = self.read_string_id();
|
|
||||||
OperandData::BuiltinConst(id)
|
|
||||||
}
|
|
||||||
OperandType::Builtins => OperandData::Builtins,
|
|
||||||
OperandType::ReplBinding => {
|
|
||||||
let id = self.read_string_id();
|
|
||||||
OperandData::ReplBinding(id)
|
|
||||||
}
|
|
||||||
OperandType::ScopedImportBinding => {
|
|
||||||
let slot_id = self.read_u32();
|
|
||||||
let name = self.read_string_id();
|
|
||||||
OperandData::ScopedImportBinding { slot_id, name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pc(&self) -> usize {
|
|
||||||
self.pc
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_pc(&mut self, pc: usize) {
|
|
||||||
self.pc = pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_start_pc(&self) -> usize {
|
|
||||||
self.inst_start_pc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fix-builtins"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
gc-arena = { workspace = true }
|
|
||||||
@@ -1,406 +0,0 @@
|
|||||||
use gc_arena::Collect;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
|
|
||||||
macro_rules! define_builtins {
|
|
||||||
($(($name:literal, $variant:ident, $arity:expr)),* $(,)?) => {
|
|
||||||
/// Builtin function registry.
|
|
||||||
/// Array index IS the PrimOp id. (name, arity) pairs.
|
|
||||||
pub const BUILTINS: &[(&str, u8)] = &[
|
|
||||||
$(($name, $arity),)*
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Collect)]
|
|
||||||
#[repr(u8)]
|
|
||||||
#[collect(require_static)]
|
|
||||||
pub enum BuiltinId {
|
|
||||||
$($variant,)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
define_builtins! {
|
|
||||||
("abort", Abort, 1),
|
|
||||||
("__add", Add, 2),
|
|
||||||
("__addErrorContext", AddErrorContext, 2),
|
|
||||||
("__all", All, 2),
|
|
||||||
("__any", Any, 2),
|
|
||||||
("__appendContext", AppendContext, 2),
|
|
||||||
("__attrNames", AttrNames, 1),
|
|
||||||
("__attrValues", AttrValues, 1),
|
|
||||||
("baseNameOf", BaseNameOf, 1),
|
|
||||||
("__bitAnd", BitAnd, 2),
|
|
||||||
("__bitOr", BitOr, 2),
|
|
||||||
("__bitXor", BitXor, 2),
|
|
||||||
("break", Break, 1),
|
|
||||||
("__catAttrs", CatAttrs, 2),
|
|
||||||
("__ceil", Ceil, 1),
|
|
||||||
("__compareVersions", CompareVersions, 2),
|
|
||||||
("__concatLists", ConcatLists, 1),
|
|
||||||
("__concatMap", ConcatMap, 2),
|
|
||||||
("__concatStringsSep", ConcatStringsSep, 2),
|
|
||||||
("__convertHash", ConvertHash, 1),
|
|
||||||
("__deepSeq", DeepSeq, 2),
|
|
||||||
("derivation", Derivation, 1),
|
|
||||||
("derivationStrict", DerivationStrict, 1),
|
|
||||||
("dirOf", DirOf, 1),
|
|
||||||
("__div", Div, 2),
|
|
||||||
("__elem", Elem, 2),
|
|
||||||
("__elemAt", ElemAt, 2),
|
|
||||||
("fetchGit", FetchGit, 1),
|
|
||||||
("fetchMercurial", FetchMercurial, 1),
|
|
||||||
("fetchTarball", FetchTarball, 1),
|
|
||||||
("fetchTree", FetchTree, 1),
|
|
||||||
("__fetchurl", FetchUrl, 1),
|
|
||||||
("__filter", Filter, 2),
|
|
||||||
("__filterSource", FilterSource, 2),
|
|
||||||
("__findFile", FindFile, 2),
|
|
||||||
("__floor", Floor, 1),
|
|
||||||
("__foldl'", FoldlStrict, 3),
|
|
||||||
("__fromJSON", FromJSON, 1),
|
|
||||||
("fromTOML", FromTOML, 1),
|
|
||||||
("__functionArgs", FunctionArgs, 1),
|
|
||||||
("__genList", GenList, 2),
|
|
||||||
("__genericClosure", GenericClosure, 1),
|
|
||||||
("__getAttr", GetAttr, 2),
|
|
||||||
("__getContext", GetContext, 1),
|
|
||||||
("__getEnv", GetEnv, 1),
|
|
||||||
("__groupBy", GroupBy, 2),
|
|
||||||
("__hasAttr", HasAttr, 2),
|
|
||||||
("__hasContext", HasContext, 1),
|
|
||||||
("__hashFile", HashFile, 2),
|
|
||||||
("__hashString", HashString, 2),
|
|
||||||
("__head", Head, 1),
|
|
||||||
("import", Import, 1),
|
|
||||||
("__intersectAttrs", IntersectAttrs, 2),
|
|
||||||
("__isAttrs", IsAttrs, 1),
|
|
||||||
("__isBool", IsBool, 1),
|
|
||||||
("__isFloat", IsFloat, 1),
|
|
||||||
("__isFunction", IsFunction, 1),
|
|
||||||
("__isInt", IsInt, 1),
|
|
||||||
("__isList", IsList, 1),
|
|
||||||
("isNull", IsNull, 1),
|
|
||||||
("__isPath", IsPath, 1),
|
|
||||||
("__isString", IsString, 1),
|
|
||||||
("__length", Length, 1),
|
|
||||||
("__lessThan", LessThan, 2),
|
|
||||||
("__listToAttrs", ListToAttrs, 1),
|
|
||||||
("map", Map, 2),
|
|
||||||
("__mapAttrs", MapAttrs, 2),
|
|
||||||
("__match", Match, 2),
|
|
||||||
("__mul", Mul, 2),
|
|
||||||
("__parseDrvName", ParseDrvName, 1),
|
|
||||||
("__partition", Partition, 2),
|
|
||||||
("__path", Path, 1),
|
|
||||||
("__pathExists", PathExists, 1),
|
|
||||||
("placeholder", Placeholder, 1),
|
|
||||||
("__readDir", ReadDir, 1),
|
|
||||||
("__readFile", ReadFile, 1),
|
|
||||||
("__readFileType", ReadFileType, 1),
|
|
||||||
("removeAttrs", RemoveAttrs, 2),
|
|
||||||
("__replaceStrings", ReplaceStrings, 3),
|
|
||||||
("scopedImport", ScopedImport, 2),
|
|
||||||
("__seq", Seq, 2),
|
|
||||||
("__sort", Sort, 2),
|
|
||||||
("__split", Split, 2),
|
|
||||||
("__splitVersion", SplitVersion, 1),
|
|
||||||
("__storePath", StorePath, 1),
|
|
||||||
("__stringLength", StringLength, 1),
|
|
||||||
("__sub", Sub, 2),
|
|
||||||
("__substring", Substring, 3),
|
|
||||||
("__tail", Tail, 1),
|
|
||||||
("throw", Throw, 1),
|
|
||||||
("__toFile", ToFile, 2),
|
|
||||||
("__toJSON", ToJSON, 1),
|
|
||||||
("__toPath", ToPath, 1),
|
|
||||||
("toString", ToString, 1),
|
|
||||||
("__toXML", ToXML, 1),
|
|
||||||
("__trace", Trace, 2),
|
|
||||||
("__tryEval", TryEval, 1),
|
|
||||||
("__typeOf", TypeOf, 1),
|
|
||||||
("__unsafeDiscardStringContext", UnsafeDiscardStringContext, 1),
|
|
||||||
("__unsafeDiscardOutputDependency", UnsafeDiscardOutputDependency, 1),
|
|
||||||
("__unsafeGetAttrPos", UnsafeGetAttrPos, 2),
|
|
||||||
("__warn", Warn, 2),
|
|
||||||
("__zipAttrsWith", ZipAttrsWith, 2),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
|
||||||
pub enum PrimOpPhase {
|
|
||||||
Abort,
|
|
||||||
Add,
|
|
||||||
AddErrorContext,
|
|
||||||
|
|
||||||
All,
|
|
||||||
AllCallPred,
|
|
||||||
AllCheck,
|
|
||||||
|
|
||||||
Any,
|
|
||||||
AnyCallPred,
|
|
||||||
AnyCheck,
|
|
||||||
|
|
||||||
AppendContext,
|
|
||||||
AttrNames,
|
|
||||||
AttrValues,
|
|
||||||
BaseNameOf,
|
|
||||||
BitAnd,
|
|
||||||
BitOr,
|
|
||||||
BitXor,
|
|
||||||
Break,
|
|
||||||
CatAttrs,
|
|
||||||
Ceil,
|
|
||||||
CompareVersions,
|
|
||||||
ConcatLists,
|
|
||||||
ConcatMap,
|
|
||||||
ConcatStringsSep,
|
|
||||||
ConvertHash,
|
|
||||||
|
|
||||||
DeepSeq,
|
|
||||||
DeepSeqPush,
|
|
||||||
DeepSeqLoop,
|
|
||||||
|
|
||||||
Derivation,
|
|
||||||
DerivationStrict,
|
|
||||||
DirOf,
|
|
||||||
Div,
|
|
||||||
Elem,
|
|
||||||
ElemAt,
|
|
||||||
FetchGit,
|
|
||||||
FetchMercurial,
|
|
||||||
FetchTarball,
|
|
||||||
FetchTree,
|
|
||||||
FetchUrl,
|
|
||||||
|
|
||||||
FilterForceList,
|
|
||||||
FilterCallPred,
|
|
||||||
FilterCheck,
|
|
||||||
|
|
||||||
FilterSource,
|
|
||||||
FindFile,
|
|
||||||
Floor,
|
|
||||||
FoldlStrict,
|
|
||||||
FoldlStrictEmpty,
|
|
||||||
FoldlStrictCall1,
|
|
||||||
FoldlStrictCall2,
|
|
||||||
FoldlStrictUpdate,
|
|
||||||
FromJSON,
|
|
||||||
FromTOML,
|
|
||||||
FunctionArgs,
|
|
||||||
GenList,
|
|
||||||
GenericClosure,
|
|
||||||
GetAttr,
|
|
||||||
GetContext,
|
|
||||||
GetEnv,
|
|
||||||
GroupBy,
|
|
||||||
HasAttr,
|
|
||||||
HasContext,
|
|
||||||
HashFile,
|
|
||||||
HashString,
|
|
||||||
Head,
|
|
||||||
Import,
|
|
||||||
IntersectAttrs,
|
|
||||||
IsAttrs,
|
|
||||||
IsBool,
|
|
||||||
IsFloat,
|
|
||||||
IsFunction,
|
|
||||||
IsInt,
|
|
||||||
IsList,
|
|
||||||
IsNull,
|
|
||||||
IsPath,
|
|
||||||
IsString,
|
|
||||||
Length,
|
|
||||||
LessThan,
|
|
||||||
ListToAttrs,
|
|
||||||
Map,
|
|
||||||
MapAttrs,
|
|
||||||
Match,
|
|
||||||
Mul,
|
|
||||||
ParseDrvName,
|
|
||||||
Partition,
|
|
||||||
Path,
|
|
||||||
PathExists,
|
|
||||||
Placeholder,
|
|
||||||
ReadDir,
|
|
||||||
ReadFile,
|
|
||||||
ReadFileType,
|
|
||||||
RemoveAttrs,
|
|
||||||
ReplaceStrings,
|
|
||||||
ScopedImport,
|
|
||||||
Seq,
|
|
||||||
Sort,
|
|
||||||
Split,
|
|
||||||
SplitVersion,
|
|
||||||
StorePath,
|
|
||||||
StringLength,
|
|
||||||
Sub,
|
|
||||||
Substring,
|
|
||||||
Tail,
|
|
||||||
Throw,
|
|
||||||
ToFile,
|
|
||||||
ToJSON,
|
|
||||||
ToPath,
|
|
||||||
ToString,
|
|
||||||
ToXML,
|
|
||||||
Trace,
|
|
||||||
TryEval,
|
|
||||||
TypeOf,
|
|
||||||
UnsafeDiscardStringContext,
|
|
||||||
UnsafeGetAttrPos,
|
|
||||||
Warn,
|
|
||||||
ZipAttrsWith,
|
|
||||||
|
|
||||||
ForceResultShallow,
|
|
||||||
ForceResultShallowPush,
|
|
||||||
ForceResultShallowLoop,
|
|
||||||
ForceResultDeepFinish,
|
|
||||||
|
|
||||||
EqStep,
|
|
||||||
EqForce,
|
|
||||||
|
|
||||||
// TODO: split into separate enums
|
|
||||||
CallPattern,
|
|
||||||
CallFunctor1,
|
|
||||||
CallFunctor2,
|
|
||||||
|
|
||||||
ImportFinalize,
|
|
||||||
ScopedImportFinalize,
|
|
||||||
|
|
||||||
AppendContextLoop,
|
|
||||||
AppendContextEntryForced,
|
|
||||||
AppendContextOutputsForced,
|
|
||||||
AppendContextOutputElementLoop,
|
|
||||||
AppendContextOutputElementForced,
|
|
||||||
|
|
||||||
UnsafeDiscardOutputDependency,
|
|
||||||
|
|
||||||
Illegal,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u8> for PrimOpPhase {
|
|
||||||
type Error = u8;
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
||||||
if (0..Self::Illegal as u8).contains(&value) {
|
|
||||||
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
|
|
||||||
} else {
|
|
||||||
Err(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuiltinId {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn entry_phase(self) -> PrimOpPhase {
|
|
||||||
use BuiltinId::*;
|
|
||||||
match self {
|
|
||||||
Abort => PrimOpPhase::Abort,
|
|
||||||
Add => PrimOpPhase::Add,
|
|
||||||
AddErrorContext => PrimOpPhase::AddErrorContext,
|
|
||||||
All => PrimOpPhase::All,
|
|
||||||
Any => PrimOpPhase::Any,
|
|
||||||
AppendContext => PrimOpPhase::AppendContext,
|
|
||||||
AttrNames => PrimOpPhase::AttrNames,
|
|
||||||
AttrValues => PrimOpPhase::AttrValues,
|
|
||||||
BaseNameOf => PrimOpPhase::BaseNameOf,
|
|
||||||
BitAnd => PrimOpPhase::BitAnd,
|
|
||||||
BitOr => PrimOpPhase::BitOr,
|
|
||||||
BitXor => PrimOpPhase::BitXor,
|
|
||||||
Break => PrimOpPhase::Break,
|
|
||||||
CatAttrs => PrimOpPhase::CatAttrs,
|
|
||||||
Ceil => PrimOpPhase::Ceil,
|
|
||||||
CompareVersions => PrimOpPhase::CompareVersions,
|
|
||||||
ConcatLists => PrimOpPhase::ConcatLists,
|
|
||||||
ConcatMap => PrimOpPhase::ConcatMap,
|
|
||||||
ConcatStringsSep => PrimOpPhase::ConcatStringsSep,
|
|
||||||
ConvertHash => PrimOpPhase::ConvertHash,
|
|
||||||
DeepSeq => PrimOpPhase::DeepSeq,
|
|
||||||
Derivation => PrimOpPhase::Derivation,
|
|
||||||
DerivationStrict => PrimOpPhase::DerivationStrict,
|
|
||||||
DirOf => PrimOpPhase::DirOf,
|
|
||||||
Div => PrimOpPhase::Div,
|
|
||||||
Elem => PrimOpPhase::Elem,
|
|
||||||
ElemAt => PrimOpPhase::ElemAt,
|
|
||||||
FetchGit => PrimOpPhase::FetchGit,
|
|
||||||
FetchMercurial => PrimOpPhase::FetchMercurial,
|
|
||||||
FetchTarball => PrimOpPhase::FetchTarball,
|
|
||||||
FetchTree => PrimOpPhase::FetchTree,
|
|
||||||
FetchUrl => PrimOpPhase::FetchUrl,
|
|
||||||
Filter => PrimOpPhase::FilterForceList,
|
|
||||||
FilterSource => PrimOpPhase::FilterSource,
|
|
||||||
FindFile => PrimOpPhase::FindFile,
|
|
||||||
Floor => PrimOpPhase::Floor,
|
|
||||||
FoldlStrict => PrimOpPhase::FoldlStrict,
|
|
||||||
FromJSON => PrimOpPhase::FromJSON,
|
|
||||||
FromTOML => PrimOpPhase::FromTOML,
|
|
||||||
FunctionArgs => PrimOpPhase::FunctionArgs,
|
|
||||||
GenList => PrimOpPhase::GenList,
|
|
||||||
GenericClosure => PrimOpPhase::GenericClosure,
|
|
||||||
GetAttr => PrimOpPhase::GetAttr,
|
|
||||||
GetContext => PrimOpPhase::GetContext,
|
|
||||||
GetEnv => PrimOpPhase::GetEnv,
|
|
||||||
GroupBy => PrimOpPhase::GroupBy,
|
|
||||||
HasAttr => PrimOpPhase::HasAttr,
|
|
||||||
HasContext => PrimOpPhase::HasContext,
|
|
||||||
HashFile => PrimOpPhase::HashFile,
|
|
||||||
HashString => PrimOpPhase::HashString,
|
|
||||||
Head => PrimOpPhase::Head,
|
|
||||||
Import => PrimOpPhase::Import,
|
|
||||||
IntersectAttrs => PrimOpPhase::IntersectAttrs,
|
|
||||||
IsAttrs => PrimOpPhase::IsAttrs,
|
|
||||||
IsBool => PrimOpPhase::IsBool,
|
|
||||||
IsFloat => PrimOpPhase::IsFloat,
|
|
||||||
IsFunction => PrimOpPhase::IsFunction,
|
|
||||||
IsInt => PrimOpPhase::IsInt,
|
|
||||||
IsList => PrimOpPhase::IsList,
|
|
||||||
IsNull => PrimOpPhase::IsNull,
|
|
||||||
IsPath => PrimOpPhase::IsPath,
|
|
||||||
IsString => PrimOpPhase::IsString,
|
|
||||||
Length => PrimOpPhase::Length,
|
|
||||||
LessThan => PrimOpPhase::LessThan,
|
|
||||||
ListToAttrs => PrimOpPhase::ListToAttrs,
|
|
||||||
Map => PrimOpPhase::Map,
|
|
||||||
MapAttrs => PrimOpPhase::MapAttrs,
|
|
||||||
Match => PrimOpPhase::Match,
|
|
||||||
Mul => PrimOpPhase::Mul,
|
|
||||||
ParseDrvName => PrimOpPhase::ParseDrvName,
|
|
||||||
Partition => PrimOpPhase::Partition,
|
|
||||||
Path => PrimOpPhase::Path,
|
|
||||||
PathExists => PrimOpPhase::PathExists,
|
|
||||||
Placeholder => PrimOpPhase::Placeholder,
|
|
||||||
ReadDir => PrimOpPhase::ReadDir,
|
|
||||||
ReadFile => PrimOpPhase::ReadFile,
|
|
||||||
ReadFileType => PrimOpPhase::ReadFileType,
|
|
||||||
RemoveAttrs => PrimOpPhase::RemoveAttrs,
|
|
||||||
ReplaceStrings => PrimOpPhase::ReplaceStrings,
|
|
||||||
ScopedImport => PrimOpPhase::ScopedImport,
|
|
||||||
Seq => PrimOpPhase::Seq,
|
|
||||||
Sort => PrimOpPhase::Sort,
|
|
||||||
Split => PrimOpPhase::Split,
|
|
||||||
SplitVersion => PrimOpPhase::SplitVersion,
|
|
||||||
StorePath => PrimOpPhase::StorePath,
|
|
||||||
StringLength => PrimOpPhase::StringLength,
|
|
||||||
Sub => PrimOpPhase::Sub,
|
|
||||||
Substring => PrimOpPhase::Substring,
|
|
||||||
Tail => PrimOpPhase::Tail,
|
|
||||||
Throw => PrimOpPhase::Throw,
|
|
||||||
ToFile => PrimOpPhase::ToFile,
|
|
||||||
ToJSON => PrimOpPhase::ToJSON,
|
|
||||||
ToPath => PrimOpPhase::ToPath,
|
|
||||||
ToString => PrimOpPhase::ToString,
|
|
||||||
ToXML => PrimOpPhase::ToXML,
|
|
||||||
Trace => PrimOpPhase::Trace,
|
|
||||||
TryEval => PrimOpPhase::TryEval,
|
|
||||||
TypeOf => PrimOpPhase::TypeOf,
|
|
||||||
UnsafeDiscardStringContext => PrimOpPhase::UnsafeDiscardStringContext,
|
|
||||||
UnsafeDiscardOutputDependency => PrimOpPhase::UnsafeDiscardOutputDependency,
|
|
||||||
UnsafeGetAttrPos => PrimOpPhase::UnsafeGetAttrPos,
|
|
||||||
Warn => PrimOpPhase::Warn,
|
|
||||||
ZipAttrsWith => PrimOpPhase::ZipAttrsWith,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrimOpPhase {
|
|
||||||
pub fn ip(self) -> u32 {
|
|
||||||
self as u32 * 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "fix-bytecode"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
colored = "3.1.1"
|
||||||
|
likely_stable = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
|
string-interner = { workspace = true }
|
||||||
|
|
||||||
|
fix-lang = { path = "../fix-lang" }
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use fix_builtins::BuiltinId;
|
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::{InstructionPtr, Op, OperandType};
|
use crate::{InstructionPtr, Op, OperandType, PrimOpPhase};
|
||||||
|
|
||||||
pub trait DisassemblerContext {
|
pub trait DisassemblerContext {
|
||||||
fn resolve_string(&self, id: u32) -> &str;
|
fn resolve_string(&self, id: u32) -> &str;
|
||||||
@@ -297,8 +296,8 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
("Call", "arg=?".into())
|
("Call", "arg=?".into())
|
||||||
}
|
}
|
||||||
Op::DispatchPrimOp => {
|
Op::DispatchPrimOp => {
|
||||||
let id = BuiltinId::try_from_primitive(self.read_u8()).expect("invalid builtin id");
|
let phase = PrimOpPhase::try_from(self.read_u8()).expect("invalid primop phase");
|
||||||
("DispatchPrimOp", format!("id={id:?}"))
|
("DispatchPrimOp", format!("phase={phase:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::MakeAttrs => {
|
Op::MakeAttrs => {
|
||||||
@@ -0,0 +1,531 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use fix_lang::{BuiltinId, StringId};
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use string_interner::Symbol as _;
|
||||||
|
|
||||||
|
pub mod disassembler;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct InstructionPtr(pub usize);
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
pub enum Op {
|
||||||
|
PushSmi,
|
||||||
|
PushBigInt,
|
||||||
|
PushFloat,
|
||||||
|
PushString,
|
||||||
|
PushNull,
|
||||||
|
PushTrue,
|
||||||
|
PushFalse,
|
||||||
|
|
||||||
|
LoadLocal,
|
||||||
|
LoadOuter,
|
||||||
|
StoreLocal,
|
||||||
|
AllocLocals,
|
||||||
|
|
||||||
|
MakeThunk,
|
||||||
|
MakeClosure,
|
||||||
|
MakePatternClosure,
|
||||||
|
|
||||||
|
Call,
|
||||||
|
DispatchPrimOp,
|
||||||
|
|
||||||
|
MakeAttrs,
|
||||||
|
MakeEmptyAttrs,
|
||||||
|
SelectStatic,
|
||||||
|
SelectDynamic,
|
||||||
|
HasAttrPathStatic,
|
||||||
|
HasAttrPathDynamic,
|
||||||
|
HasAttrStatic,
|
||||||
|
HasAttrDynamic,
|
||||||
|
HasAttrResolve,
|
||||||
|
JumpIfSelectSucceeded,
|
||||||
|
JumpIfSelectFailed,
|
||||||
|
|
||||||
|
MakeList,
|
||||||
|
MakeEmptyList,
|
||||||
|
|
||||||
|
OpAdd,
|
||||||
|
OpSub,
|
||||||
|
OpMul,
|
||||||
|
OpDiv,
|
||||||
|
OpEq,
|
||||||
|
OpNeq,
|
||||||
|
OpLt,
|
||||||
|
OpGt,
|
||||||
|
OpLeq,
|
||||||
|
OpGeq,
|
||||||
|
OpConcat,
|
||||||
|
OpUpdate,
|
||||||
|
|
||||||
|
OpNeg,
|
||||||
|
OpNot,
|
||||||
|
|
||||||
|
JumpIfFalse,
|
||||||
|
JumpIfTrue,
|
||||||
|
Jump,
|
||||||
|
|
||||||
|
CoerceToString,
|
||||||
|
|
||||||
|
ConcatStrings,
|
||||||
|
ResolvePath,
|
||||||
|
|
||||||
|
Assert,
|
||||||
|
|
||||||
|
LookupWith,
|
||||||
|
|
||||||
|
LoadBuiltins,
|
||||||
|
LoadBuiltin,
|
||||||
|
|
||||||
|
LoadReplBinding,
|
||||||
|
LoadScopedBinding,
|
||||||
|
|
||||||
|
Return,
|
||||||
|
|
||||||
|
Illegal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
|
pub enum OperandType {
|
||||||
|
Const,
|
||||||
|
BigInt,
|
||||||
|
Local,
|
||||||
|
BuiltinConst,
|
||||||
|
Builtins,
|
||||||
|
ReplBinding,
|
||||||
|
ScopedImportBinding,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Const {
|
||||||
|
Smi(i32),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
String(StringId),
|
||||||
|
Path(StringId),
|
||||||
|
PrimOp {
|
||||||
|
id: BuiltinId,
|
||||||
|
arity: u8,
|
||||||
|
dispatch_ip: u32,
|
||||||
|
},
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
|
pub enum AttrKeyType {
|
||||||
|
Static,
|
||||||
|
Dynamic,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum OperandData {
|
||||||
|
Const(u32),
|
||||||
|
BigInt(i64),
|
||||||
|
Local { layer: u8, idx: u32 },
|
||||||
|
BuiltinConst(StringId),
|
||||||
|
Builtins,
|
||||||
|
ReplBinding(StringId),
|
||||||
|
ScopedImportBinding { slot_id: u32, name: StringId },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
pub enum PrimOpPhase {
|
||||||
|
Abort,
|
||||||
|
Add,
|
||||||
|
AddErrorContext,
|
||||||
|
|
||||||
|
All,
|
||||||
|
AllCallPred,
|
||||||
|
AllCheck,
|
||||||
|
|
||||||
|
Any,
|
||||||
|
AnyCallPred,
|
||||||
|
AnyCheck,
|
||||||
|
|
||||||
|
AppendContext,
|
||||||
|
AttrNames,
|
||||||
|
AttrValues,
|
||||||
|
BaseNameOf,
|
||||||
|
BitAnd,
|
||||||
|
BitOr,
|
||||||
|
BitXor,
|
||||||
|
Break,
|
||||||
|
CatAttrs,
|
||||||
|
Ceil,
|
||||||
|
CompareVersions,
|
||||||
|
ConcatLists,
|
||||||
|
ConcatMap,
|
||||||
|
ConcatStringsSep,
|
||||||
|
ConvertHash,
|
||||||
|
|
||||||
|
DeepSeq,
|
||||||
|
DeepSeqPush,
|
||||||
|
DeepSeqLoop,
|
||||||
|
|
||||||
|
Derivation,
|
||||||
|
DerivationStrict,
|
||||||
|
DirOf,
|
||||||
|
Div,
|
||||||
|
Elem,
|
||||||
|
ElemAt,
|
||||||
|
FetchGit,
|
||||||
|
FetchMercurial,
|
||||||
|
FetchTarball,
|
||||||
|
FetchTree,
|
||||||
|
FetchUrl,
|
||||||
|
|
||||||
|
FilterForceList,
|
||||||
|
FilterCallPred,
|
||||||
|
FilterCheck,
|
||||||
|
|
||||||
|
FilterSource,
|
||||||
|
FindFile,
|
||||||
|
Floor,
|
||||||
|
FoldlStrict,
|
||||||
|
FoldlStrictEmpty,
|
||||||
|
FoldlStrictCall1,
|
||||||
|
FoldlStrictCall2,
|
||||||
|
FoldlStrictUpdate,
|
||||||
|
FromJSON,
|
||||||
|
FromTOML,
|
||||||
|
FunctionArgs,
|
||||||
|
GenList,
|
||||||
|
GenericClosure,
|
||||||
|
GetAttr,
|
||||||
|
GetContext,
|
||||||
|
GetEnv,
|
||||||
|
GroupBy,
|
||||||
|
HasAttr,
|
||||||
|
HasContext,
|
||||||
|
HashFile,
|
||||||
|
HashString,
|
||||||
|
Head,
|
||||||
|
Import,
|
||||||
|
IntersectAttrs,
|
||||||
|
IsAttrs,
|
||||||
|
IsBool,
|
||||||
|
IsFloat,
|
||||||
|
IsFunction,
|
||||||
|
IsInt,
|
||||||
|
IsList,
|
||||||
|
IsNull,
|
||||||
|
IsPath,
|
||||||
|
IsString,
|
||||||
|
Length,
|
||||||
|
LessThan,
|
||||||
|
ListToAttrs,
|
||||||
|
Map,
|
||||||
|
MapAttrs,
|
||||||
|
Match,
|
||||||
|
Mul,
|
||||||
|
ParseDrvName,
|
||||||
|
Partition,
|
||||||
|
Path,
|
||||||
|
PathExists,
|
||||||
|
Placeholder,
|
||||||
|
ReadDir,
|
||||||
|
ReadFile,
|
||||||
|
ReadFileType,
|
||||||
|
RemoveAttrs,
|
||||||
|
ReplaceStrings,
|
||||||
|
ScopedImport,
|
||||||
|
Seq,
|
||||||
|
Sort,
|
||||||
|
Split,
|
||||||
|
SplitVersion,
|
||||||
|
StorePath,
|
||||||
|
StringLength,
|
||||||
|
Sub,
|
||||||
|
Substring,
|
||||||
|
Tail,
|
||||||
|
Throw,
|
||||||
|
ToFile,
|
||||||
|
ToJSON,
|
||||||
|
ToPath,
|
||||||
|
ToString,
|
||||||
|
ToXML,
|
||||||
|
Trace,
|
||||||
|
TryEval,
|
||||||
|
TypeOf,
|
||||||
|
UnsafeDiscardStringContext,
|
||||||
|
UnsafeGetAttrPos,
|
||||||
|
Warn,
|
||||||
|
ZipAttrsWith,
|
||||||
|
|
||||||
|
ForceResultShallow,
|
||||||
|
ForceResultShallowPush,
|
||||||
|
ForceResultShallowLoop,
|
||||||
|
ForceResultDeepFinish,
|
||||||
|
|
||||||
|
EqStep,
|
||||||
|
EqForce,
|
||||||
|
|
||||||
|
CallPattern,
|
||||||
|
CallFunctor1,
|
||||||
|
CallFunctor2,
|
||||||
|
|
||||||
|
ImportFinalize,
|
||||||
|
ScopedImportFinalize,
|
||||||
|
|
||||||
|
AppendContextLoop,
|
||||||
|
AppendContextEntryForced,
|
||||||
|
AppendContextOutputsForced,
|
||||||
|
AppendContextOutputElementLoop,
|
||||||
|
AppendContextOutputElementForced,
|
||||||
|
|
||||||
|
UnsafeDiscardOutputDependency,
|
||||||
|
|
||||||
|
Illegal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for PrimOpPhase {
|
||||||
|
type Error = u8;
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
if (0..Self::Illegal as u8).contains(&value) {
|
||||||
|
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
|
||||||
|
} else {
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimOpPhase {
|
||||||
|
pub fn entry_for_builtin(id: BuiltinId) -> Self {
|
||||||
|
use BuiltinId::*;
|
||||||
|
match id {
|
||||||
|
Abort => Self::Abort,
|
||||||
|
Add => Self::Add,
|
||||||
|
AddErrorContext => Self::AddErrorContext,
|
||||||
|
All => Self::All,
|
||||||
|
Any => Self::Any,
|
||||||
|
AppendContext => Self::AppendContext,
|
||||||
|
AttrNames => Self::AttrNames,
|
||||||
|
AttrValues => Self::AttrValues,
|
||||||
|
BaseNameOf => Self::BaseNameOf,
|
||||||
|
BitAnd => Self::BitAnd,
|
||||||
|
BitOr => Self::BitOr,
|
||||||
|
BitXor => Self::BitXor,
|
||||||
|
Break => Self::Break,
|
||||||
|
CatAttrs => Self::CatAttrs,
|
||||||
|
Ceil => Self::Ceil,
|
||||||
|
CompareVersions => Self::CompareVersions,
|
||||||
|
ConcatLists => Self::ConcatLists,
|
||||||
|
ConcatMap => Self::ConcatMap,
|
||||||
|
ConcatStringsSep => Self::ConcatStringsSep,
|
||||||
|
ConvertHash => Self::ConvertHash,
|
||||||
|
DeepSeq => Self::DeepSeq,
|
||||||
|
Derivation => Self::Derivation,
|
||||||
|
DerivationStrict => Self::DerivationStrict,
|
||||||
|
DirOf => Self::DirOf,
|
||||||
|
Div => Self::Div,
|
||||||
|
Elem => Self::Elem,
|
||||||
|
ElemAt => Self::ElemAt,
|
||||||
|
FetchGit => Self::FetchGit,
|
||||||
|
FetchMercurial => Self::FetchMercurial,
|
||||||
|
FetchTarball => Self::FetchTarball,
|
||||||
|
FetchTree => Self::FetchTree,
|
||||||
|
FetchUrl => Self::FetchUrl,
|
||||||
|
Filter => Self::FilterForceList,
|
||||||
|
FilterSource => Self::FilterSource,
|
||||||
|
FindFile => Self::FindFile,
|
||||||
|
Floor => Self::Floor,
|
||||||
|
FoldlStrict => Self::FoldlStrict,
|
||||||
|
FromJSON => Self::FromJSON,
|
||||||
|
FromTOML => Self::FromTOML,
|
||||||
|
FunctionArgs => Self::FunctionArgs,
|
||||||
|
GenList => Self::GenList,
|
||||||
|
GenericClosure => Self::GenericClosure,
|
||||||
|
GetAttr => Self::GetAttr,
|
||||||
|
GetContext => Self::GetContext,
|
||||||
|
GetEnv => Self::GetEnv,
|
||||||
|
GroupBy => Self::GroupBy,
|
||||||
|
HasAttr => Self::HasAttr,
|
||||||
|
HasContext => Self::HasContext,
|
||||||
|
HashFile => Self::HashFile,
|
||||||
|
HashString => Self::HashString,
|
||||||
|
Head => Self::Head,
|
||||||
|
Import => Self::Import,
|
||||||
|
IntersectAttrs => Self::IntersectAttrs,
|
||||||
|
IsAttrs => Self::IsAttrs,
|
||||||
|
IsBool => Self::IsBool,
|
||||||
|
IsFloat => Self::IsFloat,
|
||||||
|
IsFunction => Self::IsFunction,
|
||||||
|
IsInt => Self::IsInt,
|
||||||
|
IsList => Self::IsList,
|
||||||
|
IsNull => Self::IsNull,
|
||||||
|
IsPath => Self::IsPath,
|
||||||
|
IsString => Self::IsString,
|
||||||
|
Length => Self::Length,
|
||||||
|
LessThan => Self::LessThan,
|
||||||
|
ListToAttrs => Self::ListToAttrs,
|
||||||
|
Map => Self::Map,
|
||||||
|
MapAttrs => Self::MapAttrs,
|
||||||
|
Match => Self::Match,
|
||||||
|
Mul => Self::Mul,
|
||||||
|
ParseDrvName => Self::ParseDrvName,
|
||||||
|
Partition => Self::Partition,
|
||||||
|
Path => Self::Path,
|
||||||
|
PathExists => Self::PathExists,
|
||||||
|
Placeholder => Self::Placeholder,
|
||||||
|
ReadDir => Self::ReadDir,
|
||||||
|
ReadFile => Self::ReadFile,
|
||||||
|
ReadFileType => Self::ReadFileType,
|
||||||
|
RemoveAttrs => Self::RemoveAttrs,
|
||||||
|
ReplaceStrings => Self::ReplaceStrings,
|
||||||
|
ScopedImport => Self::ScopedImport,
|
||||||
|
Seq => Self::Seq,
|
||||||
|
Sort => Self::Sort,
|
||||||
|
Split => Self::Split,
|
||||||
|
SplitVersion => Self::SplitVersion,
|
||||||
|
StorePath => Self::StorePath,
|
||||||
|
StringLength => Self::StringLength,
|
||||||
|
Sub => Self::Sub,
|
||||||
|
Substring => Self::Substring,
|
||||||
|
Tail => Self::Tail,
|
||||||
|
Throw => Self::Throw,
|
||||||
|
ToFile => Self::ToFile,
|
||||||
|
ToJSON => Self::ToJSON,
|
||||||
|
ToPath => Self::ToPath,
|
||||||
|
ToString => Self::ToString,
|
||||||
|
ToXML => Self::ToXML,
|
||||||
|
Trace => Self::Trace,
|
||||||
|
TryEval => Self::TryEval,
|
||||||
|
TypeOf => Self::TypeOf,
|
||||||
|
UnsafeDiscardStringContext => Self::UnsafeDiscardStringContext,
|
||||||
|
UnsafeDiscardOutputDependency => Self::UnsafeDiscardOutputDependency,
|
||||||
|
UnsafeGetAttrPos => Self::UnsafeGetAttrPos,
|
||||||
|
Warn => Self::Warn,
|
||||||
|
ZipAttrsWith => Self::ZipAttrsWith,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ip(self) -> u32 {
|
||||||
|
self as u32 * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BytecodeReader<'a> {
|
||||||
|
bytecode: &'a [u8],
|
||||||
|
pc: usize,
|
||||||
|
inst_start_pc: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BytecodeReader<'a> {
|
||||||
|
pub fn new(bytecode: &'a [u8], pc: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
bytecode,
|
||||||
|
pc,
|
||||||
|
inst_start_pc: pc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
bytecode,
|
||||||
|
pc: inst_start_pc + 1,
|
||||||
|
inst_start_pc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg_attr(debug_assertions, track_caller)]
|
||||||
|
fn read_array<const N: usize>(&mut self) -> [u8; N] {
|
||||||
|
let ret = self.bytecode[self.pc..self.pc + N]
|
||||||
|
.try_into()
|
||||||
|
.expect("read_array failed");
|
||||||
|
self.pc += N;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_op(&mut self) -> Op {
|
||||||
|
self.inst_start_pc = self.pc;
|
||||||
|
let byte = self.bytecode[self.pc];
|
||||||
|
if !likely_stable::likely((0..Op::Illegal as u8).contains(&byte)) {
|
||||||
|
panic!("unknown opcode: {byte:#04x}")
|
||||||
|
}
|
||||||
|
self.pc += 1;
|
||||||
|
unsafe { std::mem::transmute::<u8, Op>(byte) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_u8(&mut self) -> u8 {
|
||||||
|
let val = self.bytecode[self.pc];
|
||||||
|
self.pc += 1;
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_u16(&mut self) -> u16 {
|
||||||
|
u16::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_u32(&mut self) -> u32 {
|
||||||
|
u32::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_i32(&mut self) -> i32 {
|
||||||
|
i32::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_i64(&mut self) -> i64 {
|
||||||
|
i64::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_f64(&mut self) -> f64 {
|
||||||
|
f64::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_string_id(&mut self) -> StringId {
|
||||||
|
let raw = self.read_u32();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
StringId(string_interner::symbol::SymbolU32::try_from_usize(raw as usize).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_operand_data(&mut self) -> OperandData {
|
||||||
|
let tag = self.read_u8();
|
||||||
|
let Ok(ty) = OperandType::try_from_primitive(tag)
|
||||||
|
.map_err(|err| panic!("unknown operand tag: {:#04x}", err.number));
|
||||||
|
match ty {
|
||||||
|
OperandType::Const => OperandData::Const(self.read_u32()),
|
||||||
|
OperandType::BigInt => OperandData::BigInt(self.read_i64()),
|
||||||
|
OperandType::Local => {
|
||||||
|
let layer = self.read_u8();
|
||||||
|
let idx = self.read_u32();
|
||||||
|
OperandData::Local { layer, idx }
|
||||||
|
}
|
||||||
|
OperandType::BuiltinConst => OperandData::BuiltinConst(self.read_string_id()),
|
||||||
|
OperandType::Builtins => OperandData::Builtins,
|
||||||
|
OperandType::ReplBinding => OperandData::ReplBinding(self.read_string_id()),
|
||||||
|
OperandType::ScopedImportBinding => {
|
||||||
|
let slot_id = self.read_u32();
|
||||||
|
let name = self.read_string_id();
|
||||||
|
OperandData::ScopedImportBinding { slot_id, name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pc(&self) -> usize {
|
||||||
|
self.pc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pc(&mut self, pc: usize) {
|
||||||
|
self.pc = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inst_start_pc(&self) -> usize {
|
||||||
|
self.inst_start_pc
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fix-codegen"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
hashbrown = { workspace = true }
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
rnix = { workspace = true }
|
|
||||||
string-interner = { workspace = true }
|
|
||||||
colored = "3.1.1"
|
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-ir = { path = "../fix-ir" }
|
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fix-ir"
|
name = "fix-compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bumpalo = { workspace = true }
|
bumpalo = { workspace = true }
|
||||||
ghost-cell = { workspace = true }
|
ghost-cell = { workspace = true }
|
||||||
|
hashbrown = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
rnix = { workspace = true }
|
rnix = { workspace = true }
|
||||||
rowan = { workspace = true }
|
rowan = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
colored = "3.1.1"
|
||||||
num_enum = { workspace = true }
|
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
|
fix-lang = { path = "../fix-lang" }
|
||||||
|
fix-runtime = { path = "../fix-runtime" }
|
||||||
|
tracing = "0.1"
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
use bumpalo::Bump;
|
||||||
|
use fix_bytecode::{Const, InstructionPtr, Op, PrimOpPhase};
|
||||||
|
use fix_error::{Error, Result, Source};
|
||||||
|
use fix_lang::{StringId, Symbol};
|
||||||
|
use fix_runtime::{StaticValue, VmCode, VmRuntimeCtx};
|
||||||
|
use ghost_cell::{GhostCell, GhostToken};
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
|
use crate::BytecodeContext;
|
||||||
|
use crate::ir::downgrade::{Downgrade as _, DowngradeContext};
|
||||||
|
use crate::ir::{
|
||||||
|
GhostMaybeThunkRef, GhostRoIrRef, GhostRoMaybeThunkRef, GhostRoRef, Ir, MaybeThunk, RawIrRef,
|
||||||
|
ThunkId,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CodeState {
|
||||||
|
pub bytecode: Vec<u8>,
|
||||||
|
pub sources: Vec<Source>,
|
||||||
|
pub spans: Vec<(usize, rnix::TextRange)>,
|
||||||
|
pub thunk_count: usize,
|
||||||
|
pub global_env: HashMap<StringId, MaybeThunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeState {
|
||||||
|
pub fn new(strings: &mut DefaultStringInterner) -> Self {
|
||||||
|
let global_env = crate::ir::new_global_env(strings);
|
||||||
|
let mut bytecode = Vec::with_capacity(PrimOpPhase::Illegal as usize * 2);
|
||||||
|
for phase in 0..=PrimOpPhase::Illegal as u8 {
|
||||||
|
bytecode.push(Op::DispatchPrimOp as u8);
|
||||||
|
bytecode.push(phase);
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
sources: Vec::new(),
|
||||||
|
spans: Vec::new(),
|
||||||
|
thunk_count: 0,
|
||||||
|
bytecode,
|
||||||
|
global_env,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_bytecode<'ctx>(
|
||||||
|
&'ctx mut self,
|
||||||
|
source: Source,
|
||||||
|
extra_scope: Option<ExtraScope<'ctx>>,
|
||||||
|
runtime: &'ctx mut impl VmRuntimeCtx,
|
||||||
|
) -> Result<InstructionPtr> {
|
||||||
|
let mut compiler = CompilerCtx {
|
||||||
|
code: self,
|
||||||
|
runtime,
|
||||||
|
};
|
||||||
|
compiler.compile_bytecode(source, extra_scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmCode for CodeState {
|
||||||
|
fn bytecode(&self) -> &[u8] {
|
||||||
|
&self.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_with_scope(
|
||||||
|
&mut self,
|
||||||
|
source: Source,
|
||||||
|
extra_scope: Option<fix_runtime::ExtraScope>,
|
||||||
|
runtime: &mut impl VmRuntimeCtx,
|
||||||
|
) -> Result<InstructionPtr> {
|
||||||
|
let extra = extra_scope.map(|s| match s {
|
||||||
|
fix_runtime::ExtraScope::ScopedImport { keys, slot_id } => {
|
||||||
|
ExtraScope::ScopedImport { keys, slot_id }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CodeState::compile_bytecode(self, source, extra, runtime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CompilerCtx<'a, R: VmRuntimeCtx> {
|
||||||
|
code: &'a mut CodeState,
|
||||||
|
runtime: &'a mut R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
||||||
|
fn compile_bytecode(
|
||||||
|
&mut self,
|
||||||
|
source: Source,
|
||||||
|
extra_scope: Option<ExtraScope>,
|
||||||
|
) -> Result<InstructionPtr> {
|
||||||
|
let root = self.downgrade(source, extra_scope)?;
|
||||||
|
let ip = crate::compile_bytecode(root.as_ref(), self);
|
||||||
|
Ok(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade(&mut self, source: Source, extra_scope: Option<ExtraScope>) -> Result<OwnedIr> {
|
||||||
|
tracing::debug!("Parsing Nix expression");
|
||||||
|
|
||||||
|
self.code.sources.push(source.clone());
|
||||||
|
|
||||||
|
let root = rnix::Root::parse(&source.src);
|
||||||
|
handle_parse_error(root.errors(), source.clone()).map_or(Ok(()), Err)?;
|
||||||
|
|
||||||
|
tracing::debug!("Downgrading Nix expression");
|
||||||
|
let expr = root
|
||||||
|
.tree()
|
||||||
|
.expr()
|
||||||
|
.ok_or_else(|| Error::parse_error("unexpected EOF".into()))?;
|
||||||
|
let bump = Bump::new();
|
||||||
|
GhostToken::new(|token| {
|
||||||
|
let downgrade_ctx = DowngradeCtx::new(
|
||||||
|
&bump,
|
||||||
|
token,
|
||||||
|
self.runtime,
|
||||||
|
&self.code.global_env,
|
||||||
|
extra_scope.map(Into::into),
|
||||||
|
&mut self.code.thunk_count,
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
let ir = downgrade_ctx.downgrade_toplevel(expr)?;
|
||||||
|
let ir = unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) };
|
||||||
|
Ok(OwnedIr { _bump: bump, ir })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R: VmRuntimeCtx> BytecodeContext for CompilerCtx<'a, R> {
|
||||||
|
fn intern_string(&mut self, s: &str) -> StringId {
|
||||||
|
self.runtime.intern_string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_span(&mut self, range: rnix::TextRange) -> u32 {
|
||||||
|
let id = self.code.spans.len();
|
||||||
|
let source_id = self
|
||||||
|
.code
|
||||||
|
.sources
|
||||||
|
.len()
|
||||||
|
.checked_sub(1)
|
||||||
|
.expect("current_source not set");
|
||||||
|
self.code.spans.push((source_id, range));
|
||||||
|
id as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code(&self) -> &[u8] {
|
||||||
|
&self.code.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.code.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_constant(&mut self, val: Const) -> u32 {
|
||||||
|
use Const::*;
|
||||||
|
let val = match val {
|
||||||
|
Smi(x) => StaticValue::new_inline(x),
|
||||||
|
Float(x) => StaticValue::new_float(x),
|
||||||
|
Bool(x) => StaticValue::new_inline(x),
|
||||||
|
String(x) => StaticValue::new_inline(x),
|
||||||
|
Path(x) => StaticValue::new_inline(fix_runtime::Path(x)),
|
||||||
|
PrimOp {
|
||||||
|
id,
|
||||||
|
arity,
|
||||||
|
dispatch_ip,
|
||||||
|
} => StaticValue::new_primop(id, arity, dispatch_ip),
|
||||||
|
Null => StaticValue::default(),
|
||||||
|
};
|
||||||
|
self.runtime.add_const(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_source_dir(&mut self) -> StringId {
|
||||||
|
let dir = self
|
||||||
|
.code
|
||||||
|
.sources
|
||||||
|
.last()
|
||||||
|
.expect("current_source not set")
|
||||||
|
.get_dir()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
self.runtime.intern_string(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_error_span(error: &rnix::ParseError) -> Option<rnix::TextRange> {
|
||||||
|
use rnix::ParseError::*;
|
||||||
|
match error {
|
||||||
|
Unexpected(range)
|
||||||
|
| UnexpectedExtra(range)
|
||||||
|
| UnexpectedWanted(_, range, _)
|
||||||
|
| UnexpectedDoubleBind(range)
|
||||||
|
| DuplicatedArgs(range, _) => Some(*range),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_parse_error<'a>(
|
||||||
|
errors: impl IntoIterator<Item = &'a rnix::ParseError>,
|
||||||
|
source: Source,
|
||||||
|
) -> Option<Box<Error>> {
|
||||||
|
for err in errors {
|
||||||
|
if let Some(span) = parse_error_span(err) {
|
||||||
|
return Some(
|
||||||
|
Error::parse_error(err.to_string())
|
||||||
|
.with_source(source)
|
||||||
|
.with_span(span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DowngradeCtx<'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
||||||
|
bump: &'ir Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
runtime: &'ctx mut R,
|
||||||
|
source: Source,
|
||||||
|
scopes: Vec<Scope<'ctx, 'id, 'ir>>,
|
||||||
|
with_stack: Vec<GhostRoMaybeThunkRef<'id, 'ir>>,
|
||||||
|
thunk_count: &'ctx mut usize,
|
||||||
|
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
|
fn new(
|
||||||
|
bump: &'ir Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
runtime: &'ctx mut R,
|
||||||
|
global: &'ctx HashMap<StringId, MaybeThunk>,
|
||||||
|
extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
|
||||||
|
thunk_count: &'ctx mut usize,
|
||||||
|
source: Source,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
bump,
|
||||||
|
token,
|
||||||
|
runtime,
|
||||||
|
source,
|
||||||
|
scopes: std::iter::once(Scope::Global(global))
|
||||||
|
.chain(extra_scope)
|
||||||
|
.collect(),
|
||||||
|
thunk_count,
|
||||||
|
with_stack: Vec::new(),
|
||||||
|
thunk_scopes: vec![ThunkScope::new_in(bump)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
||||||
|
for DowngradeCtx<'ctx, 'id, 'ir, R>
|
||||||
|
{
|
||||||
|
fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir> {
|
||||||
|
self.bump.alloc(GhostCell::new(expr).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir> {
|
||||||
|
use MaybeThunk::*;
|
||||||
|
let expr = (|| {
|
||||||
|
let expr = match *ir.borrow(&self.token) {
|
||||||
|
Ir::Builtin(x) => Builtin(x),
|
||||||
|
Ir::Int(x) => Int(x),
|
||||||
|
Ir::Float(x) => Float(x),
|
||||||
|
Ir::Bool(x) => Bool(x),
|
||||||
|
Ir::Str(x) => Str(x),
|
||||||
|
Ir::Arg { layer } => Arg { layer },
|
||||||
|
Ir::Builtins => Builtins,
|
||||||
|
Ir::Null => Null,
|
||||||
|
Ir::MaybeThunk(thunk) => return Some(thunk),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(self.bump.alloc(GhostCell::new(expr).into()))
|
||||||
|
})();
|
||||||
|
if let Some(thunk) = expr {
|
||||||
|
return thunk;
|
||||||
|
}
|
||||||
|
let id = ThunkId(*self.thunk_count);
|
||||||
|
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
||||||
|
self.thunk_scopes
|
||||||
|
.last_mut()
|
||||||
|
.expect("no active cache scope")
|
||||||
|
.add_binding(id, ir);
|
||||||
|
self.bump.alloc(GhostCell::new(Thunk(id)).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
|
||||||
|
self.runtime.intern_string(sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_sym(&self, id: StringId) -> Symbol<'_> {
|
||||||
|
self.runtime.resolve_string(id).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(
|
||||||
|
&mut self,
|
||||||
|
sym: StringId,
|
||||||
|
span: rnix::TextRange,
|
||||||
|
) -> Result<GhostRoMaybeThunkRef<'id, 'ir>> {
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
match scope {
|
||||||
|
&Scope::Global(global_scope) => {
|
||||||
|
if let Some(expr) = global_scope.get(&sym) {
|
||||||
|
return Ok(expr.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::Repl(repl_bindings) => {
|
||||||
|
if repl_bindings.contains(&sym) {
|
||||||
|
return Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)).into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::ScopedImport { ref keys, slot_id } => {
|
||||||
|
if keys.contains(&sym) {
|
||||||
|
return Ok(self.bump.alloc(
|
||||||
|
GhostCell::new(MaybeThunk::ScopedImportBinding { sym, slot_id }).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::Let(let_scope) => {
|
||||||
|
if let Some(&expr) = let_scope.get(&sym) {
|
||||||
|
return Ok(expr.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::Param {
|
||||||
|
sym: param_sym,
|
||||||
|
abs_layer,
|
||||||
|
} => {
|
||||||
|
if param_sym == sym {
|
||||||
|
let layers: u8 =
|
||||||
|
self.thunk_scopes.len().try_into().expect("scope too deep!");
|
||||||
|
let layer = layers - abs_layer;
|
||||||
|
return Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::Arg { layer }).into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.with_stack.is_empty() {
|
||||||
|
let id = ThunkId(*self.thunk_count);
|
||||||
|
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
||||||
|
let mut namespaces =
|
||||||
|
bumpalo::collections::Vec::with_capacity_in(self.with_stack.len(), self.bump);
|
||||||
|
namespaces.extend(self.with_stack.iter().rev().copied());
|
||||||
|
let body = self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(Ir::WithLookup { sym, namespaces }).into());
|
||||||
|
self.thunk_scopes
|
||||||
|
.last_mut()
|
||||||
|
.expect("no active thunk scope")
|
||||||
|
.add_binding(id, body);
|
||||||
|
Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::Thunk(id)).into()))
|
||||||
|
} else {
|
||||||
|
Err(Error::downgrade_error(
|
||||||
|
format!("'{}' not found", self.resolve_sym(sym)),
|
||||||
|
self.get_current_source(),
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_source(&self) -> Source {
|
||||||
|
self.source.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
&mut Self,
|
||||||
|
) -> Result<(
|
||||||
|
bumpalo::collections::Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>,
|
||||||
|
Ret,
|
||||||
|
)>,
|
||||||
|
{
|
||||||
|
let base = *self.thunk_count;
|
||||||
|
*self.thunk_count = self
|
||||||
|
.thunk_count
|
||||||
|
.checked_add(keys.len())
|
||||||
|
.expect("thunk id overflow");
|
||||||
|
let handles = (base..base + keys.len())
|
||||||
|
.map(|id| {
|
||||||
|
&*self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(id))))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let scope = keys.iter().copied().zip(handles.iter().copied()).collect();
|
||||||
|
self.scopes.push(Scope::Let(scope));
|
||||||
|
let (vals, ret) = { f(self)? };
|
||||||
|
self.scopes.pop();
|
||||||
|
assert_eq!(keys.len(), vals.len());
|
||||||
|
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
|
||||||
|
for (i, (val, handle)) in vals.into_iter().zip(handles).enumerate() {
|
||||||
|
let thunk = *val.borrow(&self.token);
|
||||||
|
*handle.borrow_mut(&mut self.token) = thunk;
|
||||||
|
let id = ThunkId(base + i);
|
||||||
|
let ir_ref = self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(Ir::MaybeThunk(handle.into())).into());
|
||||||
|
scope.add_binding(id, ir_ref);
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_param_scope<F, Ret>(&mut self, sym: StringId, f: F) -> Ret
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Ret,
|
||||||
|
{
|
||||||
|
self.scopes.push(Scope::Param {
|
||||||
|
sym,
|
||||||
|
abs_layer: self.thunk_scopes.len().try_into().expect("scope too deep!"),
|
||||||
|
});
|
||||||
|
let mut guard = ScopeGuard { ctx: self };
|
||||||
|
f(guard.as_ctx())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_with_scope<F, Ret>(&mut self, namespace: GhostRoMaybeThunkRef<'id, 'ir>, f: F) -> Ret
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Ret,
|
||||||
|
{
|
||||||
|
self.with_stack.push(namespace);
|
||||||
|
let ret = f(self);
|
||||||
|
self.with_stack.pop();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_thunk_scope<F, Ret>(
|
||||||
|
&mut self,
|
||||||
|
f: F,
|
||||||
|
) -> (
|
||||||
|
Ret,
|
||||||
|
bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Ret,
|
||||||
|
{
|
||||||
|
if self.thunk_scopes.len() == u8::MAX as usize {
|
||||||
|
panic!("scope too deep!");
|
||||||
|
}
|
||||||
|
self.thunk_scopes.push(ThunkScope::new_in(self.bump));
|
||||||
|
let ret = f(self);
|
||||||
|
(
|
||||||
|
ret,
|
||||||
|
self.thunk_scopes
|
||||||
|
.pop()
|
||||||
|
.expect("no thunk scope left???")
|
||||||
|
.bindings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bump(&self) -> &'ir bumpalo::Bump {
|
||||||
|
self.bump
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
|
fn downgrade_toplevel(mut self, root: rnix::ast::Expr) -> Result<RawIrRef<'ir>> {
|
||||||
|
let body = root.downgrade(&mut self)?;
|
||||||
|
let thunks = self
|
||||||
|
.thunk_scopes
|
||||||
|
.pop()
|
||||||
|
.expect("no thunk scope left???")
|
||||||
|
.bindings;
|
||||||
|
Ok(Ir::freeze(
|
||||||
|
self.new_expr(Ir::TopLevel { body, thunks }),
|
||||||
|
self.token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ThunkScope<'id, 'ir> {
|
||||||
|
bindings: bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'id, 'ir> ThunkScope<'id, 'ir> {
|
||||||
|
fn new_in(bump: &'ir Bump) -> Self {
|
||||||
|
Self {
|
||||||
|
bindings: bumpalo::collections::Vec::new_in(bump),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_binding(&mut self, id: ThunkId, ir: GhostRoIrRef<'id, 'ir>) {
|
||||||
|
self.bindings.push((id, ir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Scope<'ctx, 'id, 'ir> {
|
||||||
|
Global(&'ctx HashMap<StringId, MaybeThunk>),
|
||||||
|
Repl(&'ctx HashSet<StringId>),
|
||||||
|
ScopedImport {
|
||||||
|
keys: HashSet<StringId>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
slot_id: u32,
|
||||||
|
},
|
||||||
|
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),
|
||||||
|
Param {
|
||||||
|
sym: StringId,
|
||||||
|
abs_layer: u8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ExtraScope<'ctx> {
|
||||||
|
Repl(&'ctx HashSet<StringId>),
|
||||||
|
ScopedImport {
|
||||||
|
keys: HashSet<StringId>,
|
||||||
|
slot_id: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> From<ExtraScope<'ctx>> for Scope<'ctx, '_, '_> {
|
||||||
|
fn from(value: ExtraScope<'ctx>) -> Self {
|
||||||
|
use ExtraScope::*;
|
||||||
|
match value {
|
||||||
|
ScopedImport { keys, slot_id } => Scope::ScopedImport { keys, slot_id },
|
||||||
|
Repl(scope) => Scope::Repl(scope),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScopeGuard<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
||||||
|
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir, R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> Drop for ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.ctx.scopes.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
||||||
|
fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OwnedIr {
|
||||||
|
_bump: Bump,
|
||||||
|
ir: RawIrRef<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedIr {
|
||||||
|
fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
||||||
|
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,7 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use fix_builtins::{BUILTINS, BuiltinId};
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
use fix_common::StringId;
|
|
||||||
use ghost_cell::{GhostCell, GhostToken};
|
use ghost_cell::{GhostCell, GhostToken};
|
||||||
use num_enum::TryFromPrimitive as _;
|
use num_enum::TryFromPrimitive as _;
|
||||||
use rnix::{TextRange, ast};
|
use rnix::{TextRange, ast};
|
||||||
@@ -92,7 +91,7 @@ pub enum MaybeThunk {
|
|||||||
BuiltinConst(StringId),
|
BuiltinConst(StringId),
|
||||||
Builtins,
|
Builtins,
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding { slot_id: u32, sym: StringId },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Ref<'ir> {
|
pub trait Ref<'ir> {
|
||||||
@@ -213,17 +212,16 @@ pub enum Ir<'ir, R: RefExt<'ir> + ?Sized + 'ir> {
|
|||||||
},
|
},
|
||||||
MaybeThunk(R::MaybeThunkRef),
|
MaybeThunk(R::MaybeThunkRef),
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding {
|
||||||
|
sym: StringId,
|
||||||
|
slot_id: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct ThunkId(pub usize);
|
pub struct ThunkId(pub usize);
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
pub struct SpanId(pub u32);
|
|
||||||
|
|
||||||
/// Represents a key in an attribute path.
|
/// Represents a key in an attribute path.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use bumpalo::collections::{CollectIn, Vec};
|
use bumpalo::collections::{CollectIn, Vec};
|
||||||
use fix_builtins::BuiltinId;
|
|
||||||
use fix_common::Symbol;
|
|
||||||
use fix_error::{Error, Result, Source};
|
use fix_error::{Error, Result, Source};
|
||||||
|
use fix_lang::{BuiltinId, Symbol};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use rnix::TextRange;
|
use rnix::TextRange;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
use fix_builtins::{BUILTINS, BuiltinId};
|
use fix_bytecode::{Const, InstructionPtr, Op, OperandType, PrimOpPhase};
|
||||||
use fix_common::StringId;
|
use fix_lang::{BUILTINS, StringId};
|
||||||
use fix_ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use rnix::TextRange;
|
use rnix::TextRange;
|
||||||
use string_interner::Symbol as _;
|
use string_interner::Symbol as _;
|
||||||
|
|
||||||
pub mod disassembler;
|
mod context;
|
||||||
|
pub mod ir;
|
||||||
pub struct InstructionPtr(pub usize);
|
pub use context::{CodeState, ExtraScope};
|
||||||
|
pub use fix_bytecode::disassembler;
|
||||||
|
pub use ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
|
||||||
|
|
||||||
pub trait BytecodeContext {
|
pub trait BytecodeContext {
|
||||||
fn intern_string(&mut self, s: &str) -> StringId;
|
fn intern_string(&mut self, s: &str) -> StringId;
|
||||||
@@ -17,86 +17,6 @@ pub trait BytecodeContext {
|
|||||||
fn get_code_mut(&mut self) -> &mut Vec<u8>;
|
fn get_code_mut(&mut self) -> &mut Vec<u8>;
|
||||||
fn add_constant(&mut self, val: Const) -> u32;
|
fn add_constant(&mut self, val: Const) -> u32;
|
||||||
fn current_source_dir(&mut self) -> StringId;
|
fn current_source_dir(&mut self) -> StringId;
|
||||||
fn current_scope_slot(&self) -> Option<u32>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
pub enum Op {
|
|
||||||
PushSmi,
|
|
||||||
PushBigInt,
|
|
||||||
PushFloat,
|
|
||||||
PushString,
|
|
||||||
PushNull,
|
|
||||||
PushTrue,
|
|
||||||
PushFalse,
|
|
||||||
|
|
||||||
LoadLocal,
|
|
||||||
LoadOuter,
|
|
||||||
StoreLocal,
|
|
||||||
AllocLocals,
|
|
||||||
|
|
||||||
MakeThunk,
|
|
||||||
MakeClosure,
|
|
||||||
MakePatternClosure,
|
|
||||||
|
|
||||||
Call,
|
|
||||||
DispatchPrimOp,
|
|
||||||
|
|
||||||
MakeAttrs,
|
|
||||||
MakeEmptyAttrs,
|
|
||||||
SelectStatic,
|
|
||||||
SelectDynamic,
|
|
||||||
HasAttrPathStatic,
|
|
||||||
HasAttrPathDynamic,
|
|
||||||
HasAttrStatic,
|
|
||||||
HasAttrDynamic,
|
|
||||||
HasAttrResolve,
|
|
||||||
JumpIfSelectSucceeded,
|
|
||||||
JumpIfSelectFailed,
|
|
||||||
|
|
||||||
MakeList,
|
|
||||||
MakeEmptyList,
|
|
||||||
|
|
||||||
OpAdd,
|
|
||||||
OpSub,
|
|
||||||
OpMul,
|
|
||||||
OpDiv,
|
|
||||||
OpEq,
|
|
||||||
OpNeq,
|
|
||||||
OpLt,
|
|
||||||
OpGt,
|
|
||||||
OpLeq,
|
|
||||||
OpGeq,
|
|
||||||
OpConcat,
|
|
||||||
OpUpdate,
|
|
||||||
|
|
||||||
OpNeg,
|
|
||||||
OpNot,
|
|
||||||
|
|
||||||
JumpIfFalse,
|
|
||||||
JumpIfTrue,
|
|
||||||
Jump,
|
|
||||||
|
|
||||||
CoerceToString,
|
|
||||||
|
|
||||||
ConcatStrings,
|
|
||||||
ResolvePath,
|
|
||||||
|
|
||||||
Assert,
|
|
||||||
|
|
||||||
LookupWith,
|
|
||||||
|
|
||||||
LoadBuiltins,
|
|
||||||
LoadBuiltin,
|
|
||||||
|
|
||||||
LoadReplBinding,
|
|
||||||
LoadScopedBinding,
|
|
||||||
|
|
||||||
Return,
|
|
||||||
|
|
||||||
Illegal,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScopeInfo {
|
struct ScopeInfo {
|
||||||
@@ -109,39 +29,6 @@ struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
|
|||||||
scope_stack: Vec<ScopeInfo>,
|
scope_stack: Vec<ScopeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
|
||||||
pub enum OperandType {
|
|
||||||
Const,
|
|
||||||
BigInt,
|
|
||||||
Local,
|
|
||||||
BuiltinConst,
|
|
||||||
Builtins,
|
|
||||||
ReplBinding,
|
|
||||||
ScopedImportBinding,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Const {
|
|
||||||
Smi(i32),
|
|
||||||
Float(f64),
|
|
||||||
Bool(bool),
|
|
||||||
String(StringId),
|
|
||||||
Path(StringId),
|
|
||||||
PrimOp {
|
|
||||||
id: BuiltinId,
|
|
||||||
arity: u8,
|
|
||||||
dispatch_ip: u32,
|
|
||||||
},
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
|
||||||
pub enum AttrKeyType {
|
|
||||||
Static,
|
|
||||||
Dynamic,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum InlineOperand {
|
pub enum InlineOperand {
|
||||||
Const(Const),
|
Const(Const),
|
||||||
BigInt(i64),
|
BigInt(i64),
|
||||||
@@ -149,7 +36,7 @@ pub enum InlineOperand {
|
|||||||
BuiltinConst(StringId),
|
BuiltinConst(StringId),
|
||||||
Builtins,
|
Builtins,
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding { id: StringId, slot_id: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
||||||
@@ -193,13 +80,15 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
InlineOperand::Const(Const::PrimOp {
|
InlineOperand::Const(Const::PrimOp {
|
||||||
id,
|
id,
|
||||||
arity,
|
arity,
|
||||||
dispatch_ip: id.entry_phase().ip(),
|
dispatch_ip: PrimOpPhase::entry_for_builtin(id).ip(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
BuiltinConst(id) => InlineOperand::BuiltinConst(id),
|
BuiltinConst(id) => InlineOperand::BuiltinConst(id),
|
||||||
Builtins => InlineOperand::Builtins,
|
Builtins => InlineOperand::Builtins,
|
||||||
ReplBinding(id) => InlineOperand::ReplBinding(id),
|
ReplBinding(id) => InlineOperand::ReplBinding(id),
|
||||||
ScopedImportBinding(id) => InlineOperand::ScopedImportBinding(id),
|
ScopedImportBinding { slot_id, sym: id } => {
|
||||||
|
InlineOperand::ScopedImportBinding { slot_id, id }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,13 +121,9 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_u8(OperandType::ReplBinding as u8);
|
self.emit_u8(OperandType::ReplBinding as u8);
|
||||||
self.emit_str_id(id);
|
self.emit_str_id(id);
|
||||||
}
|
}
|
||||||
ScopedImportBinding(id) => {
|
ScopedImportBinding { id, slot_id } => {
|
||||||
self.emit_u8(OperandType::ScopedImportBinding as u8);
|
self.emit_u8(OperandType::ScopedImportBinding as u8);
|
||||||
let slot = self
|
self.emit_u32(slot_id);
|
||||||
.ctx
|
|
||||||
.current_scope_slot()
|
|
||||||
.expect("ScopedImportBinding outside scoped compilation");
|
|
||||||
self.emit_u32(slot);
|
|
||||||
self.emit_str_id(id);
|
self.emit_str_id(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -555,14 +440,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::LoadReplBinding);
|
self.emit_op(Op::LoadReplBinding);
|
||||||
self.emit_str_id(name);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
&Ir::ScopedImportBinding(name) => {
|
&Ir::ScopedImportBinding { sym, slot_id } => {
|
||||||
self.emit_op(Op::LoadScopedBinding);
|
self.emit_op(Op::LoadScopedBinding);
|
||||||
let slot = self
|
self.emit_u32(slot_id);
|
||||||
.ctx
|
self.emit_str_id(sym);
|
||||||
.current_scope_slot()
|
|
||||||
.expect("ScopedImportBinding outside scoped compilation");
|
|
||||||
self.emit_u32(slot);
|
|
||||||
self.emit_str_id(name);
|
|
||||||
}
|
}
|
||||||
Ir::WithLookup { sym, namespaces } => {
|
Ir::WithLookup { sym, namespaces } => {
|
||||||
// counter
|
// counter
|
||||||
@@ -629,14 +510,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::LoadReplBinding);
|
self.emit_op(Op::LoadReplBinding);
|
||||||
self.emit_str_id(name);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
ScopedImportBinding(name) => {
|
ScopedImportBinding { slot_id, sym } => {
|
||||||
self.emit_op(Op::LoadScopedBinding);
|
self.emit_op(Op::LoadScopedBinding);
|
||||||
let slot = self
|
self.emit_u32(slot_id);
|
||||||
.ctx
|
self.emit_str_id(sym);
|
||||||
.current_scope_slot()
|
|
||||||
.expect("ScopedImportBinding outside scoped compilation");
|
|
||||||
self.emit_u32(slot);
|
|
||||||
self.emit_str_id(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,7 +652,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
fn emit_attrset(
|
fn emit_attrset(
|
||||||
&mut self,
|
&mut self,
|
||||||
stcs: &fix_ir::HashMap<'_, StringId, (&MaybeThunk, TextRange)>,
|
stcs: &ir::HashMap<'_, StringId, (&MaybeThunk, TextRange)>,
|
||||||
dyns: &[(RawIrRef<'_>, &MaybeThunk, TextRange)],
|
dyns: &[(RawIrRef<'_>, &MaybeThunk, TextRange)],
|
||||||
) {
|
) {
|
||||||
if stcs.is_empty() && dyns.is_empty() {
|
if stcs.is_empty() && dyns.is_empty() {
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fix-common"
|
name = "fix-lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gc-arena = { workspace = true }
|
gc-arena = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
ere = { workspace = true }
|
ere = { workspace = true }
|
||||||
@@ -4,6 +4,128 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use gc_arena::Collect;
|
use gc_arena::Collect;
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
|
macro_rules! define_builtins {
|
||||||
|
($(($name:literal, $variant:ident, $arity:expr)),* $(,)?) => {
|
||||||
|
pub const BUILTINS: &[(&str, u8)] = &[
|
||||||
|
$(($name, $arity),)*
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Collect)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[collect(require_static)]
|
||||||
|
pub enum BuiltinId {
|
||||||
|
$($variant,)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
define_builtins! {
|
||||||
|
("abort", Abort, 1),
|
||||||
|
("__add", Add, 2),
|
||||||
|
("__addErrorContext", AddErrorContext, 2),
|
||||||
|
("__all", All, 2),
|
||||||
|
("__any", Any, 2),
|
||||||
|
("__appendContext", AppendContext, 2),
|
||||||
|
("__attrNames", AttrNames, 1),
|
||||||
|
("__attrValues", AttrValues, 1),
|
||||||
|
("baseNameOf", BaseNameOf, 1),
|
||||||
|
("__bitAnd", BitAnd, 2),
|
||||||
|
("__bitOr", BitOr, 2),
|
||||||
|
("__bitXor", BitXor, 2),
|
||||||
|
("break", Break, 1),
|
||||||
|
("__catAttrs", CatAttrs, 2),
|
||||||
|
("__ceil", Ceil, 1),
|
||||||
|
("__compareVersions", CompareVersions, 2),
|
||||||
|
("__concatLists", ConcatLists, 1),
|
||||||
|
("__concatMap", ConcatMap, 2),
|
||||||
|
("__concatStringsSep", ConcatStringsSep, 2),
|
||||||
|
("__convertHash", ConvertHash, 1),
|
||||||
|
("__deepSeq", DeepSeq, 2),
|
||||||
|
("derivation", Derivation, 1),
|
||||||
|
("derivationStrict", DerivationStrict, 1),
|
||||||
|
("dirOf", DirOf, 1),
|
||||||
|
("__div", Div, 2),
|
||||||
|
("__elem", Elem, 2),
|
||||||
|
("__elemAt", ElemAt, 2),
|
||||||
|
("fetchGit", FetchGit, 1),
|
||||||
|
("fetchMercurial", FetchMercurial, 1),
|
||||||
|
("fetchTarball", FetchTarball, 1),
|
||||||
|
("fetchTree", FetchTree, 1),
|
||||||
|
("__fetchurl", FetchUrl, 1),
|
||||||
|
("__filter", Filter, 2),
|
||||||
|
("__filterSource", FilterSource, 2),
|
||||||
|
("__findFile", FindFile, 2),
|
||||||
|
("__floor", Floor, 1),
|
||||||
|
("__foldl'", FoldlStrict, 3),
|
||||||
|
("__fromJSON", FromJSON, 1),
|
||||||
|
("fromTOML", FromTOML, 1),
|
||||||
|
("__functionArgs", FunctionArgs, 1),
|
||||||
|
("__genList", GenList, 2),
|
||||||
|
("__genericClosure", GenericClosure, 1),
|
||||||
|
("__getAttr", GetAttr, 2),
|
||||||
|
("__getContext", GetContext, 1),
|
||||||
|
("__getEnv", GetEnv, 1),
|
||||||
|
("__groupBy", GroupBy, 2),
|
||||||
|
("__hasAttr", HasAttr, 2),
|
||||||
|
("__hasContext", HasContext, 1),
|
||||||
|
("__hashFile", HashFile, 2),
|
||||||
|
("__hashString", HashString, 2),
|
||||||
|
("__head", Head, 1),
|
||||||
|
("import", Import, 1),
|
||||||
|
("__intersectAttrs", IntersectAttrs, 2),
|
||||||
|
("__isAttrs", IsAttrs, 1),
|
||||||
|
("__isBool", IsBool, 1),
|
||||||
|
("__isFloat", IsFloat, 1),
|
||||||
|
("__isFunction", IsFunction, 1),
|
||||||
|
("__isInt", IsInt, 1),
|
||||||
|
("__isList", IsList, 1),
|
||||||
|
("isNull", IsNull, 1),
|
||||||
|
("__isPath", IsPath, 1),
|
||||||
|
("__isString", IsString, 1),
|
||||||
|
("__length", Length, 1),
|
||||||
|
("__lessThan", LessThan, 2),
|
||||||
|
("__listToAttrs", ListToAttrs, 1),
|
||||||
|
("map", Map, 2),
|
||||||
|
("__mapAttrs", MapAttrs, 2),
|
||||||
|
("__match", Match, 2),
|
||||||
|
("__mul", Mul, 2),
|
||||||
|
("__parseDrvName", ParseDrvName, 1),
|
||||||
|
("__partition", Partition, 2),
|
||||||
|
("__path", Path, 1),
|
||||||
|
("__pathExists", PathExists, 1),
|
||||||
|
("placeholder", Placeholder, 1),
|
||||||
|
("__readDir", ReadDir, 1),
|
||||||
|
("__readFile", ReadFile, 1),
|
||||||
|
("__readFileType", ReadFileType, 1),
|
||||||
|
("removeAttrs", RemoveAttrs, 2),
|
||||||
|
("__replaceStrings", ReplaceStrings, 3),
|
||||||
|
("scopedImport", ScopedImport, 2),
|
||||||
|
("__seq", Seq, 2),
|
||||||
|
("__sort", Sort, 2),
|
||||||
|
("__split", Split, 2),
|
||||||
|
("__splitVersion", SplitVersion, 1),
|
||||||
|
("__storePath", StorePath, 1),
|
||||||
|
("__stringLength", StringLength, 1),
|
||||||
|
("__sub", Sub, 2),
|
||||||
|
("__substring", Substring, 3),
|
||||||
|
("__tail", Tail, 1),
|
||||||
|
("throw", Throw, 1),
|
||||||
|
("__toFile", ToFile, 2),
|
||||||
|
("__toJSON", ToJSON, 1),
|
||||||
|
("__toPath", ToPath, 1),
|
||||||
|
("toString", ToString, 1),
|
||||||
|
("__toXML", ToXML, 1),
|
||||||
|
("__trace", Trace, 2),
|
||||||
|
("__tryEval", TryEval, 1),
|
||||||
|
("__typeOf", TypeOf, 1),
|
||||||
|
("__unsafeDiscardStringContext", UnsafeDiscardStringContext, 1),
|
||||||
|
("__unsafeDiscardOutputDependency", UnsafeDiscardOutputDependency, 1),
|
||||||
|
("__unsafeGetAttrPos", UnsafeGetAttrPos, 2),
|
||||||
|
("__warn", Warn, 2),
|
||||||
|
("__zipAttrsWith", ZipAttrsWith, 2),
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Collect)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Collect)]
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fix-primops"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gc-arena = { workspace = true }
|
|
||||||
hashbrown = { workspace = true }
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
smallvec = { workspace = true }
|
|
||||||
string-interner = { workspace = true }
|
|
||||||
|
|
||||||
fix-abstract-vm = { path = "../fix-abstract-vm" }
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fix-abstract-vm"
|
name = "fix-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -12,7 +12,6 @@ string-interner = { workspace = true }
|
|||||||
likely_stable = { workspace = true }
|
likely_stable = { workspace = true }
|
||||||
sptr = "0.3"
|
sptr = "0.3"
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
fix-lang = { path = "../fix-lang" }
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use fix_common::StringId;
|
use fix_lang::StringId;
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
use fix_codegen::InstructionPtr;
|
use fix_bytecode::InstructionPtr;
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Source;
|
use fix_error::Source;
|
||||||
|
use fix_lang::{self, BUILTINS, StringId};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AttrSet, Closure, ExtraScope, List, NixString, NixType, Null, Path, PrimOp, PrimOpApp, StaticValue, StrictValue, StringContext, Thunk, ThunkState, Value
|
AttrSet, Closure, ExtraScope, List, NixString, NixType, Null, Path, PrimOp, PrimOpApp,
|
||||||
|
StaticValue, StrictValue, StringContext, Thunk, ThunkState, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait VmContext {
|
pub trait VmContext {
|
||||||
@@ -38,7 +39,7 @@ pub trait VmRuntimeCtxExt: VmRuntimeCtx {
|
|||||||
/// Returns the string context attached to `val`, or `&[]` if `val` is
|
/// Returns the string context attached to `val`, or `&[]` if `val` is
|
||||||
/// either a non-string or a string without context.
|
/// either a non-string or a string without context.
|
||||||
fn get_string_context<'gc>(&self, val: StrictValue<'gc>) -> &'gc StringContext;
|
fn get_string_context<'gc>(&self, val: StrictValue<'gc>) -> &'gc StringContext;
|
||||||
fn convert_value(&self, val: Value) -> fix_common::Value;
|
fn convert_value(&self, val: Value) -> fix_lang::Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
||||||
@@ -83,18 +84,18 @@ impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value(&self, val: Value) -> fix_common::Value {
|
fn convert_value(&self, val: Value) -> fix_lang::Value {
|
||||||
self.convert_value_with_seen(val, &mut HashSet::new())
|
self.convert_value_with_seen(val, &mut HashSet::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait ConvertValueWithSeen: VmRuntimeCtx {
|
pub(crate) trait ConvertValueWithSeen: VmRuntimeCtx {
|
||||||
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_common::Value;
|
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_lang::Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
||||||
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_common::Value {
|
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_lang::Value {
|
||||||
use fix_common::Value;
|
use fix_lang::Value;
|
||||||
if let Some(i) = val.as_inline::<i32>() {
|
if let Some(i) = val.as_inline::<i32>() {
|
||||||
Value::Int(i as i64)
|
Value::Int(i as i64)
|
||||||
} else if let Some(gc_i) = val.as_gc::<i64>() {
|
} else if let Some(gc_i) = val.as_gc::<i64>() {
|
||||||
@@ -124,9 +125,9 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
for &(key, val) in attrs.entries.iter() {
|
for &(key, val) in attrs.entries.iter() {
|
||||||
let key = self.resolve_string(key).to_owned();
|
let key = self.resolve_string(key).to_owned();
|
||||||
let converted = self.convert_value_with_seen(val, seen);
|
let converted = self.convert_value_with_seen(val, seen);
|
||||||
map.insert(fix_common::Symbol::from(key), converted);
|
map.insert(fix_lang::Symbol::from(key), converted);
|
||||||
}
|
}
|
||||||
Value::AttrSet(fix_common::AttrSet::new(map))
|
Value::AttrSet(fix_lang::AttrSet::new(map))
|
||||||
} else if let Some(list) = val.as_gc::<List>() {
|
} else if let Some(list) = val.as_gc::<List>() {
|
||||||
let bits = val.to_bits();
|
let bits = val.to_bits();
|
||||||
if list.inner.borrow().is_empty() {
|
if list.inner.borrow().is_empty() {
|
||||||
@@ -142,7 +143,7 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
.copied()
|
.copied()
|
||||||
.map(|v| self.convert_value_with_seen(v, seen))
|
.map(|v| self.convert_value_with_seen(v, seen))
|
||||||
.collect();
|
.collect();
|
||||||
Value::List(fix_common::List::new(items))
|
Value::List(fix_lang::List::new(items))
|
||||||
} else if val.is::<Closure>() {
|
} else if val.is::<Closure>() {
|
||||||
Value::Func
|
Value::Func
|
||||||
} else if let Some(thunk) = val.as_gc::<Thunk>() {
|
} else if let Some(thunk) = val.as_gc::<Thunk>() {
|
||||||
@@ -152,10 +153,10 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
Value::Thunk
|
Value::Thunk
|
||||||
}
|
}
|
||||||
} else if let Some(primop) = val.as_inline::<PrimOp>() {
|
} else if let Some(primop) = val.as_inline::<PrimOp>() {
|
||||||
let name = fix_builtins::BUILTINS[primop.id as usize].0;
|
let name = BUILTINS[primop.id as usize].0;
|
||||||
Value::PrimOp(name.strip_prefix("__").unwrap_or(name))
|
Value::PrimOp(name.strip_prefix("__").unwrap_or(name))
|
||||||
} else if let Some(app) = val.as_gc::<PrimOpApp>() {
|
} else if let Some(app) = val.as_gc::<PrimOpApp>() {
|
||||||
let name = fix_builtins::BUILTINS[app.primop.id as usize].0;
|
let name = BUILTINS[app.primop.id as usize].0;
|
||||||
Value::PrimOpApp(name.strip_prefix("__").unwrap_or(name))
|
Value::PrimOpApp(name.strip_prefix("__").unwrap_or(name))
|
||||||
} else {
|
} else {
|
||||||
Value::Null
|
Value::Null
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
mod boxing;
|
mod boxing;
|
||||||
mod bytecode_reader;
|
|
||||||
mod forced;
|
mod forced;
|
||||||
mod host;
|
mod host;
|
||||||
mod machine;
|
mod machine;
|
||||||
@@ -9,7 +8,7 @@ mod state;
|
|||||||
mod string_context;
|
mod string_context;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use bytecode_reader::*;
|
pub use fix_bytecode::{BytecodeReader, OperandData};
|
||||||
pub use forced::*;
|
pub use forced::*;
|
||||||
pub use host::*;
|
pub use host::*;
|
||||||
pub use machine::*;
|
pub use machine::*;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::{self, StringId};
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -96,7 +96,7 @@ pub trait Machine<'gc> {
|
|||||||
cur.borrow().locals[idx as usize]
|
cur.borrow().locals[idx as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_ok(&mut self, val: fix_common::Value) -> Step;
|
fn finish_ok(&mut self, val: fix_lang::Value) -> Step;
|
||||||
fn finish_err(&mut self, err: Box<Error>) -> Step;
|
fn finish_err(&mut self, err: Box<Error>) -> Step;
|
||||||
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step;
|
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step;
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use fix_bytecode::OperandData;
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{AttrSet, Machine, OperandData, Value};
|
use crate::{AttrSet, Machine, Value, VmRuntimeCtx};
|
||||||
|
|
||||||
/// Resolve a decoded operand into a runtime [`Value`].
|
/// Resolve a decoded operand into a runtime [`Value`].
|
||||||
///
|
///
|
||||||
@@ -11,11 +12,12 @@ use crate::{AttrSet, Machine, OperandData, Value};
|
|||||||
pub fn resolve_operand<'gc, M: Machine<'gc>>(
|
pub fn resolve_operand<'gc, M: Machine<'gc>>(
|
||||||
op: &OperandData,
|
op: &OperandData,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
|
ctx: &impl VmRuntimeCtx,
|
||||||
m: &M,
|
m: &M,
|
||||||
) -> Value<'gc> {
|
) -> Value<'gc> {
|
||||||
use OperandData::*;
|
use OperandData::*;
|
||||||
match *op {
|
match *op {
|
||||||
Const(sv) => sv.into(),
|
Const(id) => ctx.get_const(id).into(),
|
||||||
BigInt(val) => Value::new_gc(Gc::new(mc, val)),
|
BigInt(val) => Value::new_gc(Gc::new(mc, val)),
|
||||||
Local { layer, idx } => m.local(layer, idx),
|
Local { layer, idx } => m.local(layer, idx),
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
use gc_arena::{Collect, Gc};
|
use gc_arena::{Collect, Gc};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
use crate::{GcEnv, StaticValue, Thunk};
|
use crate::{GcEnv, Thunk};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
@@ -87,13 +87,3 @@ pub enum ExtraScope {
|
|||||||
slot_id: u32,
|
slot_id: u32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum OperandData {
|
|
||||||
Const(StaticValue),
|
|
||||||
BigInt(i64),
|
|
||||||
Local { layer: u8, idx: u32 },
|
|
||||||
BuiltinConst(StringId),
|
|
||||||
Builtins,
|
|
||||||
ReplBinding(StringId),
|
|
||||||
ScopedImportBinding { slot_id: u32, name: StringId },
|
|
||||||
}
|
|
||||||
@@ -89,7 +89,7 @@ impl<'a> IntoIterator for &'a mut StringContext {
|
|||||||
impl FromIterator<StringContextElem> for StringContext {
|
impl FromIterator<StringContextElem> for StringContext {
|
||||||
fn from_iter<T: IntoIterator<Item = StringContextElem>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = StringContextElem>>(iter: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: iter.into_iter().collect()
|
data: iter.into_iter().collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,7 @@ use std::marker::PhantomData;
|
|||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use fix_builtins::BuiltinId;
|
use fix_lang::*;
|
||||||
use fix_common::*;
|
|
||||||
use gc_arena::barrier::Unlock;
|
use gc_arena::barrier::Unlock;
|
||||||
use gc_arena::collect::Trace;
|
use gc_arena::collect::Trace;
|
||||||
use gc_arena::{Collect, Gc, GcRefLock, Mutation, RefLock};
|
use gc_arena::{Collect, Gc, GcRefLock, Mutation, RefLock};
|
||||||
+3
-5
@@ -16,9 +16,7 @@ likely_stable = { workspace = true }
|
|||||||
sptr = "0.3"
|
sptr = "0.3"
|
||||||
sysinfo = { version = "0.38", default-features = false, features = ["system"] }
|
sysinfo = { version = "0.38", default-features = false, features = ["system"] }
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
fix-abstract-vm = { path = "../fix-abstract-vm" }
|
fix-lang = { path = "../fix-lang" }
|
||||||
fix-primops = { path = "../fix-primops" }
|
fix-runtime = { path = "../fix-runtime" }
|
||||||
|
|||||||
@@ -202,16 +202,16 @@ macro_rules! table {
|
|||||||
impl<'gc, C: VmRuntimeCtx> DispatchTable<'gc, C> {
|
impl<'gc, C: VmRuntimeCtx> DispatchTable<'gc, C> {
|
||||||
pub(crate) const NEW: Self = {
|
pub(crate) const NEW: Self = {
|
||||||
let mut arr: [OpFn<'gc, C>; 256] = [op_illegal; 256];
|
let mut arr: [OpFn<'gc, C>; 256] = [op_illegal; 256];
|
||||||
$( arr[fix_codegen::Op::$variant as usize] = $fn; )*
|
$( arr[fix_bytecode::Op::$variant as usize] = $fn; )*
|
||||||
DispatchTable(arr)
|
DispatchTable(arr)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exhaustiveness check: fails to compile if `fix_codegen::Op` gains,
|
// Exhaustiveness check: fails to compile if `fix_bytecode::Op` gains,
|
||||||
// loses, or renames a variant that isn't wired up above.
|
// loses, or renames a variant that isn't wired up above.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const _: fn(fix_codegen::Op) = |op| match op {
|
const _: fn(fix_bytecode::Op) = |op| match op {
|
||||||
$( fix_codegen::Op::$variant => (), )*
|
$( fix_bytecode::Op::$variant => (), )*
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use fix_abstract_vm::*;
|
use fix_runtime::*;
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::{BytecodeReader, NixNum, Step, VmError, VmRuntimeCtx};
|
use crate::{BytecodeReader, NixNum, Step, VmError, VmRuntimeCtx};
|
||||||
@@ -29,7 +29,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let combined = format!("{ls}{rs}");
|
let combined = format!("{ls}{rs}");
|
||||||
let canon = canon_path_str(&combined);
|
let canon = canon_path_str(&combined);
|
||||||
let sid = ctx.intern_string(canon);
|
let sid = ctx.intern_string(canon);
|
||||||
self.push(Value::new_inline(fix_abstract_vm::Path(sid)));
|
self.push(Value::new_inline(fix_runtime::Path(sid)));
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string_or_path(rhs)) {
|
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string_or_path(rhs)) {
|
||||||
@@ -111,7 +111,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
fix_primops::start_eq(self, ctx, reader, mc, lhs, rhs, false)
|
crate::primops::start_eq(self, ctx, reader, mc, lhs, rhs, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -122,7 +122,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
fix_primops::start_eq(self, ctx, reader, mc, lhs, rhs, true)
|
crate::primops::start_eq(self, ctx, reader, mc, lhs, rhs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -258,7 +258,11 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// TODO: compare other types
|
// TODO: compare other types
|
||||||
Err(crate::vm_err(format!("cannot compare {} with {}", lhs.ty(), rhs.ty())))
|
Err(crate::vm_err(format!(
|
||||||
|
"cannot compare {} with {}",
|
||||||
|
lhs.ty(),
|
||||||
|
rhs.ty()
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix_abstract_vm::{resolve_operand, *};
|
use fix_bytecode::PrimOpPhase;
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{resolve_operand, *};
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -120,7 +120,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let arg = resolve_operand(&reader.read_operand_data(ctx), mc, self);
|
let arg = resolve_operand(&reader.read_operand_data(), mc, ctx, self);
|
||||||
let pc = reader.pc();
|
let pc = reader.pc();
|
||||||
self.call(reader, mc, arg, pc)
|
self.call(reader, mc, arg, pc)
|
||||||
}
|
}
|
||||||
@@ -178,6 +178,6 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
fix_primops::dispatch_primop(self, ctx, reader, mc)
|
crate::primops::dispatch_primop(self, ctx, reader, mc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix_abstract_vm::{NixType, resolve_operand};
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{NixType, resolve_operand};
|
||||||
use gc_arena::{Gc, RefLock};
|
use gc_arena::{Gc, RefLock};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -43,13 +43,13 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
|
|
||||||
for _ in 0..static_count {
|
for _ in 0..static_count {
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
let val = resolve_operand(&reader.read_operand_data(ctx), mc, self);
|
let val = resolve_operand(&reader.read_operand_data(), mc, ctx, self);
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
kv.push((key, val));
|
kv.push((key, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in dyn_keys {
|
for key in dyn_keys {
|
||||||
let val = resolve_operand(&reader.read_operand_data(ctx), mc, self);
|
let val = resolve_operand(&reader.read_operand_data(), mc, ctx, self);
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
kv.push((key, val))
|
kv.push((key, val))
|
||||||
@@ -124,7 +124,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
use fix_codegen::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
loop {
|
loop {
|
||||||
match reader.read_op() {
|
match reader.read_op() {
|
||||||
SelectStatic => {
|
SelectStatic => {
|
||||||
@@ -153,7 +153,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
/// Skip the rest of a **HasAttr** attrpath after an intermediate
|
/// Skip the rest of a **HasAttr** attrpath after an intermediate
|
||||||
/// lookup failed. Only recognises HasAttr opcodes and jumps.
|
/// lookup failed. Only recognises HasAttr opcodes and jumps.
|
||||||
fn has_attr_skip(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
fn has_attr_skip(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
use fix_codegen::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
loop {
|
loop {
|
||||||
match reader.read_op() {
|
match reader.read_op() {
|
||||||
HasAttrPathStatic => {
|
HasAttrPathStatic => {
|
||||||
@@ -314,7 +314,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let count = reader.read_u32() as usize;
|
let count = reader.read_u32() as usize;
|
||||||
let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count);
|
let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
items.push(resolve_operand(&reader.read_operand_data(ctx), mc, self));
|
items.push(resolve_operand(&reader.read_operand_data(), mc, ctx, self));
|
||||||
}
|
}
|
||||||
let list = Gc::new(
|
let list = Gc::new(
|
||||||
mc,
|
mc,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use fix_abstract_vm::*;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_runtime::*;
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
use crate::{BytecodeReader, Step, VmRuntimeCtx};
|
use crate::{BytecodeReader, Step, VmRuntimeCtx};
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
AttrSet, NixString, Path, StrictValue, StringContext, canon_path_str
|
|
||||||
};
|
|
||||||
use fix_builtins::BuiltinId;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
|
use fix_runtime::{AttrSet, NixString, Path, StrictValue, StringContext, canon_path_str};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::{BytecodeReader, PrimOp, Step, Value, VmRuntimeCtx, VmRuntimeCtxExt};
|
use crate::{BytecodeReader, PrimOp, Step, Value, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
@@ -23,8 +21,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
.map_err(|err| panic!("unknown builtin id: {}", err.number));
|
.map_err(|err| panic!("unknown builtin id: {}", err.number));
|
||||||
self.push(Value::new_inline(PrimOp {
|
self.push(Value::new_inline(PrimOp {
|
||||||
id,
|
id,
|
||||||
arity: fix_builtins::BUILTINS[id as usize].1,
|
arity: BUILTINS[id as usize].1,
|
||||||
dispatch_ip: id.entry_phase().ip(),
|
dispatch_ip: PrimOpPhase::entry_for_builtin(id).ip(),
|
||||||
}));
|
}));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix_abstract_vm::{resolve_operand, *};
|
|
||||||
use fix_common::Symbol;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::Symbol;
|
||||||
|
use fix_runtime::{resolve_operand, *};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{Break, BytecodeReader, CallFrame, Step, VmRuntimeCtx};
|
use crate::{Break, BytecodeReader, CallFrame, Step, VmRuntimeCtx};
|
||||||
@@ -20,7 +20,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let n = reader.read_u8();
|
let n = reader.read_u8();
|
||||||
let mut namespaces = SmallVec::<[_; 2]>::new();
|
let mut namespaces = SmallVec::<[_; 2]>::new();
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
namespaces.push(resolve_operand(&reader.read_operand_data(ctx), mc, self));
|
namespaces.push(resolve_operand(&reader.read_operand_data(), mc, ctx, self));
|
||||||
}
|
}
|
||||||
|
|
||||||
let resume_pc = reader.inst_start_pc();
|
let resume_pc = reader.inst_start_pc();
|
||||||
|
|||||||
+11
-11
@@ -7,10 +7,9 @@
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_builtins::{BUILTINS, BuiltinId};
|
use fix_bytecode::{InstructionPtr, PrimOpPhase};
|
||||||
use fix_codegen::InstructionPtr;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::{Error, Result, Source};
|
use fix_error::{Error, Result, Source};
|
||||||
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
use gc_arena::metrics::Pacing;
|
use gc_arena::metrics::Pacing;
|
||||||
use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable};
|
use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@@ -19,8 +18,9 @@ use smallvec::SmallVec;
|
|||||||
|
|
||||||
#[cfg(feature = "tailcall")]
|
#[cfg(feature = "tailcall")]
|
||||||
mod dispatch_tailcall;
|
mod dispatch_tailcall;
|
||||||
pub use fix_abstract_vm::*;
|
pub use fix_runtime::*;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
|
mod primops;
|
||||||
|
|
||||||
type VmResult<T> = std::result::Result<T, VmError>;
|
type VmResult<T> = std::result::Result<T, VmError>;
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ pub struct Vm<'gc> {
|
|||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
|
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
result: Option<Result<fix_common::Value>>,
|
result: Option<Result<fix_lang::Value>>,
|
||||||
|
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
pending_load: Option<PendingLoad>,
|
pending_load: Option<PendingLoad>,
|
||||||
@@ -61,7 +61,7 @@ fn init_builtins<'gc>(mc: &Mutation<'gc>, ctx: &mut impl VmRuntimeCtx) -> Value<
|
|||||||
let id = BuiltinId::try_from_primitive(idx as u8).expect("infallible");
|
let id = BuiltinId::try_from_primitive(idx as u8).expect("infallible");
|
||||||
let name = name.strip_prefix("__").unwrap_or(name);
|
let name = name.strip_prefix("__").unwrap_or(name);
|
||||||
let name = ctx.intern_string(name);
|
let name = ctx.intern_string(name);
|
||||||
let dispatch_ip = id.entry_phase().ip();
|
let dispatch_ip = PrimOpPhase::entry_for_builtin(id).ip();
|
||||||
entries.push((
|
entries.push((
|
||||||
name,
|
name,
|
||||||
Value::new_inline(PrimOp {
|
Value::new_inline(PrimOp {
|
||||||
@@ -139,7 +139,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish_ok(&mut self, val: fix_common::Value) -> Step {
|
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
||||||
self.result = Some(Ok(val));
|
self.result = Some(Ok(val));
|
||||||
Step::Break(Break::Done)
|
Step::Break(Break::Done)
|
||||||
}
|
}
|
||||||
@@ -409,7 +409,7 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish_ok(&mut self, val: fix_common::Value) -> Step {
|
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
||||||
self.finish_ok(val)
|
self.finish_ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +481,7 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
|||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
Continue { pc: usize },
|
Continue { pc: usize },
|
||||||
Done(Result<fix_common::Value>),
|
Done(Result<fix_lang::Value>),
|
||||||
LoadFile(PendingLoad),
|
LoadFile(PendingLoad),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +504,7 @@ impl Vm<'_> {
|
|||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
ip: InstructionPtr,
|
ip: InstructionPtr,
|
||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
let (code, runtime) = ctx.split();
|
let (code, runtime) = ctx.split();
|
||||||
let mut arena: Arena<Rootable![Vm<'_>]> = Arena::new(|mc| Vm::new(force_mode, mc, runtime));
|
let mut arena: Arena<Rootable![Vm<'_>]> = Arena::new(|mc| Vm::new(force_mode, mc, runtime));
|
||||||
arena.metrics().set_pacing(Pacing {
|
arena.metrics().set_pacing(Pacing {
|
||||||
@@ -589,7 +589,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
pc: usize,
|
pc: usize,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Action {
|
) -> Action {
|
||||||
use fix_codegen::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
|
|
||||||
let mut reader = BytecodeReader::new(bytecode, pc);
|
let mut reader = BytecodeReader::new(bytecode, pc);
|
||||||
let mut fuel = Self::DEFAULT_FUEL_AMOUNT;
|
let mut fuel = Self::DEFAULT_FUEL_AMOUNT;
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
//! `builtins.unsafeDiscardStringContext`,
|
//! `builtins.unsafeDiscardStringContext`,
|
||||||
//! `builtins.unsafeDiscardOutputDependency`.
|
//! `builtins.unsafeDiscardOutputDependency`.
|
||||||
//!
|
//!
|
||||||
//! See `fix-abstract-vm/src/string_context.rs` for the
|
//! See `fix-runtime/src/string_context.rs` for the
|
||||||
//! `StringContextElem` type.
|
//! `StringContextElem` type.
|
||||||
|
|
||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, BytecodeReader, List as VmList, Machine, MachineExt, NixString, NixType, Step,
|
AttrSet, BytecodeReader, List as VmList, Machine, MachineExt, NixString, NixType, Step,
|
||||||
StrictValue, StringContext, StringContextElem, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
StrictValue, StringContext, StringContextElem, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, BytecodeReader, Closure, Env, List, Machine, MachineExt, Step, StrictValue, Value,
|
AttrSet, BytecodeReader, Closure, Env, List, Machine, MachineExt, Step, StrictValue, Value,
|
||||||
VmRuntimeCtx, VmRuntimeCtxExt,
|
VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{
|
||||||
BytecodeReader, Machine, MachineExt, NixString, NixType, Path, Step, StrictValue, Value,
|
BytecodeReader, Machine, MachineExt, NixString, NixType, Path, Step, StrictValue, Value,
|
||||||
VmRuntimeCtx,
|
VmRuntimeCtx,
|
||||||
};
|
};
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
pub fn to_string<'gc, M: Machine<'gc>>(
|
pub fn to_string<'gc, M: Machine<'gc>>(
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, BytecodeReader, CallFrame, List, Machine, MachineExt, NixNum, Null, Path, Step,
|
AttrSet, BytecodeReader, CallFrame, List, Machine, MachineExt, NixNum, Null, Path, Step,
|
||||||
StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -30,10 +30,8 @@ pub fn start_eq<'gc, M: Machine<'gc>>(
|
|||||||
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
||||||
}
|
}
|
||||||
ShallowEq::RecurseAttrs(a, b) => {
|
ShallowEq::RecurseAttrs(a, b) => {
|
||||||
let lhs_init: SmallVec<[Value<'gc>; 4]> =
|
let lhs_init: SmallVec<[Value<'gc>; 4]> = a.entries.iter().map(|&(_, v)| v).collect();
|
||||||
a.entries.iter().map(|&(_, v)| v).collect();
|
let rhs_init: SmallVec<[Value<'gc>; 4]> = b.entries.iter().map(|&(_, v)| v).collect();
|
||||||
let rhs_init: SmallVec<[Value<'gc>; 4]> =
|
|
||||||
b.entries.iter().map(|&(_, v)| v).collect();
|
|
||||||
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +114,12 @@ fn apply_pair<'gc, M: Machine<'gc>>(
|
|||||||
m.replace(2, Value::new_inline(false));
|
m.replace(2, Value::new_inline(false));
|
||||||
}
|
}
|
||||||
ShallowEq::RecurseList(la, lb) => {
|
ShallowEq::RecurseList(la, lb) => {
|
||||||
extend_queues(m, mc, la.inner.borrow().iter().copied(), lb.inner.borrow().iter().copied());
|
extend_queues(
|
||||||
|
m,
|
||||||
|
mc,
|
||||||
|
la.inner.borrow().iter().copied(),
|
||||||
|
lb.inner.borrow().iter().copied(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ShallowEq::RecurseAttrs(a, b) => {
|
ShallowEq::RecurseAttrs(a, b) => {
|
||||||
extend_queues(
|
extend_queues(
|
||||||
@@ -230,9 +233,5 @@ fn shallow_eq<'gc>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bool_outcome<'gc>(b: bool) -> ShallowEq<'gc> {
|
fn bool_outcome<'gc>(b: bool) -> ShallowEq<'gc> {
|
||||||
if b {
|
if b { ShallowEq::True } else { ShallowEq::False }
|
||||||
ShallowEq::True
|
|
||||||
} else {
|
|
||||||
ShallowEq::False
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, Break, BytecodeReader, CallFrame, Machine, MachineExt, Path, PendingLoad,
|
AttrSet, Break, BytecodeReader, CallFrame, Machine, MachineExt, Path, PendingLoad,
|
||||||
PendingScope, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt, canon_path_str,
|
PendingScope, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt, canon_path_str,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
BytecodeReader, List, Machine, MachineExt, NixType, Step, StrictValue, Value,
|
use fix_runtime::{BytecodeReader, List, Machine, MachineExt, NixType, Step, StrictValue, Value};
|
||||||
};
|
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
pub fn filter_force_list<'gc, M: Machine<'gc>>(
|
pub fn filter_force_list<'gc, M: Machine<'gc>>(
|
||||||
@@ -10,9 +10,9 @@ pub use context::*;
|
|||||||
pub use control::*;
|
pub use control::*;
|
||||||
pub use conv::*;
|
pub use conv::*;
|
||||||
pub use eq::*;
|
pub use eq::*;
|
||||||
use fix_abstract_vm::{BytecodeReader, Machine, Step, VmRuntimeCtx};
|
use fix_bytecode::PrimOpPhase;
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{BytecodeReader, Machine, Step, VmRuntimeCtx};
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
pub use io::*;
|
pub use io::*;
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
@@ -86,7 +86,9 @@ pub fn dispatch_primop<'gc, M: Machine<'gc>>(
|
|||||||
AppendContextEntryForced => append_context_entry_forced(m, ctx, reader, mc),
|
AppendContextEntryForced => append_context_entry_forced(m, ctx, reader, mc),
|
||||||
AppendContextOutputsForced => append_context_outputs_forced(m, ctx, reader, mc),
|
AppendContextOutputsForced => append_context_outputs_forced(m, ctx, reader, mc),
|
||||||
AppendContextOutputElementLoop => append_context_output_element_loop(m, ctx, reader, mc),
|
AppendContextOutputElementLoop => append_context_output_element_loop(m, ctx, reader, mc),
|
||||||
AppendContextOutputElementForced => append_context_output_element_forced(m, ctx, reader, mc),
|
AppendContextOutputElementForced => {
|
||||||
|
append_context_output_element_forced(m, ctx, reader, mc)
|
||||||
|
}
|
||||||
UnsafeDiscardStringContext => unsafe_discard_string_context(m, ctx, reader, mc),
|
UnsafeDiscardStringContext => unsafe_discard_string_context(m, ctx, reader, mc),
|
||||||
UnsafeDiscardOutputDependency => unsafe_discard_output_dependency(m, ctx, reader, mc),
|
UnsafeDiscardOutputDependency => unsafe_discard_output_dependency(m, ctx, reader, mc),
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{
|
||||||
BytecodeReader, Machine, MachineExt, Path, Step, StrictValue, Value, VmRuntimeCtx,
|
BytecodeReader, Machine, MachineExt, Path, Step, StrictValue, Value, VmRuntimeCtx,
|
||||||
VmRuntimeCtxExt, canon_path_str,
|
VmRuntimeCtxExt, canon_path_str,
|
||||||
};
|
};
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
pub fn to_path<'gc, M: Machine<'gc>>(
|
pub fn to_path<'gc, M: Machine<'gc>>(
|
||||||
+4
-11
@@ -25,20 +25,13 @@ miette = { version = "7.4", features = ["fancy"] }
|
|||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
|
|
||||||
# Memory Management
|
|
||||||
bumpalo = { workspace = true }
|
|
||||||
|
|
||||||
rnix = { workspace = true }
|
|
||||||
|
|
||||||
ere = { workspace = true }
|
ere = { workspace = true }
|
||||||
ghost-cell = { workspace = true }
|
|
||||||
|
|
||||||
fix-abstract-vm = { path = "../fix-abstract-vm" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-compiler = { path = "../fix-compiler" }
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
fix-ir = { path = "../fix-ir" }
|
fix-lang = { path = "../fix-lang" }
|
||||||
|
fix-runtime = { path = "../fix-runtime" }
|
||||||
fix-vm = { path = "../fix-vm" }
|
fix-vm = { path = "../fix-vm" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::{Result, Source};
|
use fix_error::{Result, Source};
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
pub fn eval(expr: &str) -> Value {
|
pub fn eval(expr: &str) -> Value {
|
||||||
Evaluator::new()
|
Evaluator::new()
|
||||||
|
|||||||
+18
-589
@@ -1,20 +1,13 @@
|
|||||||
#![warn(clippy::unwrap_used)]
|
#![warn(clippy::unwrap_used)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use fix_bytecode::InstructionPtr;
|
||||||
use fix_abstract_vm::{ForceMode, StaticValue, VmCode, VmContext, VmRuntimeCtx};
|
use fix_bytecode::disassembler::{Disassembler, DisassemblerContext};
|
||||||
use fix_builtins::PrimOpPhase;
|
use fix_compiler::{CodeState, ExtraScope};
|
||||||
use fix_codegen::disassembler::{Disassembler, DisassemblerContext};
|
use fix_error::{Result, Source};
|
||||||
use fix_codegen::{BytecodeContext, InstructionPtr, Op};
|
use fix_lang::StringId;
|
||||||
use fix_common::{StringId, Symbol};
|
use fix_runtime::{ForceMode, StaticValue, VmCode, VmContext, VmRuntimeCtx};
|
||||||
use fix_error::{Error, Result, Source};
|
|
||||||
use fix_ir::downgrade::{Downgrade as _, DowngradeContext};
|
|
||||||
use fix_ir::{
|
|
||||||
GhostMaybeThunkRef, GhostRoIrRef, GhostRoMaybeThunkRef, GhostRoRef, Ir, MaybeThunk, RawIrRef,
|
|
||||||
ThunkId,
|
|
||||||
};
|
|
||||||
use fix_vm::Vm;
|
use fix_vm::Vm;
|
||||||
use ghost_cell::{GhostCell, GhostToken};
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use string_interner::{DefaultStringInterner, Symbol as _};
|
use string_interner::{DefaultStringInterner, Symbol as _};
|
||||||
|
|
||||||
@@ -29,15 +22,6 @@ pub struct RuntimeState {
|
|||||||
pub constants: Constants,
|
pub constants: Constants,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CodeState {
|
|
||||||
pub bytecode: Vec<u8>,
|
|
||||||
pub sources: Vec<Source>,
|
|
||||||
pub spans: Vec<(usize, rnix::TextRange)>,
|
|
||||||
pub thunk_count: usize,
|
|
||||||
pub global_env: HashMap<StringId, MaybeThunk>,
|
|
||||||
pub current_scope_slot: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Evaluator {
|
pub struct Evaluator {
|
||||||
pub runtime: RuntimeState,
|
pub runtime: RuntimeState,
|
||||||
pub code: CodeState,
|
pub code: CodeState,
|
||||||
@@ -52,37 +36,25 @@ impl Default for Evaluator {
|
|||||||
impl Evaluator {
|
impl Evaluator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut strings = DefaultStringInterner::new();
|
let mut strings = DefaultStringInterner::new();
|
||||||
let global_env = fix_ir::new_global_env(&mut strings);
|
let code = CodeState::new(&mut strings);
|
||||||
let mut bytecode = Vec::with_capacity(PrimOpPhase::Illegal as usize * 2);
|
|
||||||
for phase in 0..=PrimOpPhase::Illegal as u8 {
|
|
||||||
bytecode.push(Op::DispatchPrimOp as u8);
|
|
||||||
bytecode.push(phase);
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
runtime: RuntimeState {
|
runtime: RuntimeState {
|
||||||
strings,
|
strings,
|
||||||
constants: Constants::default(),
|
constants: Constants::default(),
|
||||||
},
|
},
|
||||||
code: CodeState {
|
code,
|
||||||
sources: Vec::new(),
|
|
||||||
spans: Vec::new(),
|
|
||||||
thunk_count: 0,
|
|
||||||
bytecode,
|
|
||||||
global_env,
|
|
||||||
current_scope_slot: None,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(&mut self, source: Source) -> Result<fix_common::Value> {
|
pub fn eval(&mut self, source: Source) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, None, ForceMode::AsIs)
|
self.do_eval(source, None, ForceMode::AsIs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_shallow(&mut self, source: Source) -> Result<fix_common::Value> {
|
pub fn eval_shallow(&mut self, source: Source) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, None, ForceMode::Shallow)
|
self.do_eval(source, None, ForceMode::Shallow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_deep(&mut self, source: Source) -> Result<fix_common::Value> {
|
pub fn eval_deep(&mut self, source: Source) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, None, ForceMode::Deep)
|
self.do_eval(source, None, ForceMode::Deep)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +62,7 @@ impl Evaluator {
|
|||||||
&mut self,
|
&mut self,
|
||||||
source: Source,
|
source: Source,
|
||||||
scope: &HashSet<StringId>,
|
scope: &HashSet<StringId>,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, Some(ExtraScope::Repl(scope)), ForceMode::Shallow)
|
self.do_eval(source, Some(ExtraScope::Repl(scope)), ForceMode::Shallow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,14 +71,10 @@ impl Evaluator {
|
|||||||
source: Source,
|
source: Source,
|
||||||
extra_scope: Option<ExtraScope<'ctx>>,
|
extra_scope: Option<ExtraScope<'ctx>>,
|
||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
let ip = {
|
let ip = self
|
||||||
let mut compiler = CompilerCtx {
|
.code
|
||||||
code: &mut self.code,
|
.compile_bytecode(source, extra_scope, &mut self.runtime)?;
|
||||||
runtime: &mut self.runtime,
|
|
||||||
};
|
|
||||||
compiler.compile_bytecode(source, extra_scope)?
|
|
||||||
};
|
|
||||||
Vm::run(self, ip, force_mode)
|
Vm::run(self, ip, force_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,16 +83,12 @@ impl Evaluator {
|
|||||||
_ident: &str,
|
_ident: &str,
|
||||||
_expr: &str,
|
_expr: &str,
|
||||||
_scope: &mut HashSet<StringId>,
|
_scope: &mut HashSet<StringId>,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
todo!("add_binding")
|
todo!("add_binding")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_bytecode(&mut self, source: Source) -> Result<InstructionPtr> {
|
pub fn compile_bytecode(&mut self, source: Source) -> Result<InstructionPtr> {
|
||||||
let mut compiler = CompilerCtx {
|
self.code.compile_bytecode(source, None, &mut self.runtime)
|
||||||
code: &mut self.code,
|
|
||||||
runtime: &mut self.runtime,
|
|
||||||
};
|
|
||||||
compiler.compile_bytecode(source, None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble_colored(&self, ip: InstructionPtr) -> String {
|
pub fn disassemble_colored(&self, ip: InstructionPtr) -> String {
|
||||||
@@ -149,151 +113,12 @@ impl VmRuntimeCtx for RuntimeState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmCode for CodeState {
|
|
||||||
fn bytecode(&self) -> &[u8] {
|
|
||||||
&self.bytecode
|
|
||||||
}
|
|
||||||
fn compile_with_scope(
|
|
||||||
&mut self,
|
|
||||||
source: Source,
|
|
||||||
extra_scope: Option<fix_abstract_vm::ExtraScope>,
|
|
||||||
runtime: &mut impl VmRuntimeCtx,
|
|
||||||
) -> Result<InstructionPtr> {
|
|
||||||
let mut compiler = CompilerCtx {
|
|
||||||
code: self,
|
|
||||||
runtime,
|
|
||||||
};
|
|
||||||
let extra = extra_scope.map(|s| match s {
|
|
||||||
fix_abstract_vm::ExtraScope::ScopedImport { keys, slot_id } => {
|
|
||||||
ExtraScope::ScopedImport { keys, slot_id }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
compiler.compile_bytecode(source, extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VmContext for Evaluator {
|
impl VmContext for Evaluator {
|
||||||
fn split(&mut self) -> (&mut impl VmCode, &mut impl VmRuntimeCtx) {
|
fn split(&mut self) -> (&mut impl VmCode, &mut impl VmRuntimeCtx) {
|
||||||
(&mut self.code, &mut self.runtime)
|
(&mut self.code, &mut self.runtime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CompilerCtx<'a, R: VmRuntimeCtx> {
|
|
||||||
code: &'a mut CodeState,
|
|
||||||
runtime: &'a mut R,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
|
||||||
fn compile_bytecode(
|
|
||||||
&mut self,
|
|
||||||
source: Source,
|
|
||||||
extra_scope: Option<ExtraScope>,
|
|
||||||
) -> Result<InstructionPtr> {
|
|
||||||
let prev_scope_slot = self.code.current_scope_slot;
|
|
||||||
self.code.current_scope_slot = match &extra_scope {
|
|
||||||
Some(ExtraScope::ScopedImport { slot_id, .. }) => Some(*slot_id),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let result = (|| -> Result<InstructionPtr> {
|
|
||||||
let root = self.downgrade(source, extra_scope)?;
|
|
||||||
let ip = fix_codegen::compile_bytecode(root.as_ref(), self);
|
|
||||||
Ok(ip)
|
|
||||||
})();
|
|
||||||
self.code.current_scope_slot = prev_scope_slot;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn downgrade(&mut self, source: Source, extra_scope: Option<ExtraScope>) -> Result<OwnedIr> {
|
|
||||||
tracing::debug!("Parsing Nix expression");
|
|
||||||
|
|
||||||
self.code.sources.push(source.clone());
|
|
||||||
|
|
||||||
let root = rnix::Root::parse(&source.src);
|
|
||||||
handle_parse_error(root.errors(), source.clone()).map_or(Ok(()), Err)?;
|
|
||||||
|
|
||||||
tracing::debug!("Downgrading Nix expression");
|
|
||||||
let expr = root
|
|
||||||
.tree()
|
|
||||||
.expr()
|
|
||||||
.ok_or_else(|| Error::parse_error("unexpected EOF".into()))?;
|
|
||||||
let bump = Bump::new();
|
|
||||||
GhostToken::new(|token| {
|
|
||||||
let downgrade_ctx = DowngradeCtx::new(
|
|
||||||
&bump,
|
|
||||||
token,
|
|
||||||
self.runtime,
|
|
||||||
&self.code.global_env,
|
|
||||||
extra_scope.map(Into::into),
|
|
||||||
&mut self.code.thunk_count,
|
|
||||||
source,
|
|
||||||
);
|
|
||||||
let ir = downgrade_ctx.downgrade_toplevel(expr)?;
|
|
||||||
let ir = unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) };
|
|
||||||
Ok(OwnedIr { _bump: bump, ir })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: VmRuntimeCtx> BytecodeContext for CompilerCtx<'a, R> {
|
|
||||||
fn intern_string(&mut self, s: &str) -> StringId {
|
|
||||||
self.runtime.intern_string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_span(&mut self, range: rnix::TextRange) -> u32 {
|
|
||||||
let id = self.code.spans.len();
|
|
||||||
let source_id = self
|
|
||||||
.code
|
|
||||||
.sources
|
|
||||||
.len()
|
|
||||||
.checked_sub(1)
|
|
||||||
.expect("current_source not set");
|
|
||||||
self.code.spans.push((source_id, range));
|
|
||||||
id as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_code(&self) -> &[u8] {
|
|
||||||
&self.code.bytecode
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_code_mut(&mut self) -> &mut Vec<u8> {
|
|
||||||
&mut self.code.bytecode
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_constant(&mut self, val: fix_codegen::Const) -> u32 {
|
|
||||||
use fix_codegen::Const::*;
|
|
||||||
let val = match val {
|
|
||||||
Smi(x) => StaticValue::new_inline(x),
|
|
||||||
Float(x) => StaticValue::new_float(x),
|
|
||||||
Bool(x) => StaticValue::new_inline(x),
|
|
||||||
String(x) => StaticValue::new_inline(x),
|
|
||||||
Path(_) => todo!("path value type"),
|
|
||||||
PrimOp {
|
|
||||||
id,
|
|
||||||
arity,
|
|
||||||
dispatch_ip,
|
|
||||||
} => StaticValue::new_primop(id, arity, dispatch_ip),
|
|
||||||
Null => StaticValue::default(),
|
|
||||||
};
|
|
||||||
self.runtime.add_const(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_source_dir(&mut self) -> StringId {
|
|
||||||
let dir = self
|
|
||||||
.code
|
|
||||||
.sources
|
|
||||||
.last()
|
|
||||||
.expect("current_source not set")
|
|
||||||
.get_dir()
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
self.runtime.intern_string(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_scope_slot(&self) -> Option<u32> {
|
|
||||||
self.code.current_scope_slot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Constants {
|
pub struct Constants {
|
||||||
data: Vec<StaticValue>,
|
data: Vec<StaticValue>,
|
||||||
@@ -315,402 +140,6 @@ impl Constants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_error_span(error: &rnix::ParseError) -> Option<rnix::TextRange> {
|
|
||||||
use rnix::ParseError::*;
|
|
||||||
match error {
|
|
||||||
Unexpected(range)
|
|
||||||
| UnexpectedExtra(range)
|
|
||||||
| UnexpectedWanted(_, range, _)
|
|
||||||
| UnexpectedDoubleBind(range)
|
|
||||||
| DuplicatedArgs(range, _) => Some(*range),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_parse_error<'a>(
|
|
||||||
errors: impl IntoIterator<Item = &'a rnix::ParseError>,
|
|
||||||
source: Source,
|
|
||||||
) -> Option<Box<Error>> {
|
|
||||||
for err in errors {
|
|
||||||
if let Some(span) = parse_error_span(err) {
|
|
||||||
return Some(
|
|
||||||
Error::parse_error(err.to_string())
|
|
||||||
.with_source(source)
|
|
||||||
.with_span(span),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DowngradeCtx<'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
|
||||||
bump: &'ir Bump,
|
|
||||||
token: GhostToken<'id>,
|
|
||||||
runtime: &'ctx mut R,
|
|
||||||
source: Source,
|
|
||||||
scopes: Vec<Scope<'ctx, 'id, 'ir>>,
|
|
||||||
with_stack: Vec<GhostRoMaybeThunkRef<'id, 'ir>>,
|
|
||||||
arg_count: u32,
|
|
||||||
thunk_count: &'ctx mut usize,
|
|
||||||
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|
||||||
fn new(
|
|
||||||
bump: &'ir Bump,
|
|
||||||
token: GhostToken<'id>,
|
|
||||||
runtime: &'ctx mut R,
|
|
||||||
global: &'ctx HashMap<StringId, MaybeThunk>,
|
|
||||||
extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
|
|
||||||
thunk_count: &'ctx mut usize,
|
|
||||||
source: Source,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
bump,
|
|
||||||
token,
|
|
||||||
runtime,
|
|
||||||
source,
|
|
||||||
scopes: std::iter::once(Scope::Global(global))
|
|
||||||
.chain(extra_scope)
|
|
||||||
.collect(),
|
|
||||||
thunk_count,
|
|
||||||
arg_count: 0,
|
|
||||||
with_stack: Vec::new(),
|
|
||||||
thunk_scopes: vec![ThunkScope::new_in(bump)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
|
||||||
for DowngradeCtx<'ctx, 'id, 'ir, R>
|
|
||||||
{
|
|
||||||
fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir> {
|
|
||||||
self.bump.alloc(GhostCell::new(expr).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir> {
|
|
||||||
use MaybeThunk::*;
|
|
||||||
let expr = (|| {
|
|
||||||
let expr = match *ir.borrow(&self.token) {
|
|
||||||
Ir::Builtin(x) => Builtin(x),
|
|
||||||
Ir::Int(x) => Int(x),
|
|
||||||
Ir::Float(x) => Float(x),
|
|
||||||
Ir::Bool(x) => Bool(x),
|
|
||||||
Ir::Str(x) => Str(x),
|
|
||||||
Ir::Arg { layer } => Arg { layer },
|
|
||||||
Ir::Builtins => Builtins,
|
|
||||||
Ir::Null => Null,
|
|
||||||
Ir::MaybeThunk(thunk) => return Some(thunk),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
Some(self.bump.alloc(GhostCell::new(expr).into()))
|
|
||||||
})();
|
|
||||||
if let Some(thunk) = expr {
|
|
||||||
return thunk;
|
|
||||||
}
|
|
||||||
let id = ThunkId(*self.thunk_count);
|
|
||||||
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
|
||||||
self.thunk_scopes
|
|
||||||
.last_mut()
|
|
||||||
.expect("no active cache scope")
|
|
||||||
.add_binding(id, ir);
|
|
||||||
self.bump.alloc(GhostCell::new(Thunk(id)).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
|
|
||||||
self.runtime.intern_string(sym)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_sym(&self, id: StringId) -> Symbol<'_> {
|
|
||||||
self.runtime.resolve_string(id).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(
|
|
||||||
&mut self,
|
|
||||||
sym: StringId,
|
|
||||||
span: rnix::TextRange,
|
|
||||||
) -> Result<GhostRoMaybeThunkRef<'id, 'ir>> {
|
|
||||||
for scope in self.scopes.iter().rev() {
|
|
||||||
match scope {
|
|
||||||
&Scope::Global(global_scope) => {
|
|
||||||
if let Some(expr) = global_scope.get(&sym) {
|
|
||||||
return Ok(expr.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&Scope::Repl(repl_bindings) => {
|
|
||||||
if repl_bindings.contains(&sym) {
|
|
||||||
return Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Scope::ScopedImport { keys, .. } => {
|
|
||||||
if keys.contains(&sym) {
|
|
||||||
return Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::ScopedImportBinding(sym)).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Scope::Let(let_scope) => {
|
|
||||||
if let Some(&expr) = let_scope.get(&sym) {
|
|
||||||
return Ok(expr.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&Scope::Param {
|
|
||||||
sym: param_sym,
|
|
||||||
abs_layer,
|
|
||||||
} => {
|
|
||||||
if param_sym == sym {
|
|
||||||
let layers: u8 =
|
|
||||||
self.thunk_scopes.len().try_into().expect("scope too deep!");
|
|
||||||
let layer = layers - abs_layer;
|
|
||||||
return Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::Arg { layer }).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.with_stack.is_empty() {
|
|
||||||
let id = ThunkId(*self.thunk_count);
|
|
||||||
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
|
||||||
let mut namespaces =
|
|
||||||
bumpalo::collections::Vec::with_capacity_in(self.with_stack.len(), self.bump);
|
|
||||||
namespaces.extend(self.with_stack.iter().rev().copied());
|
|
||||||
let body = self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(Ir::WithLookup { sym, namespaces }).into());
|
|
||||||
self.thunk_scopes
|
|
||||||
.last_mut()
|
|
||||||
.expect("no active thunk scope")
|
|
||||||
.add_binding(id, body);
|
|
||||||
Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::Thunk(id)).into()))
|
|
||||||
} else {
|
|
||||||
Err(Error::downgrade_error(
|
|
||||||
format!("'{}' not found", self.resolve_sym(sym)),
|
|
||||||
self.get_current_source(),
|
|
||||||
span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_source(&self) -> Source {
|
|
||||||
self.source.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
|
|
||||||
where
|
|
||||||
F: FnOnce(
|
|
||||||
&mut Self,
|
|
||||||
) -> Result<(
|
|
||||||
bumpalo::collections::Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>,
|
|
||||||
Ret,
|
|
||||||
)>,
|
|
||||||
{
|
|
||||||
let base = *self.thunk_count;
|
|
||||||
*self.thunk_count = self
|
|
||||||
.thunk_count
|
|
||||||
.checked_add(keys.len())
|
|
||||||
.expect("thunk id overflow");
|
|
||||||
let handles = (base..base + keys.len())
|
|
||||||
.map(|id| {
|
|
||||||
&*self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(id))))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let scope = keys.iter().copied().zip(handles.iter().copied()).collect();
|
|
||||||
self.scopes.push(Scope::Let(scope));
|
|
||||||
let (vals, ret) = { f(self)? };
|
|
||||||
self.scopes.pop();
|
|
||||||
assert_eq!(keys.len(), vals.len());
|
|
||||||
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
|
|
||||||
for (i, (val, handle)) in vals.into_iter().zip(handles).enumerate() {
|
|
||||||
let thunk = *val.borrow(&self.token);
|
|
||||||
*handle.borrow_mut(&mut self.token) = thunk;
|
|
||||||
let id = ThunkId(base + i);
|
|
||||||
let ir_ref = self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(Ir::MaybeThunk(handle.into())).into());
|
|
||||||
scope.add_binding(id, ir_ref);
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_param_scope<F, Ret>(&mut self, sym: StringId, f: F) -> Ret
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Ret,
|
|
||||||
{
|
|
||||||
self.scopes.push(Scope::Param {
|
|
||||||
sym,
|
|
||||||
abs_layer: self.thunk_scopes.len().try_into().expect("scope too deep!"),
|
|
||||||
});
|
|
||||||
let mut guard = ScopeGuard { ctx: self };
|
|
||||||
f(guard.as_ctx())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_with_scope<F, Ret>(&mut self, namespace: GhostRoMaybeThunkRef<'id, 'ir>, f: F) -> Ret
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Ret,
|
|
||||||
{
|
|
||||||
self.with_stack.push(namespace);
|
|
||||||
let ret = f(self);
|
|
||||||
self.with_stack.pop();
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_thunk_scope<F, Ret>(
|
|
||||||
&mut self,
|
|
||||||
f: F,
|
|
||||||
) -> (
|
|
||||||
Ret,
|
|
||||||
bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Ret,
|
|
||||||
{
|
|
||||||
if self.thunk_scopes.len() == u8::MAX as usize {
|
|
||||||
panic!("scope too deep!");
|
|
||||||
}
|
|
||||||
self.thunk_scopes.push(ThunkScope::new_in(self.bump));
|
|
||||||
let ret = f(self);
|
|
||||||
(
|
|
||||||
ret,
|
|
||||||
self.thunk_scopes
|
|
||||||
.pop()
|
|
||||||
.expect("no thunk scope left???")
|
|
||||||
.bindings,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bump(&self) -> &'ir bumpalo::Bump {
|
|
||||||
self.bump
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|
||||||
fn downgrade_toplevel(mut self, root: rnix::ast::Expr) -> Result<RawIrRef<'ir>> {
|
|
||||||
let body = root.downgrade(&mut self)?;
|
|
||||||
let thunks = self
|
|
||||||
.thunk_scopes
|
|
||||||
.pop()
|
|
||||||
.expect("no thunk scope left???")
|
|
||||||
.bindings;
|
|
||||||
Ok(Ir::freeze(
|
|
||||||
self.new_expr(Ir::TopLevel { body, thunks }),
|
|
||||||
self.token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ThunkScope<'id, 'ir> {
|
|
||||||
bindings: bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'id, 'ir> ThunkScope<'id, 'ir> {
|
|
||||||
fn new_in(bump: &'ir Bump) -> Self {
|
|
||||||
Self {
|
|
||||||
bindings: bumpalo::collections::Vec::new_in(bump),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_binding(&mut self, id: ThunkId, ir: GhostRoIrRef<'id, 'ir>) {
|
|
||||||
self.bindings.push((id, ir));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extend_bindings(
|
|
||||||
&mut self,
|
|
||||||
iter: impl IntoIterator<Item = (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
|
||||||
) {
|
|
||||||
self.bindings.extend(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Scope<'ctx, 'id, 'ir> {
|
|
||||||
Global(&'ctx HashMap<StringId, MaybeThunk>),
|
|
||||||
Repl(&'ctx HashSet<StringId>),
|
|
||||||
ScopedImport {
|
|
||||||
keys: HashSet<StringId>,
|
|
||||||
slot_id: u32,
|
|
||||||
},
|
|
||||||
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),
|
|
||||||
Param {
|
|
||||||
sym: StringId,
|
|
||||||
abs_layer: u8,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ExtraScope<'ctx> {
|
|
||||||
Repl(&'ctx HashSet<StringId>),
|
|
||||||
ScopedImport {
|
|
||||||
keys: HashSet<StringId>,
|
|
||||||
slot_id: u32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<ExtraScope<'ctx>> for Scope<'ctx, '_, '_> {
|
|
||||||
fn from(value: ExtraScope<'ctx>) -> Self {
|
|
||||||
use ExtraScope::*;
|
|
||||||
match value {
|
|
||||||
ScopedImport { keys, slot_id } => Scope::ScopedImport { keys, slot_id },
|
|
||||||
Repl(scope) => Scope::Repl(scope),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ScopeGuard<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
|
||||||
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir, R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> Drop for ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.ctx.scopes.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
|
||||||
fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|
||||||
self.ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OwnedIr {
|
|
||||||
_bump: Bump,
|
|
||||||
ir: RawIrRef<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnedIr {
|
|
||||||
/// # Safety
|
|
||||||
/// `ir` must be an allocation backed by `bump`. The reference's
|
|
||||||
/// lifetime is extended to `'static` as a placeholder; the stored IR
|
|
||||||
/// must only be re-borrowed via [`OwnedIr::as_ref`], which narrows
|
|
||||||
/// the lifetime back to that of the `&self` borrow. Moving `bump`
|
|
||||||
/// into the struct keeps the underlying allocation live for the
|
|
||||||
/// lifetime of the `OwnedIr`.
|
|
||||||
unsafe fn new(ir: RawIrRef<'_>, bump: Bump) -> Self {
|
|
||||||
Self {
|
|
||||||
_bump: bump,
|
|
||||||
// SAFETY: see function docs - caller guarantees `ir` is in `bump`,
|
|
||||||
// and the `'static` lifetime is a placeholder narrowed by `as_ref`.
|
|
||||||
ir: unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
|
||||||
// SAFETY: narrows the placeholder `'static` lifetime stored in
|
|
||||||
// `self.ir` down to `'ir = &'ir self`. Lifetime shortening is
|
|
||||||
// logically sound for covariant positions; the transmute is only
|
|
||||||
// needed because `RawRef<'ir>` carries `'ir` through a GAT
|
|
||||||
// (`Ref::Ref<T>`), which prevents the compiler from inferring
|
|
||||||
// covariance automatically. The bump arena that backs the IR is
|
|
||||||
// owned by `self._bump`, so the data is live for at least `'ir`.
|
|
||||||
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisassemblerContext for Evaluator {
|
impl DisassemblerContext for Evaluator {
|
||||||
fn get_code(&self) -> &[u8] {
|
fn get_code(&self) -> &[u8] {
|
||||||
&self.code.bytecode
|
&self.code.bytecode
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use fix_common::Value;
|
use fix_lang::Value;
|
||||||
|
|
||||||
use crate::utils::{eval_deep, eval_deep_result};
|
use crate::utils::{eval_deep, eval_deep_result};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::Source;
|
use fix_error::Source;
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
use crate::utils::{eval, eval_result};
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::{Source, SourceType};
|
use fix_error::{Source, SourceType};
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
fn get_lang_dir() -> PathBuf {
|
fn get_lang_dir() -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tests/lang")
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tests/lang")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
use fix_lang::Value;
|
||||||
|
|
||||||
use crate::utils::eval_result;
|
use crate::utils::eval_result;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::{Result, Source};
|
use fix_error::{Result, Source};
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
pub fn eval(expr: &str) -> Value {
|
pub fn eval(expr: &str) -> Value {
|
||||||
Evaluator::new()
|
Evaluator::new()
|
||||||
|
|||||||
Reference in New Issue
Block a user