chore: tidy
This commit is contained in:
7
.lazy.lua
Normal file
7
.lazy.lua
Normal file
@@ -0,0 +1,7 @@
|
||||
vim.lsp.config("biome", {
|
||||
root_dir = function (bufnr, on_dir)
|
||||
on_dir(vim.fn.getcwd())
|
||||
end
|
||||
})
|
||||
|
||||
return {}
|
||||
@@ -17,6 +17,13 @@
|
||||
"lineWidth": 110,
|
||||
"lineEnding": "lf"
|
||||
},
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useNamingConvention": "warn"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"arrowParentheses": "always",
|
||||
|
||||
@@ -3,33 +3,33 @@
|
||||
*/
|
||||
|
||||
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 =
|
||||
(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);
|
||||
};
|
||||
|
||||
export const 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);
|
||||
};
|
||||
|
||||
export const 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);
|
||||
};
|
||||
|
||||
export const div =
|
||||
(a: NixValue) =>
|
||||
(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) {
|
||||
throw new RangeError("Division by zero");
|
||||
@@ -42,28 +42,28 @@ export const div =
|
||||
export const bitAnd =
|
||||
(a: NixValue) =>
|
||||
(b: NixValue): NixInt => {
|
||||
const av = force_int(a);
|
||||
const bv = force_int(b);
|
||||
const av = forceInt(a);
|
||||
const bv = forceInt(b);
|
||||
return av & bv;
|
||||
};
|
||||
|
||||
export const bitOr =
|
||||
(a: NixValue) =>
|
||||
(b: NixValue): NixInt => {
|
||||
const av = force_int(a);
|
||||
const bv = force_int(b);
|
||||
const av = forceInt(a);
|
||||
const bv = forceInt(b);
|
||||
return av | bv;
|
||||
};
|
||||
|
||||
export const bitXor =
|
||||
(a: NixValue) =>
|
||||
(b: NixValue): NixInt => {
|
||||
const av = force_int(a);
|
||||
const bv = force_int(b);
|
||||
const av = forceInt(a);
|
||||
const bv = forceInt(b);
|
||||
return av ^ bv;
|
||||
};
|
||||
|
||||
export const lessThan =
|
||||
(a: NixValue) =>
|
||||
(b: NixValue): NixBool =>
|
||||
force_numeric(a) < force_numeric(b);
|
||||
forceNumeric(a) < forceNumeric(b);
|
||||
|
||||
@@ -3,30 +3,30 @@
|
||||
*/
|
||||
|
||||
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 =
|
||||
(s: NixValue) =>
|
||||
(set: NixValue): NixValue =>
|
||||
force_attrs(set)[force_string(s)];
|
||||
forceAttrs(set)[forceString(s)];
|
||||
|
||||
export const hasAttr =
|
||||
(s: NixValue) =>
|
||||
(set: NixValue): boolean =>
|
||||
Object.hasOwn(force_attrs(set), force_string(s));
|
||||
Object.hasOwn(forceAttrs(set), forceString(s));
|
||||
|
||||
export const mapAttrs =
|
||||
(f: NixValue) =>
|
||||
(attrs: NixValue): NixAttrs => {
|
||||
const new_attrs: NixAttrs = {};
|
||||
const forced_attrs = force_attrs(attrs);
|
||||
const forced_f = force_function(f);
|
||||
const forced_attrs = forceAttrs(attrs);
|
||||
const forced_f = forceFunction(f);
|
||||
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;
|
||||
};
|
||||
@@ -35,8 +35,8 @@ export const removeAttrs =
|
||||
(attrs: NixValue) =>
|
||||
(list: NixValue): NixAttrs => {
|
||||
const new_attrs: NixAttrs = {};
|
||||
const forced_attrs = force_attrs(attrs);
|
||||
const forced_list = force_list(list);
|
||||
const forced_attrs = forceAttrs(attrs);
|
||||
const forced_list = forceList(list);
|
||||
|
||||
for (const key in forced_attrs) {
|
||||
if (!(key in forced_list)) {
|
||||
@@ -48,10 +48,10 @@ export const removeAttrs =
|
||||
|
||||
export const listToAttrs = (e: NixValue): NixAttrs => {
|
||||
const attrs: NixAttrs = {};
|
||||
const forced_e = [...force_list(e)].reverse();
|
||||
const forced_e = [...forceList(e)].reverse();
|
||||
for (const obj of forced_e) {
|
||||
const item = force_attrs(obj);
|
||||
attrs[force_string(item.name)] = item.value;
|
||||
const item = forceAttrs(obj);
|
||||
attrs[forceString(item.name)] = item.value;
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
@@ -59,8 +59,8 @@ export const listToAttrs = (e: NixValue): NixAttrs => {
|
||||
export const intersectAttrs =
|
||||
(e1: NixValue) =>
|
||||
(e2: NixValue): NixAttrs => {
|
||||
const f1 = force_attrs(e1);
|
||||
const f2 = force_attrs(e2);
|
||||
const f1 = forceAttrs(e1);
|
||||
const f2 = forceAttrs(e2);
|
||||
const attrs: NixAttrs = {};
|
||||
for (const key of Object.keys(f2)) {
|
||||
if (Object.hasOwn(f1, key)) {
|
||||
@@ -73,9 +73,9 @@ export const intersectAttrs =
|
||||
export const catAttrs =
|
||||
(attr: NixValue) =>
|
||||
(list: NixValue): NixList => {
|
||||
const key = force_string(attr);
|
||||
return force_list(list)
|
||||
.map((set) => force_attrs(set)[key])
|
||||
const key = forceString(attr);
|
||||
return forceList(list)
|
||||
.map((set) => forceAttrs(set)[key])
|
||||
.filter((val) => val !== undefined);
|
||||
};
|
||||
|
||||
@@ -83,10 +83,10 @@ export const groupBy =
|
||||
(f: NixValue) =>
|
||||
(list: NixValue): NixAttrs => {
|
||||
const attrs: NixAttrs = {};
|
||||
const forced_f = force_function(f);
|
||||
const forced_list = force_list(list);
|
||||
const forced_f = forceFunction(f);
|
||||
const forced_list = forceList(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] = [];
|
||||
(attrs[key] as NixList).push(elem);
|
||||
}
|
||||
|
||||
@@ -191,6 +191,6 @@ export const coerceToString = (
|
||||
* @param value - The value to convert to a string
|
||||
* @returns The string representation
|
||||
*/
|
||||
export const toString = (value: NixValue): string => {
|
||||
export const toStringFunc = (value: NixValue): string => {
|
||||
return coerceToString(value, StringCoercionMode.ToString, false);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { CatchableError, type NixValue } from "../types";
|
||||
import { force } from "../thunk";
|
||||
import { force_string } from "../type-assert";
|
||||
import { forceString } from "../type-assert";
|
||||
|
||||
export const seq =
|
||||
(e1: NixValue) =>
|
||||
@@ -24,7 +24,7 @@ export const abort = (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 => {
|
||||
|
||||
@@ -211,7 +211,7 @@ export const builtins: any = {
|
||||
fromTOML: mkPrimop(conversion.fromTOML, "fromTOML", 1),
|
||||
toJSON: mkPrimop(conversion.toJSON, "toJSON", 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),
|
||||
appendContext: mkPrimop(misc.appendContext, "appendContext", 1),
|
||||
|
||||
@@ -3,23 +3,20 @@
|
||||
* Implemented via Rust ops exposed through deno_core
|
||||
*/
|
||||
|
||||
import { forceString } from "../type-assert";
|
||||
import type { NixValue } from "../types";
|
||||
import { force_string } from "../type-assert";
|
||||
|
||||
// Declare Deno.core.ops global (provided by deno_core runtime)
|
||||
|
||||
export const importFunc = (path: NixValue): NixValue => {
|
||||
// For MVP: only support string paths
|
||||
// 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
|
||||
const code = Deno.core.ops.op_import(pathStr);
|
||||
|
||||
// Eval in current context - returns V8 value directly!
|
||||
// (0, eval) = indirect eval = global scope
|
||||
// Wrap in parentheses to ensure object literals are parsed correctly
|
||||
return (0, eval)(`(${code})`);
|
||||
return Function(`return (${code})`)();
|
||||
};
|
||||
|
||||
export const scopedImport =
|
||||
@@ -61,7 +58,7 @@ export const readDir = (path: NixValue): never => {
|
||||
};
|
||||
|
||||
export const readFile = (path: NixValue): string => {
|
||||
const pathStr = force_string(path);
|
||||
const pathStr = forceString(path);
|
||||
return Deno.core.ops.op_read_file(pathStr);
|
||||
};
|
||||
|
||||
@@ -70,7 +67,7 @@ export const readFileType = (path: NixValue): never => {
|
||||
};
|
||||
|
||||
export const pathExists = (path: NixValue): boolean => {
|
||||
const pathStr = force_string(path);
|
||||
const pathStr = forceString(path);
|
||||
return Deno.core.ops.op_path_exists(pathStr);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,38 +5,38 @@
|
||||
|
||||
import type { NixValue, NixList, NixAttrs } from "../types";
|
||||
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 =
|
||||
(f: NixValue) =>
|
||||
(list: NixValue): NixList =>
|
||||
force_list(list).map(force_function(f));
|
||||
forceList(list).map(forceFunction(f));
|
||||
|
||||
export const filter =
|
||||
(f: NixValue) =>
|
||||
(list: NixValue): NixList =>
|
||||
force_list(list).filter(force_function(f));
|
||||
forceList(list).filter(forceFunction(f));
|
||||
|
||||
export const length = (e: NixValue): bigint => {
|
||||
const forced = force(e);
|
||||
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 =
|
||||
(x: NixValue) =>
|
||||
(xs: NixValue): boolean =>
|
||||
force_list(xs).includes(force(x));
|
||||
forceList(xs).includes(force(x));
|
||||
|
||||
export const elemAt =
|
||||
(xs: NixValue) =>
|
||||
(n: NixValue): NixValue => {
|
||||
const list = force_list(xs);
|
||||
const idx = Number(force_int(n));
|
||||
const list = forceList(xs);
|
||||
const idx = Number(forceInt(n));
|
||||
|
||||
if (idx < 0 || idx >= 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 => {
|
||||
return force_list(lists).reduce((acc: NixList, cur: NixValue) => {
|
||||
return acc.concat(force_list(cur));
|
||||
return forceList(lists).reduce((acc: NixList, cur: NixValue) => {
|
||||
return acc.concat(forceList(cur));
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const concatMap =
|
||||
(f: NixValue) =>
|
||||
(lists: NixValue): NixList => {
|
||||
const fn = force_function(f);
|
||||
return force_list(lists).reduce((acc: NixList, cur: NixValue) => {
|
||||
const fn = forceFunction(f);
|
||||
return forceList(lists).reduce((acc: NixList, cur: NixValue) => {
|
||||
return acc.concat(force(fn(cur)) as NixList);
|
||||
}, []);
|
||||
};
|
||||
@@ -64,20 +64,20 @@ export const foldlPrime =
|
||||
(op_fn: NixValue) =>
|
||||
(nul: NixValue) =>
|
||||
(list: NixValue): NixValue => {
|
||||
const forced_op = force_function(op_fn);
|
||||
return force_list(list).reduce((acc: NixValue, cur: NixValue) => {
|
||||
return forced_op(acc)(cur);
|
||||
const forced_op = forceFunction(op_fn);
|
||||
return forceList(list).reduce((acc: NixValue, cur: NixValue) => {
|
||||
return forceFunction(forced_op(acc))(cur);
|
||||
}, nul);
|
||||
};
|
||||
|
||||
export const sort =
|
||||
(cmp: NixValue) =>
|
||||
(list: NixValue): NixList => {
|
||||
const forced_list = [...force_list(list)];
|
||||
const forced_cmp = force_function(cmp);
|
||||
const forced_list = [...forceList(list)];
|
||||
const forced_cmp = forceFunction(cmp);
|
||||
return forced_list.sort((a, b) => {
|
||||
if (force(forced_cmp(a)(b))) return -1;
|
||||
if (force(forced_cmp(b)(a))) return 1;
|
||||
if (force(forceFunction(forced_cmp(a))(b))) return -1;
|
||||
if (force(forceFunction(forced_cmp(b))(a))) return 1;
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
@@ -85,8 +85,8 @@ export const sort =
|
||||
export const partition =
|
||||
(pred: NixValue) =>
|
||||
(list: NixValue): NixAttrs => {
|
||||
const forced_list = force_list(list);
|
||||
const forced_pred = force_function(pred);
|
||||
const forced_list = forceList(list);
|
||||
const forced_pred = forceFunction(pred);
|
||||
const attrs: NixAttrs = {
|
||||
right: [],
|
||||
wrong: [],
|
||||
@@ -104,8 +104,8 @@ export const partition =
|
||||
export const genList =
|
||||
(f: NixValue) =>
|
||||
(len: NixValue): NixList => {
|
||||
const func = force_function(f);
|
||||
const length = force_int(len);
|
||||
const func = forceFunction(f);
|
||||
const length = forceInt(len);
|
||||
|
||||
if (length < 0) {
|
||||
throw new TypeError(`genList length must be non-negative integer, got ${length}`);
|
||||
@@ -117,9 +117,9 @@ export const genList =
|
||||
export const all =
|
||||
(pred: NixValue) =>
|
||||
(list: NixValue): boolean =>
|
||||
force_list(list).every(force_function(pred));
|
||||
forceList(list).every(forceFunction(pred));
|
||||
|
||||
export const any =
|
||||
(pred: NixValue) =>
|
||||
(list: NixValue): boolean =>
|
||||
force_list(list).some(force_function(pred));
|
||||
forceList(list).some(forceFunction(pred));
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
*/
|
||||
|
||||
import type { NixValue } from "../types";
|
||||
import { force_numeric } from "../type-assert";
|
||||
import { forceNumeric } from "../type-assert";
|
||||
|
||||
export const ceil = (x: NixValue): bigint => {
|
||||
const val = force_numeric(x);
|
||||
const val = forceNumeric(x);
|
||||
if (typeof val === "bigint") return val; // Already an integer
|
||||
return BigInt(Math.ceil(val)); // Convert to integer
|
||||
};
|
||||
|
||||
export const floor = (x: NixValue): bigint => {
|
||||
const val = force_numeric(x);
|
||||
const val = forceNumeric(x);
|
||||
if (typeof val === "bigint") return val; // Already an integer
|
||||
return BigInt(Math.floor(val)); // Convert to integer
|
||||
};
|
||||
|
||||
@@ -3,27 +3,27 @@
|
||||
*/
|
||||
|
||||
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 =
|
||||
(start: NixValue) =>
|
||||
(len: NixValue) =>
|
||||
(s: NixValue): string => {
|
||||
const str = force_string(s);
|
||||
const startPos = Number(force_int(start));
|
||||
const length = Number(force_int(len));
|
||||
const str = forceString(s);
|
||||
const startPos = Number(forceInt(start));
|
||||
const length = Number(forceInt(len));
|
||||
return str.substring(startPos, startPos + length);
|
||||
};
|
||||
|
||||
export const concatStringsSep =
|
||||
(sep: NixValue) =>
|
||||
(list: NixValue): string =>
|
||||
force_list(list).join(force_string(sep));
|
||||
forceList(list).join(forceString(sep));
|
||||
|
||||
export const baseNameOf = (x: NixValue): string => {
|
||||
const str = force_string(x);
|
||||
const str = forceString(x);
|
||||
if (str.length === 0) return "";
|
||||
|
||||
let last = str.length - 1;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@ import { isAttrs } from "./builtins/type-check";
|
||||
* @returns Absolute path 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);
|
||||
};
|
||||
|
||||
@@ -28,8 +28,8 @@ export const resolvePath = (path: NixValue): string => {
|
||||
* @throws Error if obj is null/undefined or key not found
|
||||
*/
|
||||
export const select = (obj: NixValue, key: NixValue): NixValue => {
|
||||
const forced_obj = force_attrs(obj);
|
||||
const forced_key = force_string(key);
|
||||
const forced_obj = forceAttrs(obj);
|
||||
const forced_key = forceString(key);
|
||||
|
||||
if (!(forced_key in forced_obj)) {
|
||||
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
|
||||
*/
|
||||
export const selectWithDefault = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => {
|
||||
const attrs = force_attrs(obj);
|
||||
const forced_key = force_string(key);
|
||||
const attrs = forceAttrs(obj);
|
||||
const forced_key = forceString(key);
|
||||
|
||||
if (!(forced_key in attrs)) {
|
||||
return default_val;
|
||||
@@ -65,7 +65,7 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||
let attrs = obj;
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const cur = attrs[force_string(attr)];
|
||||
const cur = attrs[forceString(attr)];
|
||||
if (!isAttrs(cur)) {
|
||||
return false;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ export const validateParams = (
|
||||
required: string[] | null,
|
||||
allowed: string[] | null,
|
||||
): NixAttrs => {
|
||||
const forced_arg = force_attrs(arg);
|
||||
const forced_arg = forceAttrs(arg);
|
||||
|
||||
// Check required parameters
|
||||
if (required) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* 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 { op } from "./operators";
|
||||
import { builtins, PRIMOP_METADATA } from "./builtins";
|
||||
@@ -18,7 +18,7 @@ export type NixRuntime = typeof Nix;
|
||||
export const Nix = {
|
||||
createThunk,
|
||||
force,
|
||||
is_thunk,
|
||||
isThunk,
|
||||
IS_THUNK,
|
||||
|
||||
hasAttr,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import type { NixValue, NixList, NixAttrs } from "./types";
|
||||
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
|
||||
@@ -15,22 +15,22 @@ export const op = {
|
||||
// Arithmetic operators - preserve int/float distinction
|
||||
add: (a: NixValue, b: NixValue): bigint | number => {
|
||||
// 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);
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
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) {
|
||||
throw new RangeError("Division by zero");
|
||||
@@ -54,22 +54,22 @@ export const op = {
|
||||
},
|
||||
lt: (a: NixValue, b: NixValue): boolean => {
|
||||
// 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);
|
||||
},
|
||||
lte: (a: NixValue, b: NixValue): boolean => {
|
||||
// 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);
|
||||
},
|
||||
gt: (a: NixValue, b: NixValue): boolean => {
|
||||
// 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);
|
||||
},
|
||||
gte: (a: NixValue, b: NixValue): boolean => {
|
||||
// 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);
|
||||
},
|
||||
|
||||
@@ -81,11 +81,11 @@ export const op = {
|
||||
|
||||
// List concatenation
|
||||
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)
|
||||
update: (a: NixValue, b: NixValue): NixAttrs => {
|
||||
return { ...force_attrs(a), ...force_attrs(b) };
|
||||
return { ...forceAttrs(a), ...forceAttrs(b) };
|
||||
},
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export class NixThunk implements NixThunkInterface {
|
||||
* @param value - Value to check
|
||||
* @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;
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ export const is_thunk = (value: unknown): value is NixThunkInterface => {
|
||||
* @returns The forced/evaluated value
|
||||
*/
|
||||
export const force = (value: NixValue): NixStrictValue => {
|
||||
if (!is_thunk(value)) {
|
||||
if (!isThunk(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber } from "./types";
|
||||
import { force } from "./thunk";
|
||||
import { isAttrs } from "./builtins/type-check";
|
||||
|
||||
const typeName = (value: NixValue): string => {
|
||||
const val = force(value);
|
||||
@@ -26,7 +25,7 @@ const typeName = (value: NixValue): string => {
|
||||
* Force a value and assert it's a list
|
||||
* @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);
|
||||
if (!Array.isArray(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
|
||||
* @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);
|
||||
if (typeof forced !== "function") {
|
||||
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
|
||||
* @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);
|
||||
if (typeof forced !== "object" || Array.isArray(forced) || forced === null) {
|
||||
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
|
||||
* @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);
|
||||
if (typeof forced !== "string") {
|
||||
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
|
||||
* @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);
|
||||
if (typeof forced !== "boolean") {
|
||||
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
|
||||
* @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);
|
||||
if (typeof forced === "bigint") {
|
||||
return forced;
|
||||
@@ -98,7 +97,7 @@ export const force_int = (value: NixValue): NixInt => {
|
||||
* Force a value and extract float value
|
||||
* @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);
|
||||
if (typeof forced === "number") {
|
||||
return forced;
|
||||
@@ -110,7 +109,7 @@ export const force_float = (value: NixValue): NixFloat => {
|
||||
* Force a value and extract numeric value (int or float)
|
||||
* @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);
|
||||
if (typeof forced === "bigint" || typeof forced === "number") {
|
||||
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
|
||||
* @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 bIsInt = typeof b === "bigint";
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export type NixNull = null;
|
||||
// Nix composite types
|
||||
export type NixList = NixValue[];
|
||||
export type NixAttrs = { [key: string]: NixValue };
|
||||
export type NixFunction = (...args: any[]) => any;
|
||||
export type NixFunction = (arg: NixValue) => NixValue;
|
||||
|
||||
/**
|
||||
* Interface for lazy thunk values
|
||||
@@ -42,11 +42,7 @@ export type NixStrictValue = Exclude<NixValue, NixThunkInterface>;
|
||||
* CatchableError: Error type thrown by `builtins.throw`
|
||||
* This can be caught by `builtins.tryEval`
|
||||
*/
|
||||
export class CatchableError extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
export class CatchableError extends Error {}
|
||||
|
||||
// Operator function signatures
|
||||
export type BinaryOp<T = NixValue, U = NixValue, R = NixValue> = (a: T, b: U) => R;
|
||||
|
||||
@@ -199,11 +199,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Let {
|
||||
}
|
||||
|
||||
let body = ctx.get_ir(self.body).compile(ctx);
|
||||
format!(
|
||||
"(()=>{{{}; return {}}})()",
|
||||
js_statements.join(";"),
|
||||
body
|
||||
)
|
||||
format!("(()=>{{{}; return {}}})()", js_statements.join(";"), body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +289,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
||||
.map(|part| {
|
||||
let compiled = ctx.get_ir(*part).compile(ctx);
|
||||
// TODO: copyToStore
|
||||
format!("Nix.coerceToString({}, Nix.StringCoercionMode.Interpolation, false)", compiled)
|
||||
format!(
|
||||
"Nix.coerceToString({}, Nix.StringCoercionMode.Interpolation, false)",
|
||||
compiled
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
|
||||
path_str
|
||||
} else {
|
||||
let current_dir = ctx.get_current_dir();
|
||||
dbg!(¤t_dir);
|
||||
|
||||
current_dir
|
||||
.join(&path_str)
|
||||
@@ -172,29 +171,22 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
|
||||
|
||||
// rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; }
|
||||
let entries: Vec<_> = self.entries().collect();
|
||||
let (binding_sccs, body) =
|
||||
downgrade_let_bindings(entries, ctx, |ctx, binding_keys| {
|
||||
// Create plain attrset as body with inherit
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
};
|
||||
let (binding_sccs, body) = downgrade_let_bindings(entries, ctx, |ctx, binding_keys| {
|
||||
// Create plain attrset as body with inherit
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
};
|
||||
|
||||
for sym in binding_keys {
|
||||
let expr = ctx.lookup(*sym)?;
|
||||
attrs.stcs.insert(*sym, expr);
|
||||
}
|
||||
|
||||
Ok(ctx.new_expr(attrs.to_ir()))
|
||||
})?;
|
||||
|
||||
Ok(ctx.new_expr(
|
||||
Let {
|
||||
body,
|
||||
binding_sccs,
|
||||
for sym in binding_keys {
|
||||
let expr = ctx.lookup(*sym)?;
|
||||
attrs.stcs.insert(*sym, expr);
|
||||
}
|
||||
.to_ir(),
|
||||
))
|
||||
|
||||
Ok(ctx.new_expr(attrs.to_ir()))
|
||||
})?;
|
||||
|
||||
Ok(ctx.new_expr(Let { body, binding_sccs }.to_ir()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,13 +291,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LetIn {
|
||||
let (binding_sccs, body) =
|
||||
downgrade_let_bindings(entries, ctx, |ctx, _binding_keys| body_expr.downgrade(ctx))?;
|
||||
|
||||
Ok(ctx.new_expr(
|
||||
Let {
|
||||
body,
|
||||
binding_sccs,
|
||||
}
|
||||
.to_ir(),
|
||||
))
|
||||
Ok(ctx.new_expr(Let { body, binding_sccs }.to_ir()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ where
|
||||
body,
|
||||
scc_info,
|
||||
required_params: required,
|
||||
allowed_params: allowed
|
||||
allowed_params: allowed,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -348,7 +348,11 @@ where
|
||||
F: FnOnce(&mut Ctx, &[SymId]) -> Result<ExprId>,
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ fn nested_function_parameters() {
|
||||
|
||||
#[test]
|
||||
fn pattern_param_simple_reference_in_default() {
|
||||
assert_eq!(
|
||||
eval("({ a, b ? a }: b) { a = 10; }"),
|
||||
Value::Int(10)
|
||||
);
|
||||
assert_eq!(eval("({ a, b ? a }: b) { a = 10; }"), Value::Int(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -90,8 +90,7 @@ fn import_with_complex_dependency_graph() {
|
||||
std::fs::write(&utils_path, "{ double = x: x * 2; }").unwrap();
|
||||
|
||||
let math_path = temp_dir.path().join("math.nix");
|
||||
let math_content =
|
||||
r#"let utils = import ./utils.nix; in { triple = x: x + utils.double x; }"#;
|
||||
let math_content = r#"let utils = import ./utils.nix; in { triple = x: x + utils.double x; }"#;
|
||||
std::fs::write(&math_path, math_content).unwrap();
|
||||
|
||||
let main_path = temp_dir.path().join("main.nix");
|
||||
|
||||
@@ -5,7 +5,10 @@ use utils::eval;
|
||||
|
||||
#[test]
|
||||
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]
|
||||
@@ -67,10 +70,7 @@ fn list_with_multiple_empty_lists() {
|
||||
eval("toString [1 [] [] 2]"),
|
||||
Value::String("1 2".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
eval("toString [[] [] 1]"),
|
||||
Value::String("1".to_string())
|
||||
);
|
||||
assert_eq!(eval("toString [[] [] 1]"), Value::String("1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -112,7 +112,9 @@ fn attrs_with_to_string_method() {
|
||||
#[test]
|
||||
fn attrs_to_string_self_reference() {
|
||||
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())
|
||||
);
|
||||
}
|
||||
@@ -120,9 +122,7 @@ fn attrs_to_string_self_reference() {
|
||||
#[test]
|
||||
fn attrs_to_string_priority() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
r#"toString { __toString = self: "custom"; outPath = "/nix/store/foo"; }"#
|
||||
),
|
||||
eval(r#"toString { __toString = self: "custom"; outPath = "/nix/store/foo"; }"#),
|
||||
Value::String("custom".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user