implement Path type
This commit is contained in:
@@ -2,6 +2,7 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
|
use crate::instructions::misc::canon_path_str;
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use crate::{BytecodeReader, NixNum, Step, VmError, VmRuntimeCtx, VmRuntimeCtxExt as _};
|
use crate::{BytecodeReader, NixNum, Step, VmError, VmRuntimeCtx, VmRuntimeCtxExt as _};
|
||||||
|
|
||||||
@@ -14,7 +15,25 @@ 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)?;
|
||||||
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string(rhs)) {
|
// CppNix: if the LHS is a path, the result is a path obtained by
|
||||||
|
// canonicalizing the concatenated string. RHS may be a path or a
|
||||||
|
// string. (A `string + path` keeps the string-typed result, handled
|
||||||
|
// by the next branch.)
|
||||||
|
if lhs.is::<Path>() {
|
||||||
|
let (Some(ls), Some(rs)) = (ctx.get_string_or_path(lhs), ctx.get_string_or_path(rhs))
|
||||||
|
else {
|
||||||
|
return self.finish_err(fix_error::Error::eval_error(format!(
|
||||||
|
"cannot append {} to a path",
|
||||||
|
rhs.ty()
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
let combined = format!("{ls}{rs}");
|
||||||
|
let canon = canon_path_str(&combined);
|
||||||
|
let sid = ctx.intern_string(canon);
|
||||||
|
self.push(Value::new_inline(crate::value::Path(sid)));
|
||||||
|
return Step::Continue(());
|
||||||
|
}
|
||||||
|
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string_or_path(rhs)) {
|
||||||
let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}")));
|
let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}")));
|
||||||
self.push(Value::new_gc(ns));
|
self.push(Value::new_gc(ns));
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
@@ -232,6 +251,10 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
if lhs.is::<crate::Null>() && rhs.is::<crate::Null>() {
|
if lhs.is::<crate::Null>() && rhs.is::<crate::Null>() {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
// Paths only equal paths (not strings, even if their text matches).
|
||||||
|
if let (Some(a), Some(b)) = (lhs.as_inline::<Path>(), rhs.as_inline::<Path>()) {
|
||||||
|
return Ok(a.0 == b.0);
|
||||||
|
}
|
||||||
if let (Some(a), Some(b)) = (ctx.get_string(lhs), ctx.get_string(rhs)) {
|
if let (Some(a), Some(b)) = (ctx.get_string(lhs), ctx.get_string(rhs)) {
|
||||||
return Ok(a == b);
|
return Ok(a == b);
|
||||||
}
|
}
|
||||||
@@ -294,6 +317,12 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
self.push(Value::new_inline(pred(a.cmp(b))));
|
self.push(Value::new_inline(pred(a.cmp(b))));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
if let (Some(a), Some(b)) = (lhs.as_inline::<Path>(), rhs.as_inline::<Path>()) {
|
||||||
|
let a = ctx.resolve_string(a.0);
|
||||||
|
let b = ctx.resolve_string(b.0);
|
||||||
|
self.push(Value::new_inline(pred(a.cmp(b))));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// TODO: compare other types
|
// TODO: compare other types
|
||||||
Err(crate::vm_err("cannot compare these types"))
|
Err(crate::vm_err("cannot compare these types"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use fix_common::StringId;
|
|||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::value::{AttrSet, NixString, StrictValue};
|
use crate::value::{AttrSet, NixString, Path, StrictValue};
|
||||||
use crate::{BytecodeReader, PrimOp, Step, Value, VmRuntimeCtx, VmRuntimeCtxExt};
|
use crate::{BytecodeReader, PrimOp, Step, Value, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
@@ -74,6 +74,10 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
if val.is::<StringId>() || val.is::<NixString>() {
|
if val.is::<StringId>() || val.is::<NixString>() {
|
||||||
self.push(val.relax());
|
self.push(val.relax());
|
||||||
|
} else if let Some(p) = val.as_inline::<Path>() {
|
||||||
|
// Coercing a path to a string yields the canonical path text.
|
||||||
|
// FIXME: copy to store
|
||||||
|
self.push(Value::new_inline(p.0));
|
||||||
} else {
|
} else {
|
||||||
todo!("coerce other types to string: {:?}", val.ty());
|
todo!("coerce other types to string: {:?}", val.ty());
|
||||||
}
|
}
|
||||||
@@ -120,6 +124,11 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
) -> Step {
|
) -> Step {
|
||||||
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
let dir_id = reader.read_string_id();
|
let dir_id = reader.read_string_id();
|
||||||
|
// Already a path: keep as-is. ResolvePath is idempotent on paths.
|
||||||
|
if let Some(p) = path_val.as_inline::<Path>() {
|
||||||
|
self.push(Value::new_inline(p));
|
||||||
|
return Step::Continue(());
|
||||||
|
}
|
||||||
let path = match ctx.get_string(path_val) {
|
let path = match ctx.get_string(path_val) {
|
||||||
Some(s) => s.to_owned(),
|
Some(s) => s.to_owned(),
|
||||||
None => {
|
None => {
|
||||||
@@ -134,22 +143,15 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
Err(e) => return self.finish_err(e),
|
Err(e) => return self.finish_err(e),
|
||||||
};
|
};
|
||||||
let sid = ctx.intern_string(resolved);
|
let sid = ctx.intern_string(resolved);
|
||||||
self.push(Value::new_inline(sid));
|
self.push(Value::new_inline(Path(sid)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a Nix path literal against `current_dir`.
|
|
||||||
///
|
|
||||||
/// Mirrors nix-js's `op_resolve_path`: absolute paths returned as-is, `~/X`
|
|
||||||
/// expanded against `$HOME`, otherwise joined onto `current_dir`. The result
|
|
||||||
/// is normalized by removing `.` components and resolving `..` lexically
|
|
||||||
/// (no symlink resolution).
|
|
||||||
fn resolve_path_str(current_dir: &str, path: &str) -> Result<String, Box<Error>> {
|
fn resolve_path_str(current_dir: &str, path: &str) -> Result<String, Box<Error>> {
|
||||||
let raw = if path.starts_with('/') {
|
let raw = if path.starts_with('/') {
|
||||||
return Ok(path.to_owned());
|
return Ok(canon_path_str(path));
|
||||||
} else if let Some(rest) = path.strip_prefix("~/") {
|
} else if let Some(rest) = path.strip_prefix("~/") {
|
||||||
#[allow(deprecated)]
|
|
||||||
let mut dir =
|
let mut dir =
|
||||||
std::env::home_dir().ok_or_else(|| Error::eval_error("home dir not defined"))?;
|
std::env::home_dir().ok_or_else(|| Error::eval_error("home dir not defined"))?;
|
||||||
dir.push(rest);
|
dir.push(rest);
|
||||||
@@ -159,8 +161,13 @@ fn resolve_path_str(current_dir: &str, path: &str) -> Result<String, Box<Error>>
|
|||||||
dir.push(path);
|
dir.push(path);
|
||||||
dir
|
dir
|
||||||
};
|
};
|
||||||
|
Ok(canon_path_str(&raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn canon_path_str(path: impl AsRef<std::path::Path>) -> String {
|
||||||
|
let p = path.as_ref();
|
||||||
let mut normalized = PathBuf::new();
|
let mut normalized = PathBuf::new();
|
||||||
for component in raw.components() {
|
for component in p.components() {
|
||||||
match component {
|
match component {
|
||||||
Component::Prefix(p) => normalized.push(p.as_os_str()),
|
Component::Prefix(p) => normalized.push(p.as_os_str()),
|
||||||
Component::RootDir => normalized.push("/"),
|
Component::RootDir => normalized.push("/"),
|
||||||
@@ -171,5 +178,5 @@ fn resolve_path_str(current_dir: &str, path: &str) -> Result<String, Box<Error>>
|
|||||||
Component::Normal(c) => normalized.push(c),
|
Component::Normal(c) => normalized.push(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(normalized.to_string_lossy().into_owned())
|
normalized.to_string_lossy().into_owned()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ pub enum ExtraScope {
|
|||||||
|
|
||||||
trait VmRuntimeCtxExt: VmRuntimeCtx {
|
trait VmRuntimeCtxExt: VmRuntimeCtx {
|
||||||
fn get_string<'a, 'gc: 'a>(&'a self, val: StrictValue<'gc>) -> Option<&'a str>;
|
fn get_string<'a, 'gc: 'a>(&'a self, val: StrictValue<'gc>) -> Option<&'a str>;
|
||||||
|
fn get_string_or_path<'a, 'gc: 'a>(&'a self, val: StrictValue<'gc>) -> Option<&'a str>;
|
||||||
fn get_string_id<'a, 'gc: 'a>(
|
fn get_string_id<'a, 'gc: 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
val: StrictValue<'gc>,
|
val: StrictValue<'gc>,
|
||||||
@@ -114,6 +115,18 @@ impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like `get_string`, but also accepts `Path` values (returning their
|
||||||
|
/// underlying canonical-path string). Use this in places where Nix
|
||||||
|
/// would coerce a path to a string (string interpolation, file IO
|
||||||
|
/// builtins, etc.).
|
||||||
|
fn get_string_or_path<'a, 'gc: 'a>(&'a self, val: StrictValue<'gc>) -> Option<&'a str> {
|
||||||
|
if let Some(p) = val.as_inline::<Path>() {
|
||||||
|
Some(self.resolve_string(p.0))
|
||||||
|
} else {
|
||||||
|
self.get_string(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_string_id<'a, 'gc: 'a>(
|
fn get_string_id<'a, 'gc: 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
val: StrictValue<'gc>,
|
val: StrictValue<'gc>,
|
||||||
@@ -154,6 +167,8 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
Value::String(s)
|
Value::String(s)
|
||||||
} else if let Some(ns) = val.as_gc::<NixString>() {
|
} else if let Some(ns) = val.as_gc::<NixString>() {
|
||||||
Value::String(ns.as_str().to_owned())
|
Value::String(ns.as_str().to_owned())
|
||||||
|
} else if let Some(p) = val.as_inline::<Path>() {
|
||||||
|
Value::Path(self.resolve_string(p.0).to_owned())
|
||||||
} else if let Some(attrs) = val.as_gc::<AttrSet>() {
|
} else if let Some(attrs) = val.as_gc::<AttrSet>() {
|
||||||
let bits = val.to_bits();
|
let bits = val.to_bits();
|
||||||
if attrs.entries.is_empty() {
|
if attrs.entries.is_empty() {
|
||||||
|
|||||||
@@ -1 +1,53 @@
|
|||||||
|
use fix_common::StringId;
|
||||||
|
use fix_error::Error;
|
||||||
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
|
use crate::bytecode_reader::BytecodeReader;
|
||||||
|
use crate::value::*;
|
||||||
|
use crate::{Step, Vm, VmRuntimeCtx};
|
||||||
|
|
||||||
|
impl<'gc> Vm<'gc> {
|
||||||
|
pub(crate) fn primop_to_string(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &mut impl VmRuntimeCtx,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
|
if val.is::<StringId>() || val.is::<NixString>() {
|
||||||
|
return self.return_from_primop(val.relax(), reader);
|
||||||
|
}
|
||||||
|
if let Some(p) = val.as_inline::<Path>() {
|
||||||
|
return self.return_from_primop(Value::new_inline(p.0), reader);
|
||||||
|
}
|
||||||
|
// TODO: derivations / `__toString` / `outPath`,
|
||||||
|
// numbers, lists.
|
||||||
|
self.finish_err(Error::eval_error(format!(
|
||||||
|
"cannot coerce {} to a string",
|
||||||
|
val.ty()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primop_type_of(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
|
let name: &str = match val.ty() {
|
||||||
|
NixType::Int => "int",
|
||||||
|
NixType::Float => "float",
|
||||||
|
NixType::Bool => "bool",
|
||||||
|
NixType::Null => "null",
|
||||||
|
NixType::String => "string",
|
||||||
|
NixType::Path => "path",
|
||||||
|
NixType::AttrSet => "set",
|
||||||
|
NixType::List => "list",
|
||||||
|
NixType::Closure | NixType::PrimOp | NixType::PrimOpApp => "lambda",
|
||||||
|
NixType::Thunk => unreachable!("forced"),
|
||||||
|
};
|
||||||
|
let sid = ctx.intern_string(name);
|
||||||
|
self.return_from_primop(Value::new_inline(sid), reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+25
-13
@@ -7,6 +7,7 @@ use gc_arena::{Gc, Mutation};
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
use crate::bytecode_reader::BytecodeReader;
|
use crate::bytecode_reader::BytecodeReader;
|
||||||
|
use crate::instructions::misc::canon_path_str;
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use crate::{Break, CallFrame, PendingLoad, PendingScope, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
|
use crate::{Break, CallFrame, PendingLoad, PendingScope, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
|
|
||||||
@@ -19,11 +20,11 @@ impl<'gc> Vm<'gc> {
|
|||||||
) -> Step {
|
) -> Step {
|
||||||
// stack: [path]
|
// stack: [path]
|
||||||
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
let path_str = match ctx.get_string(path_val) {
|
let path_str = match ctx.get_string_or_path(path_val) {
|
||||||
Some(s) => s.to_owned(),
|
Some(s) => s.to_owned(),
|
||||||
None => {
|
None => {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
return self.finish_err(Error::eval_error(format!(
|
||||||
"expected a path string, got {}",
|
"expected a path or string, got {}",
|
||||||
path_val.ty()
|
path_val.ty()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -93,11 +94,11 @@ impl<'gc> Vm<'gc> {
|
|||||||
// stack: [scope, path]
|
// stack: [scope, path]
|
||||||
let (scope_attrs, path_val) =
|
let (scope_attrs, path_val) =
|
||||||
self.force_and_retry::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
self.force_and_retry::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
||||||
let path_str = match ctx.get_string(path_val) {
|
let path_str = match ctx.get_string_or_path(path_val) {
|
||||||
Some(s) => s.to_owned(),
|
Some(s) => s.to_owned(),
|
||||||
None => {
|
None => {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
return self.finish_err(Error::eval_error(format!(
|
||||||
"expected a path string, got {}",
|
"expected a path or string, got {}",
|
||||||
path_val.ty()
|
path_val.ty()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -145,17 +146,28 @@ impl<'gc> Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
let path = match ctx.get_string(path_val) {
|
// CppNix: pathExists requires an absolute path. A `Path` value is
|
||||||
Some(s) => s.to_owned(),
|
// always absolute; a string is accepted only if it starts with `/`.
|
||||||
None => {
|
let (path, is_path_value) = if let Some(p) = path_val.as_inline::<Path>() {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
(ctx.resolve_string(p.0).to_owned(), true)
|
||||||
"expected a path string, got {}",
|
} else if let Some(s) = ctx.get_string(path_val) {
|
||||||
path_val.ty()
|
(s.to_owned(), false)
|
||||||
)));
|
} else {
|
||||||
}
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
"expected a path or string, got {}",
|
||||||
|
path_val.ty()
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
|
if !is_path_value && !path.starts_with('/') {
|
||||||
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
"string '{path}' doesn't represent an absolute path"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
// CppNix collapses consecutive slashes and resolves `.` / `..` lexically
|
||||||
|
// before checking. Trailing-slash / trailing-dot mean "must be a directory".
|
||||||
let must_be_dir = path.ends_with('/') || path.ends_with("/.");
|
let must_be_dir = path.ends_with('/') || path.ends_with("/.");
|
||||||
let p = std::path::Path::new(&path);
|
let canon = canon_path_str(&path);
|
||||||
|
let p = std::path::Path::new(&canon);
|
||||||
let exists = if must_be_dir {
|
let exists = if must_be_dir {
|
||||||
std::fs::metadata(p).map(|m| m.is_dir()).unwrap_or(false)
|
std::fs::metadata(p).map(|m| m.is_dir()).unwrap_or(false)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ impl<'gc> Vm<'gc> {
|
|||||||
ScopedImportFinalize => self.primop_scoped_import_finalize(ctx, reader, mc),
|
ScopedImportFinalize => self.primop_scoped_import_finalize(ctx, reader, mc),
|
||||||
|
|
||||||
PathExists => self.primop_path_exists(ctx, reader, mc),
|
PathExists => self.primop_path_exists(ctx, reader, mc),
|
||||||
|
ToPath => self.primop_to_path(ctx, reader, mc),
|
||||||
|
IsPath => self.primop_is_path(reader, mc),
|
||||||
|
ToString => self.primop_to_string(ctx, reader, mc),
|
||||||
|
TypeOf => self.primop_type_of(ctx, reader, mc),
|
||||||
|
|
||||||
phase => todo!("primop phase {phase:?}"),
|
phase => todo!("primop phase {phase:?}"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,46 @@
|
|||||||
|
use fix_error::Error;
|
||||||
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
|
use crate::bytecode_reader::BytecodeReader;
|
||||||
|
use crate::instructions::misc::canon_path_str;
|
||||||
|
use crate::value::*;
|
||||||
|
use crate::{Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
|
|
||||||
|
impl<'gc> Vm<'gc> {
|
||||||
|
pub(crate) fn primop_to_path(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
// coerce to path THEN TO STRING
|
||||||
|
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
|
if let Some(Path(s)) = val.as_inline::<Path>() {
|
||||||
|
return self.return_from_primop(Value::new_inline(s), reader);
|
||||||
|
}
|
||||||
|
let Some(s) = ctx.get_string(val) else {
|
||||||
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
"cannot coerce {} to a path",
|
||||||
|
val.ty()
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
if !s.starts_with('/') {
|
||||||
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
"string '{s}' doesn't represent an absolute path"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let canon = canon_path_str(s);
|
||||||
|
let sid = ctx.intern_string(canon);
|
||||||
|
self.return_from_primop(Value::new_inline(sid), reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primop_is_path(
|
||||||
|
&mut self,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
|
let is_path = val.is::<Path>();
|
||||||
|
self.return_from_primop(Value::new_inline(is_path), reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ define_value_types! {
|
|||||||
Null => RawTag::P3, "Null";
|
Null => RawTag::P3, "Null";
|
||||||
StringId => RawTag::P4, "SmallString";
|
StringId => RawTag::P4, "SmallString";
|
||||||
PrimOp => RawTag::P5, "PrimOp";
|
PrimOp => RawTag::P5, "PrimOp";
|
||||||
|
Path => RawTag::N6, "Path";
|
||||||
}
|
}
|
||||||
gc {
|
gc {
|
||||||
i64 => RawTag::P6, "BigInt";
|
i64 => RawTag::P6, "BigInt";
|
||||||
@@ -289,6 +290,8 @@ impl<'gc> Value<'gc> {
|
|||||||
NixType::PrimOp
|
NixType::PrimOp
|
||||||
} else if self.is::<NixString>() {
|
} else if self.is::<NixString>() {
|
||||||
NixType::String
|
NixType::String
|
||||||
|
} else if self.is::<Path>() {
|
||||||
|
NixType::Path
|
||||||
} else if self.is::<AttrSet>() {
|
} else if self.is::<AttrSet>() {
|
||||||
NixType::AttrSet
|
NixType::AttrSet
|
||||||
} else if self.is::<List>() {
|
} else if self.is::<List>() {
|
||||||
@@ -404,6 +407,20 @@ impl RawStore for StringId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A canonicalized absolute path. Inline value carrying an interned
|
||||||
|
/// `StringId` whose contents are the path's absolute, dot-resolved form.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) struct Path(pub StringId);
|
||||||
|
|
||||||
|
impl RawStore for Path {
|
||||||
|
fn to_val(self, value: &mut RawValue) {
|
||||||
|
self.0.to_val(value);
|
||||||
|
}
|
||||||
|
fn from_val(value: &RawValue) -> Self {
|
||||||
|
Self(StringId::from_val(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Heap-allocated Nix string.
|
/// Heap-allocated Nix string.
|
||||||
///
|
///
|
||||||
/// Stored on the GC heap via `Gc<'gc, NixString>`. The string data itself
|
/// Stored on the GC heap via `Gc<'gc, NixString>`. The string data itself
|
||||||
@@ -659,6 +676,7 @@ pub(crate) enum NixType {
|
|||||||
Bool,
|
Bool,
|
||||||
Null,
|
Null,
|
||||||
String,
|
String,
|
||||||
|
Path,
|
||||||
AttrSet,
|
AttrSet,
|
||||||
List,
|
List,
|
||||||
Thunk,
|
Thunk,
|
||||||
@@ -676,6 +694,7 @@ impl NixType {
|
|||||||
Bool => "a boolean",
|
Bool => "a boolean",
|
||||||
Null => "null",
|
Null => "null",
|
||||||
String => "a string",
|
String => "a string",
|
||||||
|
Path => "a path",
|
||||||
AttrSet => "a set",
|
AttrSet => "a set",
|
||||||
List => "a list",
|
List => "a list",
|
||||||
Thunk => "a thunk",
|
Thunk => "a thunk",
|
||||||
|
|||||||
Reference in New Issue
Block a user