optimize: type check

This commit is contained in:
2026-02-18 13:03:40 +08:00
parent ae5febd5dd
commit 782092b91e
6 changed files with 40 additions and 35 deletions

View File

@@ -2,11 +2,16 @@
* Conversion and serialization builtin functions
*/
import { addBuiltContext, mkStringWithContext, type NixStringContext } from "../string-context";
import {
addBuiltContext,
mkStringWithContext,
type NixStringContext,
StringWithContext,
} from "../string-context";
import { force, isThunk } from "../thunk";
import { forceFunction, forceStringNoCtx } from "../type-assert";
import type { NixString, NixValue } from "../types";
import { HAS_CONTEXT, IS_PATH, isNixPath, isStringWithContext } from "../types";
import { isNixPath, isStringWithContext, NixPath } from "../types";
import { isAttrs, isPath, typeOf } from "./type-check";
export const fromJSON = (e: NixValue): NixValue => {
@@ -306,13 +311,13 @@ export const nixValueToJson = (
if (typeof v === "number") return v;
if (typeof v === "boolean") return v;
if (typeof v === "string") return v;
if (typeof v === "object" && HAS_CONTEXT in v) {
if (v instanceof StringWithContext) {
for (const elem of v.context) {
outContext.add(elem);
}
return v.value;
}
if (typeof v === "object" && IS_PATH in v) {
if (v instanceof NixPath) {
if (copyToStore) {
const storePath = Deno.core.ops.op_copy_path_to_store(v.value);
outContext.add(storePath);

View File

@@ -11,8 +11,8 @@ import {
forceStringNoCtx,
forceStringValue,
} from "../type-assert";
import type { NixAttrs, NixPath, NixString, NixValue } from "../types";
import { CatchableError, IS_PATH, isNixPath } from "../types";
import type { NixAttrs, NixString, NixValue } from "../types";
import { CatchableError, isNixPath, NixPath } from "../types";
import { coerceToPath, coerceToString, StringCoercionMode } from "./conversion";
import { baseNameOf } from "./path";
import { isAttrs, isPath, isString } from "./type-check";
@@ -475,13 +475,13 @@ export const findFile =
suffix.length > 0 ? Deno.core.ops.op_resolve_path(suffix, resolvedPath) : resolvedPath;
if (Deno.core.ops.op_path_exists(candidatePath)) {
return { [IS_PATH]: true, value: candidatePath };
return new NixPath(candidatePath);
}
}
if (lookupPathStr.startsWith("nix/")) {
// FIXME: special path type
return { [IS_PATH]: true, value: `<${lookupPathStr}>` };
return new NixPath(`<${lookupPathStr}>`);
}
throw new CatchableError(`file '${lookupPathStr}' was not found in the Nix search path`);

View File

@@ -1,4 +1,4 @@
import { IS_PATH, type NixPath } from "./types";
import { NixPath } from "./types";
const canonicalizePath = (path: string): string => {
const parts: string[] = [];
@@ -30,7 +30,7 @@ const canonicalizePath = (path: string): string => {
};
export const mkPath = (value: string): NixPath => {
return { [IS_PATH]: true, value: canonicalizePath(value) };
return new NixPath(canonicalizePath(value));
};
export const getPathValue = (p: NixPath): string => {

View File

@@ -22,18 +22,22 @@ export type StringContextElem = StringContextOpaque | StringContextDrvDeep | Str
export type NixStringContext = Set<string>;
export interface StringWithContext {
readonly [HAS_CONTEXT]: true;
export class StringWithContext {
readonly [HAS_CONTEXT] = true as const;
value: string;
context: NixStringContext;
constructor(value: string, context: NixStringContext) {
this.value = value;
this.context = context;
}
}
export const isStringWithContext = (v: NixStrictValue): v is StringWithContext => {
return typeof v === "object" && v !== null && HAS_CONTEXT in v;
return v instanceof StringWithContext;
};
export const mkStringWithContext = (value: string, context: NixStringContext): StringWithContext => {
return { [HAS_CONTEXT]: true, value, context };
return new StringWithContext(value, context);
};
export const mkPlainString = (value: string): string => value;

View File

@@ -1,7 +1,7 @@
import { isAttrs, isList } from "./builtins/type-check";
import { HAS_CONTEXT } from "./string-context";
import type { NixAttrs, NixStrictValue, NixThunkInterface, NixValue } from "./types";
import { IS_PATH } from "./types";
import { StringWithContext } from "./string-context";
import type { NixAttrs, NixStrictValue, NixValue } from "./types";
import { NixPath } from "./types";
export const IS_THUNK = Symbol("is_thunk");
@@ -21,8 +21,7 @@ export const DEBUG_THUNKS = { enabled: true };
* - Evaluating (blackhole): func is undefined, result is undefined
* - Evaluated: func is undefined, result is defined
*/
export class NixThunk implements NixThunkInterface {
[key: symbol]: unknown;
export class NixThunk {
readonly [IS_THUNK] = true as const;
func: (() => NixValue) | undefined;
result: NixStrictValue | undefined;
@@ -42,8 +41,8 @@ export class NixThunk implements NixThunkInterface {
}
}
export const isThunk = (value: NixValue): value is NixThunkInterface => {
return value !== null && typeof value === "object" && IS_THUNK in value && value[IS_THUNK] === true;
export const isThunk = (value: NixValue): value is NixThunk => {
return value instanceof NixThunk;
};
/**
@@ -112,7 +111,7 @@ export const force = (value: NixValue): NixStrictValue => {
}
};
export const createThunk = (func: () => NixValue, label?: string): NixThunkInterface => {
export const createThunk = (func: () => NixValue, label?: string): NixThunk => {
return new NixThunk(func, label);
};
@@ -142,7 +141,7 @@ export const forceDeep = (value: NixValue, seen: WeakSet<object> = new WeakSet()
seen.add(forced);
}
if (HAS_CONTEXT in forced || IS_PATH in forced) {
if (forced instanceof StringWithContext || forced instanceof NixPath) {
return forced;
}

View File

@@ -1,18 +1,21 @@
import { HAS_CONTEXT, isStringWithContext, type StringWithContext } from "./string-context";
import { type CYCLE_MARKER, force, IS_THUNK } from "./thunk";
import { type CYCLE_MARKER, force, type NixThunk } from "./thunk";
import { forceAttrs, forceStringNoCtx } from "./type-assert";
export { HAS_CONTEXT, isStringWithContext };
export type { StringWithContext };
export const IS_PATH = Symbol("IS_PATH");
export interface NixPath {
readonly [IS_PATH]: true;
export class NixPath {
readonly [IS_PATH] = true as const;
value: string;
constructor(value: string) {
this.value = value;
}
}
export const isNixPath = (v: NixStrictValue): v is NixPath => {
return typeof v === "object" && v !== null && IS_PATH in v;
return v instanceof NixPath;
};
export type NixInt = bigint;
@@ -109,12 +112,6 @@ export const mkAttrsWithPos = (
return attrs;
};
export interface NixThunkInterface {
readonly [IS_THUNK]: true;
func: (() => NixValue) | undefined;
result: NixStrictValue | undefined;
}
export type NixPrimitive = NixNull | NixBool | NixInt | NixFloat | NixString;
export type NixValue =
| NixPrimitive
@@ -122,8 +119,8 @@ export type NixValue =
| NixList
| NixAttrs
| NixFunction
| NixThunkInterface
| NixThunk
| typeof CYCLE_MARKER;
export type NixStrictValue = Exclude<NixValue, NixThunkInterface>;
export type NixStrictValue = Exclude<NixValue, NixThunk>;
export class CatchableError extends Error {}