chore: tidy

This commit is contained in:
2026-01-10 19:56:20 +08:00
parent fbf35ba4cd
commit e33770c1bf
23 changed files with 163 additions and 172 deletions

7
.lazy.lua Normal file
View File

@@ -0,0 +1,7 @@
vim.lsp.config("biome", {
root_dir = function (bufnr, on_dir)
on_dir(vim.fn.getcwd())
end
})
return {}

View File

@@ -17,6 +17,13 @@
"lineWidth": 110, "lineWidth": 110,
"lineEnding": "lf" "lineEnding": "lf"
}, },
"linter": {
"rules": {
"style": {
"useNamingConvention": "warn"
}
}
},
"javascript": { "javascript": {
"formatter": { "formatter": {
"arrowParentheses": "always", "arrowParentheses": "always",

View File

@@ -3,33 +3,33 @@
*/ */
import type { NixBool, NixInt, NixNumber, NixValue } from "../types"; import type { NixBool, NixInt, NixNumber, NixValue } from "../types";
import { force_numeric, coerce_numeric, force_int } from "../type-assert"; import { forceNumeric, coerceNumeric, forceInt } from "../type-assert";
export const add = export const add =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): bigint | number => { (b: NixValue): bigint | number => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) + (bv as any); return (av as any) + (bv as any);
}; };
export const sub = export const sub =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): bigint | number => { (b: NixValue): bigint | number => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) - (bv as any); return (av as any) - (bv as any);
}; };
export const mul = export const mul =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): bigint | number => { (b: NixValue): bigint | number => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) * (bv as any); return (av as any) * (bv as any);
}; };
export const div = export const div =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): NixNumber => { (b: NixValue): NixNumber => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
if (bv === 0 || bv === 0n) { if (bv === 0 || bv === 0n) {
throw new RangeError("Division by zero"); throw new RangeError("Division by zero");
@@ -42,28 +42,28 @@ export const div =
export const bitAnd = export const bitAnd =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): NixInt => { (b: NixValue): NixInt => {
const av = force_int(a); const av = forceInt(a);
const bv = force_int(b); const bv = forceInt(b);
return av & bv; return av & bv;
}; };
export const bitOr = export const bitOr =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): NixInt => { (b: NixValue): NixInt => {
const av = force_int(a); const av = forceInt(a);
const bv = force_int(b); const bv = forceInt(b);
return av | bv; return av | bv;
}; };
export const bitXor = export const bitXor =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): NixInt => { (b: NixValue): NixInt => {
const av = force_int(a); const av = forceInt(a);
const bv = force_int(b); const bv = forceInt(b);
return av ^ bv; return av ^ bv;
}; };
export const lessThan = export const lessThan =
(a: NixValue) => (a: NixValue) =>
(b: NixValue): NixBool => (b: NixValue): NixBool =>
force_numeric(a) < force_numeric(b); forceNumeric(a) < forceNumeric(b);

View File

@@ -3,30 +3,30 @@
*/ */
import type { NixValue, NixAttrs, NixList } from "../types"; import type { NixValue, NixAttrs, NixList } from "../types";
import { force_attrs, force_string, force_function, force_list } from "../type-assert"; import { forceAttrs, forceString, forceFunction, forceList } from "../type-assert";
export const attrNames = (set: NixValue): string[] => Object.keys(force_attrs(set)).sort(); export const attrNames = (set: NixValue): string[] => Object.keys(forceAttrs(set)).sort();
export const attrValues = (set: NixValue): NixValue[] => Object.values(force_attrs(set)); export const attrValues = (set: NixValue): NixValue[] => Object.values(forceAttrs(set));
export const getAttr = export const getAttr =
(s: NixValue) => (s: NixValue) =>
(set: NixValue): NixValue => (set: NixValue): NixValue =>
force_attrs(set)[force_string(s)]; forceAttrs(set)[forceString(s)];
export const hasAttr = export const hasAttr =
(s: NixValue) => (s: NixValue) =>
(set: NixValue): boolean => (set: NixValue): boolean =>
Object.hasOwn(force_attrs(set), force_string(s)); Object.hasOwn(forceAttrs(set), forceString(s));
export const mapAttrs = export const mapAttrs =
(f: NixValue) => (f: NixValue) =>
(attrs: NixValue): NixAttrs => { (attrs: NixValue): NixAttrs => {
const new_attrs: NixAttrs = {}; const new_attrs: NixAttrs = {};
const forced_attrs = force_attrs(attrs); const forced_attrs = forceAttrs(attrs);
const forced_f = force_function(f); const forced_f = forceFunction(f);
for (const key in forced_attrs) { for (const key in forced_attrs) {
new_attrs[key] = forced_f(key)(forced_attrs[key]); new_attrs[key] = forceFunction(forced_f(key))(forced_attrs[key]);
} }
return new_attrs; return new_attrs;
}; };
@@ -35,8 +35,8 @@ export const removeAttrs =
(attrs: NixValue) => (attrs: NixValue) =>
(list: NixValue): NixAttrs => { (list: NixValue): NixAttrs => {
const new_attrs: NixAttrs = {}; const new_attrs: NixAttrs = {};
const forced_attrs = force_attrs(attrs); const forced_attrs = forceAttrs(attrs);
const forced_list = force_list(list); const forced_list = forceList(list);
for (const key in forced_attrs) { for (const key in forced_attrs) {
if (!(key in forced_list)) { if (!(key in forced_list)) {
@@ -48,10 +48,10 @@ export const removeAttrs =
export const listToAttrs = (e: NixValue): NixAttrs => { export const listToAttrs = (e: NixValue): NixAttrs => {
const attrs: NixAttrs = {}; const attrs: NixAttrs = {};
const forced_e = [...force_list(e)].reverse(); const forced_e = [...forceList(e)].reverse();
for (const obj of forced_e) { for (const obj of forced_e) {
const item = force_attrs(obj); const item = forceAttrs(obj);
attrs[force_string(item.name)] = item.value; attrs[forceString(item.name)] = item.value;
} }
return attrs; return attrs;
}; };
@@ -59,8 +59,8 @@ export const listToAttrs = (e: NixValue): NixAttrs => {
export const intersectAttrs = export const intersectAttrs =
(e1: NixValue) => (e1: NixValue) =>
(e2: NixValue): NixAttrs => { (e2: NixValue): NixAttrs => {
const f1 = force_attrs(e1); const f1 = forceAttrs(e1);
const f2 = force_attrs(e2); const f2 = forceAttrs(e2);
const attrs: NixAttrs = {}; const attrs: NixAttrs = {};
for (const key of Object.keys(f2)) { for (const key of Object.keys(f2)) {
if (Object.hasOwn(f1, key)) { if (Object.hasOwn(f1, key)) {
@@ -73,9 +73,9 @@ export const intersectAttrs =
export const catAttrs = export const catAttrs =
(attr: NixValue) => (attr: NixValue) =>
(list: NixValue): NixList => { (list: NixValue): NixList => {
const key = force_string(attr); const key = forceString(attr);
return force_list(list) return forceList(list)
.map((set) => force_attrs(set)[key]) .map((set) => forceAttrs(set)[key])
.filter((val) => val !== undefined); .filter((val) => val !== undefined);
}; };
@@ -83,10 +83,10 @@ export const groupBy =
(f: NixValue) => (f: NixValue) =>
(list: NixValue): NixAttrs => { (list: NixValue): NixAttrs => {
const attrs: NixAttrs = {}; const attrs: NixAttrs = {};
const forced_f = force_function(f); const forced_f = forceFunction(f);
const forced_list = force_list(list); const forced_list = forceList(list);
for (const elem of forced_list) { for (const elem of forced_list) {
const key = force_string(forced_f(elem)); const key = forceString(forced_f(elem));
if (!attrs[key]) attrs[key] = []; if (!attrs[key]) attrs[key] = [];
(attrs[key] as NixList).push(elem); (attrs[key] as NixList).push(elem);
} }

View File

@@ -191,6 +191,6 @@ export const coerceToString = (
* @param value - The value to convert to a string * @param value - The value to convert to a string
* @returns The string representation * @returns The string representation
*/ */
export const toString = (value: NixValue): string => { export const toStringFunc = (value: NixValue): string => {
return coerceToString(value, StringCoercionMode.ToString, false); return coerceToString(value, StringCoercionMode.ToString, false);
}; };

View File

@@ -4,7 +4,7 @@
import { CatchableError, type NixValue } from "../types"; import { CatchableError, type NixValue } from "../types";
import { force } from "../thunk"; import { force } from "../thunk";
import { force_string } from "../type-assert"; import { forceString } from "../type-assert";
export const seq = export const seq =
(e1: NixValue) => (e1: NixValue) =>
@@ -24,7 +24,7 @@ export const abort = (s: NixValue): never => {
}; };
export const throwFunc = (s: NixValue): never => { export const throwFunc = (s: NixValue): never => {
throw new CatchableError(force_string(s)); throw new CatchableError(forceString(s));
}; };
export const trace = (e1: NixValue, e2: NixValue): NixValue => { export const trace = (e1: NixValue, e2: NixValue): NixValue => {

View File

@@ -211,7 +211,7 @@ export const builtins: any = {
fromTOML: mkPrimop(conversion.fromTOML, "fromTOML", 1), fromTOML: mkPrimop(conversion.fromTOML, "fromTOML", 1),
toJSON: mkPrimop(conversion.toJSON, "toJSON", 1), toJSON: mkPrimop(conversion.toJSON, "toJSON", 1),
toXML: mkPrimop(conversion.toXML, "toXML", 1), toXML: mkPrimop(conversion.toXML, "toXML", 1),
toString: mkPrimop(conversion.toString, "toString", 1), toString: mkPrimop(conversion.toStringFunc, "toString", 1),
addErrorContext: mkPrimop(misc.addErrorContext, "addErrorContext", 1), addErrorContext: mkPrimop(misc.addErrorContext, "addErrorContext", 1),
appendContext: mkPrimop(misc.appendContext, "appendContext", 1), appendContext: mkPrimop(misc.appendContext, "appendContext", 1),

View File

@@ -3,23 +3,20 @@
* Implemented via Rust ops exposed through deno_core * Implemented via Rust ops exposed through deno_core
*/ */
import { forceString } from "../type-assert";
import type { NixValue } from "../types"; import type { NixValue } from "../types";
import { force_string } from "../type-assert";
// Declare Deno.core.ops global (provided by deno_core runtime) // Declare Deno.core.ops global (provided by deno_core runtime)
export const importFunc = (path: NixValue): NixValue => { export const importFunc = (path: NixValue): NixValue => {
// For MVP: only support string paths // For MVP: only support string paths
// TODO: After implementing path type, also accept path values // TODO: After implementing path type, also accept path values
const pathStr = force_string(path); const pathStr = forceString(path);
// Call Rust op - returns JS code string // Call Rust op - returns JS code string
const code = Deno.core.ops.op_import(pathStr); const code = Deno.core.ops.op_import(pathStr);
// Eval in current context - returns V8 value directly! return Function(`return (${code})`)();
// (0, eval) = indirect eval = global scope
// Wrap in parentheses to ensure object literals are parsed correctly
return (0, eval)(`(${code})`);
}; };
export const scopedImport = export const scopedImport =
@@ -61,7 +58,7 @@ export const readDir = (path: NixValue): never => {
}; };
export const readFile = (path: NixValue): string => { export const readFile = (path: NixValue): string => {
const pathStr = force_string(path); const pathStr = forceString(path);
return Deno.core.ops.op_read_file(pathStr); return Deno.core.ops.op_read_file(pathStr);
}; };
@@ -70,7 +67,7 @@ export const readFileType = (path: NixValue): never => {
}; };
export const pathExists = (path: NixValue): boolean => { export const pathExists = (path: NixValue): boolean => {
const pathStr = force_string(path); const pathStr = forceString(path);
return Deno.core.ops.op_path_exists(pathStr); return Deno.core.ops.op_path_exists(pathStr);
}; };

View File

@@ -5,38 +5,38 @@
import type { NixValue, NixList, NixAttrs } from "../types"; import type { NixValue, NixList, NixAttrs } from "../types";
import { force } from "../thunk"; import { force } from "../thunk";
import { force_list, force_function, force_numeric, force_int } from "../type-assert"; import { forceList, forceFunction, forceInt } from "../type-assert";
export const map = export const map =
(f: NixValue) => (f: NixValue) =>
(list: NixValue): NixList => (list: NixValue): NixList =>
force_list(list).map(force_function(f)); forceList(list).map(forceFunction(f));
export const filter = export const filter =
(f: NixValue) => (f: NixValue) =>
(list: NixValue): NixList => (list: NixValue): NixList =>
force_list(list).filter(force_function(f)); forceList(list).filter(forceFunction(f));
export const length = (e: NixValue): bigint => { export const length = (e: NixValue): bigint => {
const forced = force(e); const forced = force(e);
if (typeof forced === "string") return BigInt(forced.length); if (typeof forced === "string") return BigInt(forced.length);
return BigInt(force_list(forced).length); return BigInt(forceList(forced).length);
}; };
export const head = (list: NixValue): NixValue => force_list(list)[0]; export const head = (list: NixValue): NixValue => forceList(list)[0];
export const tail = (list: NixValue): NixList => force_list(list).slice(1); export const tail = (list: NixValue): NixList => forceList(list).slice(1);
export const elem = export const elem =
(x: NixValue) => (x: NixValue) =>
(xs: NixValue): boolean => (xs: NixValue): boolean =>
force_list(xs).includes(force(x)); forceList(xs).includes(force(x));
export const elemAt = export const elemAt =
(xs: NixValue) => (xs: NixValue) =>
(n: NixValue): NixValue => { (n: NixValue): NixValue => {
const list = force_list(xs); const list = forceList(xs);
const idx = Number(force_int(n)); const idx = Number(forceInt(n));
if (idx < 0 || idx >= list.length) { if (idx < 0 || idx >= list.length) {
throw new RangeError(`Index ${idx} out of bounds for list of length ${list.length}`); throw new RangeError(`Index ${idx} out of bounds for list of length ${list.length}`);
@@ -46,16 +46,16 @@ export const elemAt =
}; };
export const concatLists = (lists: NixValue): NixList => { export const concatLists = (lists: NixValue): NixList => {
return force_list(lists).reduce((acc: NixList, cur: NixValue) => { return forceList(lists).reduce((acc: NixList, cur: NixValue) => {
return acc.concat(force_list(cur)); return acc.concat(forceList(cur));
}, []); }, []);
}; };
export const concatMap = export const concatMap =
(f: NixValue) => (f: NixValue) =>
(lists: NixValue): NixList => { (lists: NixValue): NixList => {
const fn = force_function(f); const fn = forceFunction(f);
return force_list(lists).reduce((acc: NixList, cur: NixValue) => { return forceList(lists).reduce((acc: NixList, cur: NixValue) => {
return acc.concat(force(fn(cur)) as NixList); return acc.concat(force(fn(cur)) as NixList);
}, []); }, []);
}; };
@@ -64,20 +64,20 @@ export const foldlPrime =
(op_fn: NixValue) => (op_fn: NixValue) =>
(nul: NixValue) => (nul: NixValue) =>
(list: NixValue): NixValue => { (list: NixValue): NixValue => {
const forced_op = force_function(op_fn); const forced_op = forceFunction(op_fn);
return force_list(list).reduce((acc: NixValue, cur: NixValue) => { return forceList(list).reduce((acc: NixValue, cur: NixValue) => {
return forced_op(acc)(cur); return forceFunction(forced_op(acc))(cur);
}, nul); }, nul);
}; };
export const sort = export const sort =
(cmp: NixValue) => (cmp: NixValue) =>
(list: NixValue): NixList => { (list: NixValue): NixList => {
const forced_list = [...force_list(list)]; const forced_list = [...forceList(list)];
const forced_cmp = force_function(cmp); const forced_cmp = forceFunction(cmp);
return forced_list.sort((a, b) => { return forced_list.sort((a, b) => {
if (force(forced_cmp(a)(b))) return -1; if (force(forceFunction(forced_cmp(a))(b))) return -1;
if (force(forced_cmp(b)(a))) return 1; if (force(forceFunction(forced_cmp(b))(a))) return 1;
return 0; return 0;
}); });
}; };
@@ -85,8 +85,8 @@ export const sort =
export const partition = export const partition =
(pred: NixValue) => (pred: NixValue) =>
(list: NixValue): NixAttrs => { (list: NixValue): NixAttrs => {
const forced_list = force_list(list); const forced_list = forceList(list);
const forced_pred = force_function(pred); const forced_pred = forceFunction(pred);
const attrs: NixAttrs = { const attrs: NixAttrs = {
right: [], right: [],
wrong: [], wrong: [],
@@ -104,8 +104,8 @@ export const partition =
export const genList = export const genList =
(f: NixValue) => (f: NixValue) =>
(len: NixValue): NixList => { (len: NixValue): NixList => {
const func = force_function(f); const func = forceFunction(f);
const length = force_int(len); const length = forceInt(len);
if (length < 0) { if (length < 0) {
throw new TypeError(`genList length must be non-negative integer, got ${length}`); throw new TypeError(`genList length must be non-negative integer, got ${length}`);
@@ -117,9 +117,9 @@ export const genList =
export const all = export const all =
(pred: NixValue) => (pred: NixValue) =>
(list: NixValue): boolean => (list: NixValue): boolean =>
force_list(list).every(force_function(pred)); forceList(list).every(forceFunction(pred));
export const any = export const any =
(pred: NixValue) => (pred: NixValue) =>
(list: NixValue): boolean => (list: NixValue): boolean =>
force_list(list).some(force_function(pred)); forceList(list).some(forceFunction(pred));

View File

@@ -3,16 +3,16 @@
*/ */
import type { NixValue } from "../types"; import type { NixValue } from "../types";
import { force_numeric } from "../type-assert"; import { forceNumeric } from "../type-assert";
export const ceil = (x: NixValue): bigint => { export const ceil = (x: NixValue): bigint => {
const val = force_numeric(x); const val = forceNumeric(x);
if (typeof val === "bigint") return val; // Already an integer if (typeof val === "bigint") return val; // Already an integer
return BigInt(Math.ceil(val)); // Convert to integer return BigInt(Math.ceil(val)); // Convert to integer
}; };
export const floor = (x: NixValue): bigint => { export const floor = (x: NixValue): bigint => {
const val = force_numeric(x); const val = forceNumeric(x);
if (typeof val === "bigint") return val; // Already an integer if (typeof val === "bigint") return val; // Already an integer
return BigInt(Math.floor(val)); // Convert to integer return BigInt(Math.floor(val)); // Convert to integer
}; };

View File

@@ -3,27 +3,27 @@
*/ */
import type { NixValue } from "../types"; import type { NixValue } from "../types";
import { force_string, force_list, force_int } from "../type-assert"; import { forceString, forceList, forceInt } from "../type-assert";
export const stringLength = (e: NixValue): number => force_string(e).length; export const stringLength = (e: NixValue): number => forceString(e).length;
export const substring = export const substring =
(start: NixValue) => (start: NixValue) =>
(len: NixValue) => (len: NixValue) =>
(s: NixValue): string => { (s: NixValue): string => {
const str = force_string(s); const str = forceString(s);
const startPos = Number(force_int(start)); const startPos = Number(forceInt(start));
const length = Number(force_int(len)); const length = Number(forceInt(len));
return str.substring(startPos, startPos + length); return str.substring(startPos, startPos + length);
}; };
export const concatStringsSep = export const concatStringsSep =
(sep: NixValue) => (sep: NixValue) =>
(list: NixValue): string => (list: NixValue): string =>
force_list(list).join(force_string(sep)); forceList(list).join(forceString(sep));
export const baseNameOf = (x: NixValue): string => { export const baseNameOf = (x: NixValue): string => {
const str = force_string(x); const str = forceString(x);
if (str.length === 0) return ""; if (str.length === 0) return "";
let last = str.length - 1; let last = str.length - 1;

View File

@@ -3,7 +3,7 @@
*/ */
import type { NixValue, NixAttrs, NixBool } from "./types"; import type { NixValue, NixAttrs, NixBool } from "./types";
import { force_attrs, force_string } from "./type-assert"; import { forceAttrs, forceString } from "./type-assert";
import { isAttrs } from "./builtins/type-check"; import { isAttrs } from "./builtins/type-check";
/** /**
@@ -14,7 +14,7 @@ import { isAttrs } from "./builtins/type-check";
* @returns Absolute path string * @returns Absolute path string
*/ */
export const resolvePath = (path: NixValue): string => { export const resolvePath = (path: NixValue): string => {
const path_str = force_string(path); const path_str = forceString(path);
return Deno.core.ops.op_resolve_path(path_str); return Deno.core.ops.op_resolve_path(path_str);
}; };
@@ -28,8 +28,8 @@ export const resolvePath = (path: NixValue): string => {
* @throws Error if obj is null/undefined or key not found * @throws Error if obj is null/undefined or key not found
*/ */
export const select = (obj: NixValue, key: NixValue): NixValue => { export const select = (obj: NixValue, key: NixValue): NixValue => {
const forced_obj = force_attrs(obj); const forced_obj = forceAttrs(obj);
const forced_key = force_string(key); const forced_key = forceString(key);
if (!(forced_key in forced_obj)) { if (!(forced_key in forced_obj)) {
throw new Error(`Attribute '${forced_key}' not found`); throw new Error(`Attribute '${forced_key}' not found`);
@@ -48,8 +48,8 @@ export const select = (obj: NixValue, key: NixValue): NixValue => {
* @returns obj[key] if exists, otherwise default_val * @returns obj[key] if exists, otherwise default_val
*/ */
export const selectWithDefault = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => { export const selectWithDefault = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => {
const attrs = force_attrs(obj); const attrs = forceAttrs(obj);
const forced_key = force_string(key); const forced_key = forceString(key);
if (!(forced_key in attrs)) { if (!(forced_key in attrs)) {
return default_val; return default_val;
@@ -65,7 +65,7 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
let attrs = obj; let attrs = obj;
for (const attr of attrpath.slice(0, -1)) { for (const attr of attrpath.slice(0, -1)) {
const cur = attrs[force_string(attr)]; const cur = attrs[forceString(attr)];
if (!isAttrs(cur)) { if (!isAttrs(cur)) {
return false; return false;
} }
@@ -94,7 +94,7 @@ export const validateParams = (
required: string[] | null, required: string[] | null,
allowed: string[] | null, allowed: string[] | null,
): NixAttrs => { ): NixAttrs => {
const forced_arg = force_attrs(arg); const forced_arg = forceAttrs(arg);
// Check required parameters // Check required parameters
if (required) { if (required) {

View File

@@ -4,7 +4,7 @@
* All functionality is exported via the global `Nix` object * All functionality is exported via the global `Nix` object
*/ */
import { createThunk, force, is_thunk, IS_THUNK } from "./thunk"; import { createThunk, force, isThunk, IS_THUNK } from "./thunk";
import { select, selectWithDefault, validateParams, resolvePath, hasAttr } from "./helpers"; import { select, selectWithDefault, validateParams, resolvePath, hasAttr } from "./helpers";
import { op } from "./operators"; import { op } from "./operators";
import { builtins, PRIMOP_METADATA } from "./builtins"; import { builtins, PRIMOP_METADATA } from "./builtins";
@@ -18,7 +18,7 @@ export type NixRuntime = typeof Nix;
export const Nix = { export const Nix = {
createThunk, createThunk,
force, force,
is_thunk, isThunk,
IS_THUNK, IS_THUNK,
hasAttr, hasAttr,

View File

@@ -5,7 +5,7 @@
import type { NixValue, NixList, NixAttrs } from "./types"; import type { NixValue, NixList, NixAttrs } from "./types";
import { force } from "./thunk"; import { force } from "./thunk";
import { force_numeric, force_list, force_attrs, coerce_numeric } from "./type-assert"; import { forceNumeric, forceList, forceAttrs, coerceNumeric } from "./type-assert";
/** /**
* Operator object exported as Nix.op * Operator object exported as Nix.op
@@ -15,22 +15,22 @@ export const op = {
// Arithmetic operators - preserve int/float distinction // Arithmetic operators - preserve int/float distinction
add: (a: NixValue, b: NixValue): bigint | number => { add: (a: NixValue, b: NixValue): bigint | number => {
// FIXME: String & Path // FIXME: String & Path
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) + (bv as any); return (av as any) + (bv as any);
}, },
sub: (a: NixValue, b: NixValue): bigint | number => { sub: (a: NixValue, b: NixValue): bigint | number => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) - (bv as any); return (av as any) - (bv as any);
}, },
mul: (a: NixValue, b: NixValue): bigint | number => { mul: (a: NixValue, b: NixValue): bigint | number => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) * (bv as any); return (av as any) * (bv as any);
}, },
div: (a: NixValue, b: NixValue): bigint | number => { div: (a: NixValue, b: NixValue): bigint | number => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
if (bv === 0 || bv === 0n) { if (bv === 0 || bv === 0n) {
throw new RangeError("Division by zero"); throw new RangeError("Division by zero");
@@ -54,22 +54,22 @@ export const op = {
}, },
lt: (a: NixValue, b: NixValue): boolean => { lt: (a: NixValue, b: NixValue): boolean => {
// FIXME: Non-numeric // FIXME: Non-numeric
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) < (bv as any); return (av as any) < (bv as any);
}, },
lte: (a: NixValue, b: NixValue): boolean => { lte: (a: NixValue, b: NixValue): boolean => {
// FIXME: Non-numeric // FIXME: Non-numeric
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) <= (bv as any); return (av as any) <= (bv as any);
}, },
gt: (a: NixValue, b: NixValue): boolean => { gt: (a: NixValue, b: NixValue): boolean => {
// FIXME: Non-numeric // FIXME: Non-numeric
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) > (bv as any); return (av as any) > (bv as any);
}, },
gte: (a: NixValue, b: NixValue): boolean => { gte: (a: NixValue, b: NixValue): boolean => {
// FIXME: Non-numeric // FIXME: Non-numeric
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerceNumeric(forceNumeric(a), forceNumeric(b));
return (av as any) >= (bv as any); return (av as any) >= (bv as any);
}, },
@@ -81,11 +81,11 @@ export const op = {
// List concatenation // List concatenation
concat: (a: NixValue, b: NixValue): NixList => { concat: (a: NixValue, b: NixValue): NixList => {
return Array.prototype.concat.call(force_list(a), force_list(b)); return Array.prototype.concat.call(forceList(a), forceList(b));
}, },
// Attribute set update (merge) // Attribute set update (merge)
update: (a: NixValue, b: NixValue): NixAttrs => { update: (a: NixValue, b: NixValue): NixAttrs => {
return { ...force_attrs(a), ...force_attrs(b) }; return { ...forceAttrs(a), ...forceAttrs(b) };
}, },
}; };

View File

@@ -34,7 +34,7 @@ export class NixThunk implements NixThunkInterface {
* @param value - Value to check * @param value - Value to check
* @returns true if value is a NixThunk * @returns true if value is a NixThunk
*/ */
export const is_thunk = (value: unknown): value is NixThunkInterface => { export const isThunk = (value: unknown): value is NixThunkInterface => {
return value !== null && typeof value === "object" && IS_THUNK in value && value[IS_THUNK] === true; return value !== null && typeof value === "object" && IS_THUNK in value && value[IS_THUNK] === true;
}; };
@@ -47,7 +47,7 @@ export const is_thunk = (value: unknown): value is NixThunkInterface => {
* @returns The forced/evaluated value * @returns The forced/evaluated value
*/ */
export const force = (value: NixValue): NixStrictValue => { export const force = (value: NixValue): NixStrictValue => {
if (!is_thunk(value)) { if (!isThunk(value)) {
return value; return value;
} }

View File

@@ -5,7 +5,6 @@
import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber } from "./types"; import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber } from "./types";
import { force } from "./thunk"; import { force } from "./thunk";
import { isAttrs } from "./builtins/type-check";
const typeName = (value: NixValue): string => { const typeName = (value: NixValue): string => {
const val = force(value); const val = force(value);
@@ -26,7 +25,7 @@ const typeName = (value: NixValue): string => {
* Force a value and assert it's a list * Force a value and assert it's a list
* @throws TypeError if value is not a list after forcing * @throws TypeError if value is not a list after forcing
*/ */
export const force_list = (value: NixValue): NixList => { export const forceList = (value: NixValue): NixList => {
const forced = force(value); const forced = force(value);
if (!Array.isArray(forced)) { if (!Array.isArray(forced)) {
throw new TypeError(`Expected list, got ${typeName(forced)}`); throw new TypeError(`Expected list, got ${typeName(forced)}`);
@@ -38,7 +37,7 @@ export const force_list = (value: NixValue): NixList => {
* Force a value and assert it's a function * Force a value and assert it's a function
* @throws TypeError if value is not a function after forcing * @throws TypeError if value is not a function after forcing
*/ */
export const force_function = (value: NixValue): NixFunction => { export const forceFunction = (value: NixValue): NixFunction => {
const forced = force(value); const forced = force(value);
if (typeof forced !== "function") { if (typeof forced !== "function") {
throw new TypeError(`Expected function, got ${typeName(forced)}`); throw new TypeError(`Expected function, got ${typeName(forced)}`);
@@ -50,7 +49,7 @@ export const force_function = (value: NixValue): NixFunction => {
* Force a value and assert it's an attribute set * Force a value and assert it's an attribute set
* @throws TypeError if value is not an attribute set after forcing * @throws TypeError if value is not an attribute set after forcing
*/ */
export const force_attrs = (value: NixValue): NixAttrs => { export const forceAttrs = (value: NixValue): NixAttrs => {
const forced = force(value); const forced = force(value);
if (typeof forced !== "object" || Array.isArray(forced) || forced === null) { if (typeof forced !== "object" || Array.isArray(forced) || forced === null) {
throw new TypeError(`Expected attribute set, got ${typeName(forced)}`); throw new TypeError(`Expected attribute set, got ${typeName(forced)}`);
@@ -62,7 +61,7 @@ export const force_attrs = (value: NixValue): NixAttrs => {
* Force a value and assert it's a string * Force a value and assert it's a string
* @throws TypeError if value is not a string after forcing * @throws TypeError if value is not a string after forcing
*/ */
export const force_string = (value: NixValue): string => { export const forceString = (value: NixValue): string => {
const forced = force(value); const forced = force(value);
if (typeof forced !== "string") { if (typeof forced !== "string") {
throw new TypeError(`Expected string, got ${typeName(forced)}`); throw new TypeError(`Expected string, got ${typeName(forced)}`);
@@ -74,7 +73,7 @@ export const force_string = (value: NixValue): string => {
* Force a value and assert it's a boolean * Force a value and assert it's a boolean
* @throws TypeError if value is not a boolean after forcing * @throws TypeError if value is not a boolean after forcing
*/ */
export const force_bool = (value: NixValue): boolean => { export const forceBool = (value: NixValue): boolean => {
const forced = force(value); const forced = force(value);
if (typeof forced !== "boolean") { if (typeof forced !== "boolean") {
throw new TypeError(`Expected boolean, got ${typeName(forced)}`); throw new TypeError(`Expected boolean, got ${typeName(forced)}`);
@@ -86,7 +85,7 @@ export const force_bool = (value: NixValue): boolean => {
* Force a value and extract int value * Force a value and extract int value
* @throws TypeError if value is not an int * @throws TypeError if value is not an int
*/ */
export const force_int = (value: NixValue): NixInt => { export const forceInt = (value: NixValue): NixInt => {
const forced = force(value); const forced = force(value);
if (typeof forced === "bigint") { if (typeof forced === "bigint") {
return forced; return forced;
@@ -98,7 +97,7 @@ export const force_int = (value: NixValue): NixInt => {
* Force a value and extract float value * Force a value and extract float value
* @throws TypeError if value is not a float * @throws TypeError if value is not a float
*/ */
export const force_float = (value: NixValue): NixFloat => { export const forceFloat = (value: NixValue): NixFloat => {
const forced = force(value); const forced = force(value);
if (typeof forced === "number") { if (typeof forced === "number") {
return forced; return forced;
@@ -110,7 +109,7 @@ export const force_float = (value: NixValue): NixFloat => {
* Force a value and extract numeric value (int or float) * Force a value and extract numeric value (int or float)
* @throws TypeError if value is not a numeric type * @throws TypeError if value is not a numeric type
*/ */
export const force_numeric = (value: NixValue): NixNumber => { export const forceNumeric = (value: NixValue): NixNumber => {
const forced = force(value); const forced = force(value);
if (typeof forced === "bigint" || typeof forced === "number") { if (typeof forced === "bigint" || typeof forced === "number") {
return forced; return forced;
@@ -123,7 +122,7 @@ export const force_numeric = (value: NixValue): NixNumber => {
* Rule: If either is float, convert both to float; otherwise keep as bigint * Rule: If either is float, convert both to float; otherwise keep as bigint
* @returns [a, b] tuple of coerced values * @returns [a, b] tuple of coerced values
*/ */
export const coerce_numeric = (a: NixNumber, b: NixNumber): [NixFloat, NixFloat] | [NixInt, NixInt] => { export const coerceNumeric = (a: NixNumber, b: NixNumber): [NixFloat, NixFloat] | [NixInt, NixInt] => {
const aIsInt = typeof a === "bigint"; const aIsInt = typeof a === "bigint";
const bIsInt = typeof b === "bigint"; const bIsInt = typeof b === "bigint";

View File

@@ -15,7 +15,7 @@ export type NixNull = null;
// Nix composite types // Nix composite types
export type NixList = NixValue[]; export type NixList = NixValue[];
export type NixAttrs = { [key: string]: NixValue }; export type NixAttrs = { [key: string]: NixValue };
export type NixFunction = (...args: any[]) => any; export type NixFunction = (arg: NixValue) => NixValue;
/** /**
* Interface for lazy thunk values * Interface for lazy thunk values
@@ -42,11 +42,7 @@ export type NixStrictValue = Exclude<NixValue, NixThunkInterface>;
* CatchableError: Error type thrown by `builtins.throw` * CatchableError: Error type thrown by `builtins.throw`
* This can be caught by `builtins.tryEval` * This can be caught by `builtins.tryEval`
*/ */
export class CatchableError extends Error { export class CatchableError extends Error {}
constructor(msg: string) {
super(msg);
}
}
// Operator function signatures // Operator function signatures
export type BinaryOp<T = NixValue, U = NixValue, R = NixValue> = (a: T, b: U) => R; export type BinaryOp<T = NixValue, U = NixValue, R = NixValue> = (a: T, b: U) => R;

View File

@@ -199,11 +199,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Let {
} }
let body = ctx.get_ir(self.body).compile(ctx); let body = ctx.get_ir(self.body).compile(ctx);
format!( format!("(()=>{{{}; return {}}})()", js_statements.join(";"), body)
"(()=>{{{}; return {}}})()",
js_statements.join(";"),
body
)
} }
} }
@@ -293,7 +289,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
.map(|part| { .map(|part| {
let compiled = ctx.get_ir(*part).compile(ctx); let compiled = ctx.get_ir(*part).compile(ctx);
// TODO: copyToStore // TODO: copyToStore
format!("Nix.coerceToString({}, Nix.StringCoercionMode.Interpolation, false)", compiled) format!(
"Nix.coerceToString({}, Nix.StringCoercionMode.Interpolation, false)",
compiled
)
}) })
.collect(); .collect();

View File

@@ -77,7 +77,6 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
path_str path_str
} else { } else {
let current_dir = ctx.get_current_dir(); let current_dir = ctx.get_current_dir();
dbg!(&current_dir);
current_dir current_dir
.join(&path_str) .join(&path_str)
@@ -172,8 +171,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
// rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; } // rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; }
let entries: Vec<_> = self.entries().collect(); let entries: Vec<_> = self.entries().collect();
let (binding_sccs, body) = let (binding_sccs, body) = downgrade_let_bindings(entries, ctx, |ctx, binding_keys| {
downgrade_let_bindings(entries, ctx, |ctx, binding_keys| {
// Create plain attrset as body with inherit // Create plain attrset as body with inherit
let mut attrs = AttrSet { let mut attrs = AttrSet {
stcs: HashMap::new(), stcs: HashMap::new(),
@@ -188,13 +186,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
Ok(ctx.new_expr(attrs.to_ir())) Ok(ctx.new_expr(attrs.to_ir()))
})?; })?;
Ok(ctx.new_expr( Ok(ctx.new_expr(Let { body, binding_sccs }.to_ir()))
Let {
body,
binding_sccs,
}
.to_ir(),
))
} }
} }
@@ -299,13 +291,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LetIn {
let (binding_sccs, body) = let (binding_sccs, body) =
downgrade_let_bindings(entries, ctx, |ctx, _binding_keys| body_expr.downgrade(ctx))?; downgrade_let_bindings(entries, ctx, |ctx, _binding_keys| body_expr.downgrade(ctx))?;
Ok(ctx.new_expr( Ok(ctx.new_expr(Let { body, binding_sccs }.to_ir()))
Let {
body,
binding_sccs,
}
.to_ir(),
))
} }
} }

View File

@@ -323,7 +323,7 @@ where
body, body,
scc_info, scc_info,
required_params: required, required_params: required,
allowed_params: allowed allowed_params: allowed,
}) })
} }
@@ -348,7 +348,11 @@ where
F: FnOnce(&mut Ctx, &[SymId]) -> Result<ExprId>, F: FnOnce(&mut Ctx, &[SymId]) -> Result<ExprId>,
{ {
let slots: Vec<_> = ctx.reserve_slots(binding_keys.len()).collect(); let slots: Vec<_> = ctx.reserve_slots(binding_keys.len()).collect();
let let_bindings: HashMap<_, _> = binding_keys.iter().copied().zip(slots.iter().copied()).collect(); let let_bindings: HashMap<_, _> = binding_keys
.iter()
.copied()
.zip(slots.iter().copied())
.collect();
ctx.push_dep_tracker(&slots); ctx.push_dep_tracker(&slots);

View File

@@ -77,10 +77,7 @@ fn nested_function_parameters() {
#[test] #[test]
fn pattern_param_simple_reference_in_default() { fn pattern_param_simple_reference_in_default() {
assert_eq!( assert_eq!(eval("({ a, b ? a }: b) { a = 10; }"), Value::Int(10));
eval("({ a, b ? a }: b) { a = 10; }"),
Value::Int(10)
);
} }
#[test] #[test]

View File

@@ -90,8 +90,7 @@ fn import_with_complex_dependency_graph() {
std::fs::write(&utils_path, "{ double = x: x * 2; }").unwrap(); std::fs::write(&utils_path, "{ double = x: x * 2; }").unwrap();
let math_path = temp_dir.path().join("math.nix"); let math_path = temp_dir.path().join("math.nix");
let math_content = let math_content = r#"let utils = import ./utils.nix; in { triple = x: x + utils.double x; }"#;
r#"let utils = import ./utils.nix; in { triple = x: x + utils.double x; }"#;
std::fs::write(&math_path, math_content).unwrap(); std::fs::write(&math_path, math_content).unwrap();
let main_path = temp_dir.path().join("main.nix"); let main_path = temp_dir.path().join("main.nix");

View File

@@ -5,7 +5,10 @@ use utils::eval;
#[test] #[test]
fn string_returns_as_is() { fn string_returns_as_is() {
assert_eq!(eval(r#"toString "hello""#), Value::String("hello".to_string())); assert_eq!(
eval(r#"toString "hello""#),
Value::String("hello".to_string())
);
} }
#[test] #[test]
@@ -67,10 +70,7 @@ fn list_with_multiple_empty_lists() {
eval("toString [1 [] [] 2]"), eval("toString [1 [] [] 2]"),
Value::String("1 2".to_string()) Value::String("1 2".to_string())
); );
assert_eq!( assert_eq!(eval("toString [[] [] 1]"), Value::String("1".to_string()));
eval("toString [[] [] 1]"),
Value::String("1".to_string())
);
} }
#[test] #[test]
@@ -112,7 +112,9 @@ fn attrs_with_to_string_method() {
#[test] #[test]
fn attrs_to_string_self_reference() { fn attrs_to_string_self_reference() {
assert_eq!( assert_eq!(
eval(r#"let obj = { x = 42; __toString = self: "x is ${toString self.x}"; }; in toString obj"#), eval(
r#"let obj = { x = 42; __toString = self: "x is ${toString self.x}"; }; in toString obj"#
),
Value::String("x is 42".to_string()) Value::String("x is 42".to_string())
); );
} }
@@ -120,9 +122,7 @@ fn attrs_to_string_self_reference() {
#[test] #[test]
fn attrs_to_string_priority() { fn attrs_to_string_priority() {
assert_eq!( assert_eq!(
eval( eval(r#"toString { __toString = self: "custom"; outPath = "/nix/store/foo"; }"#),
r#"toString { __toString = self: "custom"; outPath = "/nix/store/foo"; }"#
),
Value::String("custom".to_string()) Value::String("custom".to_string())
); );
} }