fmt: biome & editorconfig

This commit is contained in:
2026-01-02 23:21:27 +08:00
parent 073c95f2c3
commit add715f560
20 changed files with 569 additions and 445 deletions

11
.editorconfig Normal file
View File

@@ -0,0 +1,11 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
[*.{rs,toml}]
indent_size = 4

36
biome.json Normal file
View File

@@ -0,0 +1,36 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.6/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"includes": ["**", "!!**/dist"]
},
"formatter": {
"enabled": true,
"formatWithErrors": true,
"attributePosition": "auto",
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 110,
"lineEnding": "lf"
},
"javascript": {
"formatter": {
"arrowParentheses": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"semicolons": "always",
"trailingCommas": "all"
}
},
"json": {
"formatter": {
"trailingCommas": "none"
}
}
}

View File

@@ -26,9 +26,9 @@
valgrind valgrind
claude-code claude-code
# Node.js tooling for TypeScript build
nodejs nodejs
nodePackages.npm nodePackages.npm
biome
]; ];
}; };
} }

View File

@@ -2,52 +2,68 @@
* Arithmetic builtin functions * Arithmetic builtin functions
*/ */
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 { force_numeric, coerce_numeric, force_int } from "../type-assert";
export const add = (a: NixValue) => (b: NixValue): bigint | number => { export const add =
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); (a: NixValue) =>
return (av as any) + (bv as any); (b: NixValue): bigint | number => {
}; const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
return (av as any) + (bv as any);
};
export const sub = (a: NixValue) => (b: NixValue): bigint | number => { export const sub =
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); (a: NixValue) =>
return (av as any) - (bv as any); (b: NixValue): bigint | number => {
}; const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
return (av as any) - (bv as any);
};
export const mul = (a: NixValue) => (b: NixValue): bigint | number => { export const mul =
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); (a: NixValue) =>
return (av as any) * (bv as any); (b: NixValue): bigint | number => {
}; const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
return (av as any) * (bv as any);
};
export const div = (a: NixValue) => (b: NixValue): NixNumber => { export const div =
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); (a: NixValue) =>
(b: NixValue): NixNumber => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
if (bv === 0 || bv === 0n) { if (bv === 0 || bv === 0n) {
throw new RangeError('Division by zero'); throw new RangeError("Division by zero");
} }
return (av as any) / (bv as any) return (av as any) / (bv as any);
}; };
// Bitwise operations - only for integers // Bitwise operations - only for integers
export const bitAnd = (a: NixValue) => (b: NixValue): NixInt => { export const bitAnd =
const av = force_int(a); (a: NixValue) =>
const bv = force_int(b); (b: NixValue): NixInt => {
return av & bv; const av = force_int(a);
}; const bv = force_int(b);
return av & bv;
};
export const bitOr = (a: NixValue) => (b: NixValue): NixInt => { export const bitOr =
const av = force_int(a); (a: NixValue) =>
const bv = force_int(b); (b: NixValue): NixInt => {
return av | bv; const av = force_int(a);
}; const bv = force_int(b);
return av | bv;
};
export const bitXor = (a: NixValue) => (b: NixValue): NixInt => { export const bitXor =
const av = force_int(a); (a: NixValue) =>
const bv = force_int(b); (b: NixValue): NixInt => {
return av ^ bv; const av = force_int(a);
}; const bv = force_int(b);
return av ^ bv;
};
export const lessThan = (a: NixValue) => (b: NixValue): NixBool => export const lessThan =
force_numeric(a) < force_numeric(b); (a: NixValue) =>
(b: NixValue): NixBool =>
force_numeric(a) < force_numeric(b);

View File

@@ -2,28 +2,34 @@
* Attribute set operation builtin functions * Attribute set operation builtin functions
*/ */
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 { force_attrs, force_string, force_function, force_list } from "../type-assert";
export const attrNames = (set: NixValue): string[] => Object.keys(force_attrs(set)); export const attrNames = (set: NixValue): string[] => Object.keys(force_attrs(set));
export const attrValues = (set: NixValue): NixValue[] => Object.values(force_attrs(set)); export const attrValues = (set: NixValue): NixValue[] => Object.values(force_attrs(set));
export const getAttr = (s: NixValue) => (set: NixValue): NixValue => export const getAttr =
force_attrs(set)[force_string(s)]; (s: NixValue) =>
(set: NixValue): NixValue =>
force_attrs(set)[force_string(s)];
export const hasAttr = (s: NixValue) => (set: NixValue): boolean => export const hasAttr =
Object.prototype.hasOwnProperty.call(force_attrs(set), force_string(s)); (s: NixValue) =>
(set: NixValue): boolean =>
Object.prototype.hasOwnProperty.call(force_attrs(set), force_string(s));
export const mapAttrs = (f: NixValue) => (attrs: NixValue): NixAttrs => { export const mapAttrs =
const new_attrs: NixAttrs = {}; (f: NixValue) =>
const forced_attrs = force_attrs(attrs); (attrs: NixValue): NixAttrs => {
const forced_f = force_function(f); const new_attrs: NixAttrs = {};
for (const key in forced_attrs) { const forced_attrs = force_attrs(attrs);
new_attrs[key] = forced_f(key)(forced_attrs[key]); const forced_f = force_function(f);
} for (const key in forced_attrs) {
return new_attrs; new_attrs[key] = forced_f(key)(forced_attrs[key]);
}; }
return new_attrs;
};
export const listToAttrs = (e: NixValue): NixAttrs => { export const listToAttrs = (e: NixValue): NixAttrs => {
const attrs: NixAttrs = {}; const attrs: NixAttrs = {};
@@ -35,33 +41,39 @@ export const listToAttrs = (e: NixValue): NixAttrs => {
return attrs; return attrs;
}; };
export const intersectAttrs = (e1: NixValue) => (e2: NixValue): NixAttrs => { export const intersectAttrs =
const f1 = force_attrs(e1); (e1: NixValue) =>
const f2 = force_attrs(e2); (e2: NixValue): NixAttrs => {
const attrs: NixAttrs = {}; const f1 = force_attrs(e1);
for (const key of Object.keys(f2)) { const f2 = force_attrs(e2);
if (Object.prototype.hasOwnProperty.call(f1, key)) { const attrs: NixAttrs = {};
attrs[key] = f2[key]; for (const key of Object.keys(f2)) {
if (Object.prototype.hasOwnProperty.call(f1, key)) {
attrs[key] = f2[key];
}
} }
} return attrs;
return attrs; };
};
export const catAttrs = (attr: NixValue) => (list: NixValue): NixList => { export const catAttrs =
const key = force_string(attr); (attr: NixValue) =>
return force_list(list) (list: NixValue): NixList => {
.map((set) => force_attrs(set)[key]) const key = force_string(attr);
.filter((val) => val !== undefined); return force_list(list)
}; .map((set) => force_attrs(set)[key])
.filter((val) => val !== undefined);
};
export const groupBy = (f: NixValue) => (list: NixValue): NixAttrs => { export const groupBy =
const attrs: NixAttrs = {}; (f: NixValue) =>
const forced_f = force_function(f); (list: NixValue): NixAttrs => {
const forced_list = force_list(list); const attrs: NixAttrs = {};
for (const elem of forced_list) { const forced_f = force_function(f);
const key = force_string(forced_f(elem)); const forced_list = force_list(list);
if (!attrs[key]) attrs[key] = []; for (const elem of forced_list) {
(attrs[key] as NixList).push(elem); const key = force_string(forced_f(elem));
} if (!attrs[key]) attrs[key] = [];
return attrs; (attrs[key] as NixList).push(elem);
}; }
return attrs;
};

View File

@@ -2,24 +2,24 @@
* Conversion and serialization builtin functions (unimplemented) * Conversion and serialization builtin functions (unimplemented)
*/ */
import type { NixValue } from '../types'; import type { NixValue } from "../types";
export const fromJSON = (e: NixValue): never => { export const fromJSON = (e: NixValue): never => {
throw 'Not implemented: fromJSON'; throw "Not implemented: fromJSON";
}; };
export const fromTOML = (e: NixValue): never => { export const fromTOML = (e: NixValue): never => {
throw 'Not implemented: fromTOML'; throw "Not implemented: fromTOML";
}; };
export const toJSON = (e: NixValue): never => { export const toJSON = (e: NixValue): never => {
throw 'Not implemented: toJSON'; throw "Not implemented: toJSON";
}; };
export const toXML = (e: NixValue): never => { export const toXML = (e: NixValue): never => {
throw 'Not implemented: toXML'; throw "Not implemented: toXML";
}; };
export const toString = (name: NixValue, s: NixValue): never => { export const toString = (name: NixValue, s: NixValue): never => {
throw 'Not implemented: toString'; throw "Not implemented: toString";
}; };

View File

@@ -2,17 +2,21 @@
* Functional programming builtin functions * Functional programming builtin functions
*/ */
import type { NixValue } from '../types'; import type { NixValue } from "../types";
import { force } from '../thunk'; import { force } from "../thunk";
export const seq = (e1: NixValue) => (e2: NixValue): NixValue => { export const seq =
force(e1); // Force evaluation of e1 (e1: NixValue) =>
return e2; (e2: NixValue): NixValue => {
}; force(e1); // Force evaluation of e1
return e2;
};
export const deepSeq = (e1: NixValue) => (e2: NixValue): never => { export const deepSeq =
throw 'Not implemented: deepSeq'; (e1: NixValue) =>
}; (e2: NixValue): never => {
throw "Not implemented: deepSeq";
};
export const abort = (s: NixValue): never => { export const abort = (s: NixValue): never => {
throw `evaluation aborted with the following error message: '${force(s)}'`; throw `evaluation aborted with the following error message: '${force(s)}'`;
@@ -27,9 +31,11 @@ export const trace = (e1: NixValue, e2: NixValue): NixValue => {
return e2; return e2;
}; };
export const warn = (e1: NixValue) => (e2: NixValue): NixValue => { export const warn =
console.log(`evaluation warning: ${force(e1)}`); (e1: NixValue) =>
return e2; (e2: NixValue): NixValue => {
}; console.log(`evaluation warning: ${force(e1)}`);
return e2;
};
export const breakFunc = (v: NixValue): NixValue => v; export const breakFunc = (v: NixValue): NixValue => v;

View File

@@ -3,13 +3,13 @@
* Combines all builtin function categories into the global `builtins` object * Combines all builtin function categories into the global `builtins` object
*/ */
import { create_thunk } from '../thunk'; import { create_thunk } from "../thunk";
/** /**
* Symbol used to mark functions as primops (primitive operations) * Symbol used to mark functions as primops (primitive operations)
* This is similar to IS_THUNK but for builtin functions * This is similar to IS_THUNK but for builtin functions
*/ */
export const IS_PRIMOP = Symbol('is_primop'); export const IS_PRIMOP = Symbol("is_primop");
/** /**
* Metadata interface for primop functions * Metadata interface for primop functions
@@ -37,7 +37,7 @@ export const markPrimop = <T extends Function>(
func: T, func: T,
name: string, name: string,
arity: number, arity: number,
applied: number = 0 applied: number = 0,
): T => { ): T => {
// Mark this function as a primop // Mark this function as a primop
(func as any)[IS_PRIMOP] = { (func as any)[IS_PRIMOP] = {
@@ -52,7 +52,7 @@ export const markPrimop = <T extends Function>(
const wrappedFunc = ((...args: any[]) => { const wrappedFunc = ((...args: any[]) => {
const result = func(...args); const result = func(...args);
// If result is a function, mark it as the next layer // If result is a function, mark it as the next layer
if (typeof result === 'function') { if (typeof result === "function") {
return markPrimop(result, name, arity, applied + args.length); return markPrimop(result, name, arity, applied + args.length);
} }
return result; return result;
@@ -78,9 +78,9 @@ export const markPrimop = <T extends Function>(
*/ */
export const is_primop = (value: unknown): value is Function & { [IS_PRIMOP]: PrimopMetadata } => { export const is_primop = (value: unknown): value is Function & { [IS_PRIMOP]: PrimopMetadata } => {
return ( return (
typeof value === 'function' && typeof value === "function" &&
IS_PRIMOP in value && IS_PRIMOP in value &&
typeof value[IS_PRIMOP] === 'object' && typeof value[IS_PRIMOP] === "object" &&
value[IS_PRIMOP] !== null value[IS_PRIMOP] !== null
); );
}; };
@@ -98,16 +98,16 @@ export const get_primop_metadata = (func: unknown): PrimopMetadata | undefined =
}; };
// Import all builtin categories // Import all builtin categories
import * as arithmetic from './arithmetic'; import * as arithmetic from "./arithmetic";
import * as math from './math'; import * as math from "./math";
import * as typeCheck from './type-check'; import * as typeCheck from "./type-check";
import * as list from './list'; import * as list from "./list";
import * as attrs from './attrs'; import * as attrs from "./attrs";
import * as string from './string'; import * as string from "./string";
import * as functional from './functional'; import * as functional from "./functional";
import * as io from './io'; import * as io from "./io";
import * as conversion from './conversion'; import * as conversion from "./conversion";
import * as misc from './misc'; import * as misc from "./misc";
/** /**
* The global builtins object * The global builtins object
@@ -120,122 +120,126 @@ import * as misc from './misc';
* All primop functions are marked with IS_PRIMOP symbol for runtime introspection * All primop functions are marked with IS_PRIMOP symbol for runtime introspection
*/ */
export const builtins: any = { export const builtins: any = {
add: markPrimop(arithmetic.add, 'add', 2), add: markPrimop(arithmetic.add, "add", 2),
sub: markPrimop(arithmetic.sub, 'sub', 2), sub: markPrimop(arithmetic.sub, "sub", 2),
mul: markPrimop(arithmetic.mul, 'mul', 2), mul: markPrimop(arithmetic.mul, "mul", 2),
div: markPrimop(arithmetic.div, 'div', 2), div: markPrimop(arithmetic.div, "div", 2),
bitAnd: markPrimop(arithmetic.bitAnd, 'bitAnd', 2), bitAnd: markPrimop(arithmetic.bitAnd, "bitAnd", 2),
bitOr: markPrimop(arithmetic.bitOr, 'bitOr', 2), bitOr: markPrimop(arithmetic.bitOr, "bitOr", 2),
bitXor: markPrimop(arithmetic.bitXor, 'bitXor', 2), bitXor: markPrimop(arithmetic.bitXor, "bitXor", 2),
lessThan: markPrimop(arithmetic.lessThan, 'lessThan', 2), lessThan: markPrimop(arithmetic.lessThan, "lessThan", 2),
ceil: markPrimop(math.ceil, 'ceil', 1), ceil: markPrimop(math.ceil, "ceil", 1),
floor: markPrimop(math.floor, 'floor', 1), floor: markPrimop(math.floor, "floor", 1),
isAttrs: markPrimop(typeCheck.isAttrs, 'isAttrs', 1), isAttrs: markPrimop(typeCheck.isAttrs, "isAttrs", 1),
isBool: markPrimop(typeCheck.isBool, 'isBool', 1), isBool: markPrimop(typeCheck.isBool, "isBool", 1),
isFloat: markPrimop(typeCheck.isFloat, 'isFloat', 1), isFloat: markPrimop(typeCheck.isFloat, "isFloat", 1),
isFunction: markPrimop(typeCheck.isFunction, 'isFunction', 1), isFunction: markPrimop(typeCheck.isFunction, "isFunction", 1),
isInt: markPrimop(typeCheck.isInt, 'isInt', 1), isInt: markPrimop(typeCheck.isInt, "isInt", 1),
isList: markPrimop(typeCheck.isList, 'isList', 1), isList: markPrimop(typeCheck.isList, "isList", 1),
isNull: markPrimop(typeCheck.isNull, 'isNull', 1), isNull: markPrimop(typeCheck.isNull, "isNull", 1),
isPath: markPrimop(typeCheck.isPath, 'isPath', 1), isPath: markPrimop(typeCheck.isPath, "isPath", 1),
isString: markPrimop(typeCheck.isString, 'isString', 1), isString: markPrimop(typeCheck.isString, "isString", 1),
typeOf: markPrimop(typeCheck.typeOf, 'typeOf', 1), typeOf: markPrimop(typeCheck.typeOf, "typeOf", 1),
map: markPrimop(list.map, 'map', 2), map: markPrimop(list.map, "map", 2),
filter: markPrimop(list.filter, 'filter', 2), filter: markPrimop(list.filter, "filter", 2),
length: markPrimop(list.length, 'length', 1), length: markPrimop(list.length, "length", 1),
head: markPrimop(list.head, 'head', 1), head: markPrimop(list.head, "head", 1),
tail: markPrimop(list.tail, 'tail', 1), tail: markPrimop(list.tail, "tail", 1),
elem: markPrimop(list.elem, 'elem', 2), elem: markPrimop(list.elem, "elem", 2),
elemAt: markPrimop(list.elemAt, 'elemAt', 2), elemAt: markPrimop(list.elemAt, "elemAt", 2),
concatLists: markPrimop(list.concatLists, 'concatLists', 1), concatLists: markPrimop(list.concatLists, "concatLists", 1),
concatMap: markPrimop(list.concatMap, 'concatMap', 2), concatMap: markPrimop(list.concatMap, "concatMap", 2),
'foldl\'': markPrimop(list.foldlPrime, 'foldl\'', 3), "foldl'": markPrimop(list.foldlPrime, "foldl'", 3),
sort: markPrimop(list.sort, 'sort', 2), sort: markPrimop(list.sort, "sort", 2),
partition: markPrimop(list.partition, 'partition', 2), partition: markPrimop(list.partition, "partition", 2),
genList: markPrimop(list.genList, 'genList', 2), genList: markPrimop(list.genList, "genList", 2),
all: markPrimop(list.all, 'all', 2), all: markPrimop(list.all, "all", 2),
any: markPrimop(list.any, 'any', 2), any: markPrimop(list.any, "any", 2),
attrNames: markPrimop(attrs.attrNames, 'attrNames', 1), attrNames: markPrimop(attrs.attrNames, "attrNames", 1),
attrValues: markPrimop(attrs.attrValues, 'attrValues', 1), attrValues: markPrimop(attrs.attrValues, "attrValues", 1),
getAttr: markPrimop(attrs.getAttr, 'getAttr', 2), getAttr: markPrimop(attrs.getAttr, "getAttr", 2),
hasAttr: markPrimop(attrs.hasAttr, 'hasAttr', 2), hasAttr: markPrimop(attrs.hasAttr, "hasAttr", 2),
mapAttrs: markPrimop(attrs.mapAttrs, 'mapAttrs', 2), mapAttrs: markPrimop(attrs.mapAttrs, "mapAttrs", 2),
listToAttrs: markPrimop(attrs.listToAttrs, 'listToAttrs', 1), listToAttrs: markPrimop(attrs.listToAttrs, "listToAttrs", 1),
intersectAttrs: markPrimop(attrs.intersectAttrs, 'intersectAttrs', 2), intersectAttrs: markPrimop(attrs.intersectAttrs, "intersectAttrs", 2),
catAttrs: markPrimop(attrs.catAttrs, 'catAttrs', 2), catAttrs: markPrimop(attrs.catAttrs, "catAttrs", 2),
groupBy: markPrimop(attrs.groupBy, 'groupBy', 2), groupBy: markPrimop(attrs.groupBy, "groupBy", 2),
stringLength: markPrimop(string.stringLength, 'stringLength', 1), stringLength: markPrimop(string.stringLength, "stringLength", 1),
substring: markPrimop(string.substring, 'substring', 3), substring: markPrimop(string.substring, "substring", 3),
concatStringsSep: markPrimop(string.concatStringsSep, 'concatStringsSep', 2), concatStringsSep: markPrimop(string.concatStringsSep, "concatStringsSep", 2),
baseNameOf: markPrimop(string.baseNameOf, 'baseNameOf', 1), baseNameOf: markPrimop(string.baseNameOf, "baseNameOf", 1),
seq: markPrimop(functional.seq, 'seq', 2), seq: markPrimop(functional.seq, "seq", 2),
deepSeq: markPrimop(functional.deepSeq, 'deepSeq', 2), deepSeq: markPrimop(functional.deepSeq, "deepSeq", 2),
abort: markPrimop(functional.abort, 'abort', 1), abort: markPrimop(functional.abort, "abort", 1),
throw: markPrimop(functional.throwFunc, 'throw', 1), throw: markPrimop(functional.throwFunc, "throw", 1),
trace: markPrimop(functional.trace, 'trace', 2), trace: markPrimop(functional.trace, "trace", 2),
warn: markPrimop(functional.warn, 'warn', 2), warn: markPrimop(functional.warn, "warn", 2),
break: markPrimop(functional.breakFunc, 'break', 1), break: markPrimop(functional.breakFunc, "break", 1),
import: markPrimop(io.importFunc, 'import', 1), import: markPrimop(io.importFunc, "import", 1),
scopedImport: markPrimop(io.scopedImport, 'scopedImport', 2), scopedImport: markPrimop(io.scopedImport, "scopedImport", 2),
fetchClosure: markPrimop(io.fetchClosure, 'fetchClosure', 1), fetchClosure: markPrimop(io.fetchClosure, "fetchClosure", 1),
fetchGit: markPrimop(io.fetchGit, 'fetchGit', 1), fetchGit: markPrimop(io.fetchGit, "fetchGit", 1),
fetchTarball: markPrimop(io.fetchTarball, 'fetchTarball', 1), fetchTarball: markPrimop(io.fetchTarball, "fetchTarball", 1),
fetchTree: markPrimop(io.fetchTree, 'fetchTree', 1), fetchTree: markPrimop(io.fetchTree, "fetchTree", 1),
fetchurl: markPrimop(io.fetchurl, 'fetchurl', 1), fetchurl: markPrimop(io.fetchurl, "fetchurl", 1),
readDir: markPrimop(io.readDir, 'readDir', 1), readDir: markPrimop(io.readDir, "readDir", 1),
readFile: markPrimop(io.readFile, 'readFile', 1), readFile: markPrimop(io.readFile, "readFile", 1),
readFileType: markPrimop(io.readFileType, 'readFileType', 1), readFileType: markPrimop(io.readFileType, "readFileType", 1),
pathExists: markPrimop(io.pathExists, 'pathExists', 1), pathExists: markPrimop(io.pathExists, "pathExists", 1),
path: markPrimop(io.path, 'path', 1), path: markPrimop(io.path, "path", 1),
toFile: markPrimop(io.toFile, 'toFile', 2), toFile: markPrimop(io.toFile, "toFile", 2),
toPath: markPrimop(io.toPath, 'toPath', 1), toPath: markPrimop(io.toPath, "toPath", 1),
filterSource: markPrimop(io.filterSource, 'filterSource', 2), filterSource: markPrimop(io.filterSource, "filterSource", 2),
findFile: markPrimop(io.findFile, 'findFile', 2), findFile: markPrimop(io.findFile, "findFile", 2),
getEnv: markPrimop(io.getEnv, 'getEnv', 1), getEnv: markPrimop(io.getEnv, "getEnv", 1),
fromJSON: markPrimop(conversion.fromJSON, 'fromJSON', 1), fromJSON: markPrimop(conversion.fromJSON, "fromJSON", 1),
fromTOML: markPrimop(conversion.fromTOML, 'fromTOML', 1), fromTOML: markPrimop(conversion.fromTOML, "fromTOML", 1),
toJSON: markPrimop(conversion.toJSON, 'toJSON', 1), toJSON: markPrimop(conversion.toJSON, "toJSON", 1),
toXML: markPrimop(conversion.toXML, 'toXML', 1), toXML: markPrimop(conversion.toXML, "toXML", 1),
toString: markPrimop(conversion.toString, 'toString', 1), toString: markPrimop(conversion.toString, "toString", 1),
getContext: markPrimop(misc.getContext, 'getContext', 1), getContext: markPrimop(misc.getContext, "getContext", 1),
hasContext: markPrimop(misc.hasContext, 'hasContext', 1), hasContext: markPrimop(misc.hasContext, "hasContext", 1),
hashFile: markPrimop(misc.hashFile, 'hashFile', 2), hashFile: markPrimop(misc.hashFile, "hashFile", 2),
hashString: markPrimop(misc.hashString, 'hashString', 2), hashString: markPrimop(misc.hashString, "hashString", 2),
convertHash: markPrimop(misc.convertHash, 'convertHash', 2), convertHash: markPrimop(misc.convertHash, "convertHash", 2),
unsafeDiscardOutputDependency: markPrimop(misc.unsafeDiscardOutputDependency, 'unsafeDiscardOutputDependency', 1), unsafeDiscardOutputDependency: markPrimop(
unsafeDiscardStringContext: markPrimop(misc.unsafeDiscardStringContext, 'unsafeDiscardStringContext', 1), misc.unsafeDiscardOutputDependency,
unsafeGetAttrPos: markPrimop(misc.unsafeGetAttrPos, 'unsafeGetAttrPos', 2), "unsafeDiscardOutputDependency",
addDrvOutputDependencies: markPrimop(misc.addDrvOutputDependencies, 'addDrvOutputDependencies', 2), 1,
compareVersions: markPrimop(misc.compareVersions, 'compareVersions', 2), ),
dirOf: markPrimop(misc.dirOf, 'dirOf', 1), unsafeDiscardStringContext: markPrimop(misc.unsafeDiscardStringContext, "unsafeDiscardStringContext", 1),
flakeRefToString: markPrimop(misc.flakeRefToString, 'flakeRefToString', 1), unsafeGetAttrPos: markPrimop(misc.unsafeGetAttrPos, "unsafeGetAttrPos", 2),
functionArgs: markPrimop(misc.functionArgs, 'functionArgs', 1), addDrvOutputDependencies: markPrimop(misc.addDrvOutputDependencies, "addDrvOutputDependencies", 2),
genericClosure: markPrimop(misc.genericClosure, 'genericClosure', 1), compareVersions: markPrimop(misc.compareVersions, "compareVersions", 2),
getFlake: markPrimop(misc.getFlake, 'getFlake', 1), dirOf: markPrimop(misc.dirOf, "dirOf", 1),
match: markPrimop(misc.match, 'match', 2), flakeRefToString: markPrimop(misc.flakeRefToString, "flakeRefToString", 1),
outputOf: markPrimop(misc.outputOf, 'outputOf', 2), functionArgs: markPrimop(misc.functionArgs, "functionArgs", 1),
parseDrvName: markPrimop(misc.parseDrvName, 'parseDrvName', 1), genericClosure: markPrimop(misc.genericClosure, "genericClosure", 1),
parseFlakeName: markPrimop(misc.parseFlakeName, 'parseFlakeName', 1), getFlake: markPrimop(misc.getFlake, "getFlake", 1),
placeholder: markPrimop(misc.placeholder, 'placeholder', 1), match: markPrimop(misc.match, "match", 2),
replaceStrings: markPrimop(misc.replaceStrings, 'replaceStrings', 3), outputOf: markPrimop(misc.outputOf, "outputOf", 2),
split: markPrimop(misc.split, 'split', 2), parseDrvName: markPrimop(misc.parseDrvName, "parseDrvName", 1),
splitVersion: markPrimop(misc.splitVersion, 'splitVersion', 1), parseFlakeName: markPrimop(misc.parseFlakeName, "parseFlakeName", 1),
traceVerbose: markPrimop(misc.traceVerbose, 'traceVerbose', 2), placeholder: markPrimop(misc.placeholder, "placeholder", 1),
tryEval: markPrimop(misc.tryEval, 'tryEval', 1), replaceStrings: markPrimop(misc.replaceStrings, "replaceStrings", 3),
zipAttrsWith: markPrimop(misc.zipAttrsWith, 'zipAttrsWith', 2), split: markPrimop(misc.split, "split", 2),
splitVersion: markPrimop(misc.splitVersion, "splitVersion", 1),
traceVerbose: markPrimop(misc.traceVerbose, "traceVerbose", 2),
tryEval: markPrimop(misc.tryEval, "tryEval", 1),
zipAttrsWith: markPrimop(misc.zipAttrsWith, "zipAttrsWith", 2),
builtins: create_thunk(() => builtins), builtins: create_thunk(() => builtins),
currentSystem: create_thunk(() => { currentSystem: create_thunk(() => {
throw 'Not implemented: currentSystem'; throw "Not implemented: currentSystem";
}), }),
currentTime: create_thunk(() => Date.now()), currentTime: create_thunk(() => Date.now()),
@@ -245,6 +249,6 @@ export const builtins: any = {
langVersion: 6, langVersion: 6,
nixPath: [], nixPath: [],
nixVersion: 'NIX_JS_VERSION', nixVersion: "NIX_JS_VERSION",
storeDir: '/nix/store', storeDir: "/nix/store",
}; };

View File

@@ -3,72 +3,76 @@
* These functions require Node.js/Deno APIs not available in V8 * These functions require Node.js/Deno APIs not available in V8
*/ */
import type { NixValue } from '../types'; import type { NixValue } from "../types";
export const importFunc = (path: NixValue): never => { export const importFunc = (path: NixValue): never => {
throw 'Not implemented: import'; throw "Not implemented: import";
}; };
export const scopedImport = (scope: NixValue) => (path: NixValue): never => { export const scopedImport =
throw 'Not implemented: scopedImport'; (scope: NixValue) =>
}; (path: NixValue): never => {
throw "Not implemented: scopedImport";
};
export const fetchClosure = (args: NixValue): never => { export const fetchClosure = (args: NixValue): never => {
throw 'Not implemented: fetchClosure'; throw "Not implemented: fetchClosure";
}; };
export const fetchGit = (args: NixValue): never => { export const fetchGit = (args: NixValue): never => {
throw 'Not implemented: fetchGit'; throw "Not implemented: fetchGit";
}; };
export const fetchTarball = (args: NixValue): never => { export const fetchTarball = (args: NixValue): never => {
throw 'Not implemented: fetchTarball'; throw "Not implemented: fetchTarball";
}; };
export const fetchTree = (args: NixValue): never => { export const fetchTree = (args: NixValue): never => {
throw 'Not implemented: fetchTree'; throw "Not implemented: fetchTree";
}; };
export const fetchurl = (args: NixValue): never => { export const fetchurl = (args: NixValue): never => {
throw 'Not implemented: fetchurl'; throw "Not implemented: fetchurl";
}; };
export const readDir = (path: NixValue): never => { export const readDir = (path: NixValue): never => {
throw 'Not implemented: readDir'; throw "Not implemented: readDir";
}; };
export const readFile = (path: NixValue): never => { export const readFile = (path: NixValue): never => {
throw 'Not implemented: readFile'; throw "Not implemented: readFile";
}; };
export const readFileType = (path: NixValue): never => { export const readFileType = (path: NixValue): never => {
throw 'Not implemented: readFileType'; throw "Not implemented: readFileType";
}; };
export const pathExists = (path: NixValue): never => { export const pathExists = (path: NixValue): never => {
throw 'Not implemented: pathExists'; throw "Not implemented: pathExists";
}; };
export const path = (args: NixValue): never => { export const path = (args: NixValue): never => {
throw 'Not implemented: path'; throw "Not implemented: path";
}; };
export const toFile = (name: NixValue, s: NixValue): never => { export const toFile = (name: NixValue, s: NixValue): never => {
throw 'Not implemented: toFile'; throw "Not implemented: toFile";
}; };
export const toPath = (name: NixValue, s: NixValue): never => { export const toPath = (name: NixValue, s: NixValue): never => {
throw 'Not implemented: toPath'; throw "Not implemented: toPath";
}; };
export const filterSource = (args: NixValue): never => { export const filterSource = (args: NixValue): never => {
throw 'Not implemented: filterSource'; throw "Not implemented: filterSource";
}; };
export const findFile = (search: NixValue) => (lookup: NixValue): never => { export const findFile =
throw 'Not implemented: findFile'; (search: NixValue) =>
}; (lookup: NixValue): never => {
throw "Not implemented: findFile";
};
export const getEnv = (s: NixValue): never => { export const getEnv = (s: NixValue): never => {
throw 'Not implemented: getEnv'; throw "Not implemented: getEnv";
}; };

View File

@@ -3,19 +3,23 @@
* All functions are properly curried * All functions are properly curried
*/ */
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 { force_list, force_function, force_numeric, force_int } from "../type-assert";
export const map = (f: NixValue) => (list: NixValue): NixList => export const map =
force_list(list).map(force_function(f)); (f: NixValue) =>
(list: NixValue): NixList =>
force_list(list).map(force_function(f));
export const filter = (f: NixValue) => (list: NixValue): NixList => export const filter =
force_list(list).filter(force_function(f)); (f: NixValue) =>
(list: NixValue): NixList =>
force_list(list).filter(force_function(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(force_list(forced).length);
}; };
@@ -23,19 +27,23 @@ export const head = (list: NixValue): NixValue => force_list(list)[0];
export const tail = (list: NixValue): NixList => force_list(list).slice(1); export const tail = (list: NixValue): NixList => force_list(list).slice(1);
export const elem = (x: NixValue) => (xs: NixValue): boolean => export const elem =
force_list(xs).includes(force(x)); (x: NixValue) =>
(xs: NixValue): boolean =>
force_list(xs).includes(force(x));
export const elemAt = (xs: NixValue) => (n: NixValue): NixValue => { export const elemAt =
const list = force_list(xs); (xs: NixValue) =>
const idx = Number(force_int(n)); (n: NixValue): NixValue => {
const list = force_list(xs);
const idx = Number(force_int(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}`);
} }
return list[idx]; return list[idx];
}; };
export const concatLists = (lists: NixValue): NixList => { export const concatLists = (lists: NixValue): NixList => {
return force_list(lists).reduce((acc: NixList, cur: NixValue) => { return force_list(lists).reduce((acc: NixList, cur: NixValue) => {
@@ -43,60 +51,75 @@ export const concatLists = (lists: NixValue): NixList => {
}, []); }, []);
}; };
export const concatMap = (f: NixValue) => (lists: NixValue): NixList => { export const concatMap =
const fn = force_function(f); (f: NixValue) =>
return force_list(lists).reduce((acc: NixList, cur: NixValue) => { (lists: NixValue): NixList => {
return acc.concat(force(fn(cur)) as NixList); const fn = force_function(f);
}, []); return force_list(lists).reduce((acc: NixList, cur: NixValue) => {
}; return acc.concat(force(fn(cur)) as NixList);
}, []);
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);
}, nul);
};
export const sort = (cmp: NixValue) => (list: NixValue): NixList => {
const forced_list = [...force_list(list)];
const forced_cmp = force_function(cmp);
return forced_list.sort((a, b) => {
if (force(forced_cmp(a)(b))) return -1;
if (force(forced_cmp(b)(a))) return 1;
return 0;
});
};
export const partition = (pred: NixValue) => (list: NixValue): NixAttrs => {
const forced_list = force_list(list);
const forced_pred = force_function(pred);
const attrs: NixAttrs = {
right: [],
wrong: [],
}; };
for (const elem of forced_list) {
if (force(forced_pred(elem))) { export const foldlPrime =
(attrs.right as NixList).push(elem); (op_fn: NixValue) =>
} else { (nul: NixValue) =>
(attrs.wrong as NixList).push(elem); (list: NixValue): NixValue => {
const forced_op = force_function(op_fn);
return force_list(list).reduce((acc: NixValue, cur: NixValue) => {
return 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);
return forced_list.sort((a, b) => {
if (force(forced_cmp(a)(b))) return -1;
if (force(forced_cmp(b)(a))) return 1;
return 0;
});
};
export const partition =
(pred: NixValue) =>
(list: NixValue): NixAttrs => {
const forced_list = force_list(list);
const forced_pred = force_function(pred);
const attrs: NixAttrs = {
right: [],
wrong: [],
};
for (const elem of forced_list) {
if (force(forced_pred(elem))) {
(attrs.right as NixList).push(elem);
} else {
(attrs.wrong as NixList).push(elem);
}
} }
} return attrs;
return attrs; };
};
export const genList = (f: NixValue) => (len: NixValue): NixList => { export const genList =
const func = force_function(f); (f: NixValue) =>
const length = force_int(len); (len: NixValue): NixList => {
const func = force_function(f);
const length = force_int(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}`);
} }
return [...Array(Number(length)).keys()].map(i => func(BigInt(i))); return [...Array(Number(length)).keys()].map((i) => func(BigInt(i)));
}; };
export const all = (pred: NixValue) => (list: NixValue): boolean => export const all =
force_list(list).every(force_function(pred)); (pred: NixValue) =>
(list: NixValue): boolean =>
force_list(list).every(force_function(pred));
export const any = (pred: NixValue) => (list: NixValue): boolean => export const any =
force_list(list).some(force_function(pred)); (pred: NixValue) =>
(list: NixValue): boolean =>
force_list(list).some(force_function(pred));

View File

@@ -2,17 +2,17 @@
* Math builtin functions * Math builtin functions
*/ */
import type { NixValue } from '../types'; import type { NixValue } from "../types";
import { force_numeric } from '../type-assert'; import { force_numeric } from "../type-assert";
export const ceil = (x: NixValue): bigint => { export const ceil = (x: NixValue): bigint => {
const val = force_numeric(x); const val = force_numeric(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 = force_numeric(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

@@ -2,108 +2,125 @@
* Miscellaneous unimplemented builtin functions * Miscellaneous unimplemented builtin functions
*/ */
import type { NixValue } from '../types'; import type { NixValue } from "../types";
export const getContext = (s: NixValue): never => { export const getContext = (s: NixValue): never => {
throw 'Not implemented: getContext'; throw "Not implemented: getContext";
}; };
export const hasContext = (s: NixValue): never => { export const hasContext = (s: NixValue): never => {
throw 'Not implemented: hasContext'; throw "Not implemented: hasContext";
}; };
export const hashFile = (type: NixValue) => (p: NixValue): never => { export const hashFile =
throw 'Not implemented: hashFile'; (type: NixValue) =>
}; (p: NixValue): never => {
throw "Not implemented: hashFile";
};
export const hashString = (type: NixValue) => (p: NixValue): never => { export const hashString =
throw 'Not implemented: hashString'; (type: NixValue) =>
}; (p: NixValue): never => {
throw "Not implemented: hashString";
};
export const convertHash = (args: NixValue): never => { export const convertHash = (args: NixValue): never => {
throw 'Not implemented: convertHash'; throw "Not implemented: convertHash";
}; };
export const unsafeDiscardOutputDependency = (s: NixValue): never => { export const unsafeDiscardOutputDependency = (s: NixValue): never => {
throw 'Not implemented: unsafeDiscardOutputDependency'; throw "Not implemented: unsafeDiscardOutputDependency";
}; };
export const unsafeDiscardStringContext = (s: NixValue): never => { export const unsafeDiscardStringContext = (s: NixValue): never => {
throw 'Not implemented: unsafeDiscardStringContext'; throw "Not implemented: unsafeDiscardStringContext";
}; };
export const unsafeGetAttrPos = (s: NixValue): never => { export const unsafeGetAttrPos = (s: NixValue): never => {
throw 'Not implemented: unsafeGetAttrPos'; throw "Not implemented: unsafeGetAttrPos";
}; };
export const addDrvOutputDependencies = (s: NixValue): never => { export const addDrvOutputDependencies = (s: NixValue): never => {
throw 'Not implemented: addDrvOutputDependencies'; throw "Not implemented: addDrvOutputDependencies";
}; };
export const compareVersions = (s1: NixValue) => (s2: NixValue): never => { export const compareVersions =
throw 'Not implemented: compareVersions'; (s1: NixValue) =>
}; (s2: NixValue): never => {
throw "Not implemented: compareVersions";
};
export const dirOf = (s: NixValue): never => { export const dirOf = (s: NixValue): never => {
throw 'Not implemented: dirOf'; throw "Not implemented: dirOf";
}; };
export const flakeRefToString = (attrs: NixValue): never => { export const flakeRefToString = (attrs: NixValue): never => {
throw 'Not implemented: flakeRefToString'; throw "Not implemented: flakeRefToString";
}; };
export const functionArgs = (f: NixValue): never => { export const functionArgs = (f: NixValue): never => {
throw 'Not implemented: functionArgs'; throw "Not implemented: functionArgs";
}; };
export const genericClosure = (args: NixValue): never => { export const genericClosure = (args: NixValue): never => {
throw 'Not implemented: genericClosure'; throw "Not implemented: genericClosure";
}; };
export const getFlake = (attrs: NixValue): never => { export const getFlake = (attrs: NixValue): never => {
throw 'Not implemented: getFlake'; throw "Not implemented: getFlake";
}; };
export const match = (regex: NixValue) => (str: NixValue): never => { export const match =
throw 'Not implemented: match'; (regex: NixValue) =>
}; (str: NixValue): never => {
throw "Not implemented: match";
};
export const outputOf = (drv: NixValue) => (out: NixValue): never => { export const outputOf =
throw 'Not implemented: outputOf'; (drv: NixValue) =>
}; (out: NixValue): never => {
throw "Not implemented: outputOf";
};
export const parseDrvName = (s: NixValue): never => { export const parseDrvName = (s: NixValue): never => {
throw 'Not implemented: parseDrvName'; throw "Not implemented: parseDrvName";
}; };
export const parseFlakeName = (s: NixValue): never => { export const parseFlakeName = (s: NixValue): never => {
throw 'Not implemented: parseFlakeName'; throw "Not implemented: parseFlakeName";
}; };
export const placeholder = (output: NixValue): never => { export const placeholder = (output: NixValue): never => {
throw 'Not implemented: placeholder'; throw "Not implemented: placeholder";
}; };
export const replaceStrings = (from: NixValue) => (to: NixValue) => (s: NixValue): never => { export const replaceStrings =
throw 'Not implemented: replaceStrings'; (from: NixValue) =>
}; (to: NixValue) =>
(s: NixValue): never => {
throw "Not implemented: replaceStrings";
};
export const split = (regex: NixValue, str: NixValue): never => { export const split = (regex: NixValue, str: NixValue): never => {
throw 'Not implemented: split'; throw "Not implemented: split";
}; };
export const splitVersion = (s: NixValue): never => { export const splitVersion = (s: NixValue): never => {
throw 'Not implemented: splitVersion'; throw "Not implemented: splitVersion";
}; };
export const traceVerbose = (e1: NixValue, e2: NixValue): never => { export const traceVerbose = (e1: NixValue, e2: NixValue): never => {
throw 'Not implemented: traceVerbose'; throw "Not implemented: traceVerbose";
}; };
export const tryEval = (e1: NixValue) => (e2: NixValue): never => { export const tryEval =
throw 'Not implemented: tryEval'; (e1: NixValue) =>
}; (e2: NixValue): never => {
throw "Not implemented: tryEval";
};
export const zipAttrsWith = (f: NixValue) => (list: NixValue): never => { export const zipAttrsWith =
throw 'Not implemented: zipAttrsWith'; (f: NixValue) =>
}; (list: NixValue): never => {
throw "Not implemented: zipAttrsWith";
};

View File

@@ -2,32 +2,37 @@
* String operation builtin functions * String operation builtin functions
*/ */
import type { NixValue } from '../types'; import type { NixValue } from "../types";
import { force_string, force_list, force_int } from '../type-assert'; import { force_string, force_list, force_int } from "../type-assert";
export const stringLength = (e: NixValue): number => force_string(e).length; export const stringLength = (e: NixValue): number => force_string(e).length;
export const substring = (start: NixValue) => (len: NixValue) => (s: NixValue): string => { export const substring =
const str = force_string(s); (start: NixValue) =>
const startPos = Number(force_int(start)); (len: NixValue) =>
const length = Number(force_int(len)); (s: NixValue): string => {
return str.substring(startPos, startPos + length); const str = force_string(s);
}; const startPos = Number(force_int(start));
const length = Number(force_int(len));
return str.substring(startPos, startPos + length);
};
export const concatStringsSep = (sep: NixValue) => (list: NixValue): string => export const concatStringsSep =
force_list(list).join(force_string(sep)); (sep: NixValue) =>
(list: NixValue): string =>
force_list(list).join(force_string(sep));
export const baseNameOf = (x: NixValue): string => { export const baseNameOf = (x: NixValue): string => {
const str = force_string(x); const str = force_string(x);
if (str.length === 0) return ''; if (str.length === 0) return "";
let last = str.length - 1; let last = str.length - 1;
if (str[last] === '/' && last > 0) last -= 1; if (str[last] === "/" && last > 0) last -= 1;
let pos = last; let pos = last;
while (pos >= 0 && str[pos] !== '/') pos -= 1; while (pos >= 0 && str[pos] !== "/") pos -= 1;
if (pos !== 0 || (pos === 0 && str[pos] === '/')) { if (pos !== 0 || (pos === 0 && str[pos] === "/")) {
pos += 1; pos += 1;
} }

View File

@@ -2,26 +2,36 @@
* Type checking builtin functions * Type checking builtin functions
*/ */
import type { NixAttrs, NixBool, NixFloat, NixFunction, NixInt, NixList, NixNull, NixString, NixValue } from '../types'; import type {
import { force } from '../thunk'; NixAttrs,
NixBool,
NixFloat,
NixFunction,
NixInt,
NixList,
NixNull,
NixString,
NixValue,
} from "../types";
import { force } from "../thunk";
export const isAttrs = (e: NixValue): e is NixAttrs => { export const isAttrs = (e: NixValue): e is NixAttrs => {
const val = force(e); const val = force(e);
return typeof val === 'object' && !Array.isArray(val) && val !== null; return typeof val === "object" && !Array.isArray(val) && val !== null;
}; };
export const isBool = (e: NixValue): e is NixBool => typeof force(e) === 'boolean'; export const isBool = (e: NixValue): e is NixBool => typeof force(e) === "boolean";
export const isFloat = (e: NixValue): e is NixFloat => { export const isFloat = (e: NixValue): e is NixFloat => {
const val = force(e); const val = force(e);
return typeof val === 'number'; // Only number is float return typeof val === "number"; // Only number is float
}; };
export const isFunction = (e: NixValue): e is NixFunction => typeof force(e) === 'function'; export const isFunction = (e: NixValue): e is NixFunction => typeof force(e) === "function";
export const isInt = (e: NixValue): e is NixInt => { export const isInt = (e: NixValue): e is NixInt => {
const val = force(e); const val = force(e);
return typeof val === 'bigint'; // Only bigint is int return typeof val === "bigint"; // Only bigint is int
}; };
export const isList = (e: NixValue): e is NixList => Array.isArray(force(e)); export const isList = (e: NixValue): e is NixList => Array.isArray(force(e));
@@ -29,22 +39,22 @@ export const isList = (e: NixValue): e is NixList => Array.isArray(force(e));
export const isNull = (e: NixValue): e is NixNull => force(e) === null; export const isNull = (e: NixValue): e is NixNull => force(e) === null;
export const isPath = (e: NixValue): never => { export const isPath = (e: NixValue): never => {
throw 'Not implemented: isPath'; throw "Not implemented: isPath";
}; };
export const isString = (e: NixValue): e is NixString => typeof force(e) === 'string'; export const isString = (e: NixValue): e is NixString => typeof force(e) === "string";
export const typeOf = (e: NixValue): string => { export const typeOf = (e: NixValue): string => {
const val = force(e); const val = force(e);
if (typeof val === 'bigint') return 'int'; if (typeof val === "bigint") return "int";
if (typeof val === 'number') return 'float'; if (typeof val === "number") return "float";
if (typeof val === 'boolean') return 'bool'; if (typeof val === "boolean") return "bool";
if (typeof val === 'string') return 'string'; if (typeof val === "string") return "string";
if (val === null) return 'null'; if (val === null) return "null";
if (Array.isArray(val)) return 'list'; if (Array.isArray(val)) return "list";
if (typeof val === 'function') return 'lambda'; if (typeof val === "function") return "lambda";
if (typeof val === 'object') return 'set'; if (typeof val === "object") return "set";
throw new TypeError(`Unknown Nix type: ${typeof val}`); throw new TypeError(`Unknown Nix type: ${typeof val}`);
}; };

View File

@@ -3,8 +3,8 @@
* Implements attribute selection, parameter validation, and lazy sets * Implements attribute selection, parameter validation, and lazy sets
*/ */
import type { NixValue, NixAttrs } from './types'; import type { NixValue, NixAttrs } from "./types";
import { force_attrs, force_string } from './type-assert'; import { force_attrs, force_string } from "./type-assert";
/** /**
* Select an attribute from an attribute set * Select an attribute from an attribute set
@@ -35,11 +35,7 @@ export const select = (obj: NixValue, key: NixValue): NixValue => {
* @param default_val - Value to return if key not found * @param default_val - Value to return if key not found
* @returns obj[key] if exists, otherwise default_val * @returns obj[key] if exists, otherwise default_val
*/ */
export const select_with_default = ( export const select_with_default = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => {
obj: NixValue,
key: NixValue,
default_val: NixValue
): NixValue => {
const forced_obj = force_attrs(obj); const forced_obj = force_attrs(obj);
const forced_key = force_string(key); const forced_key = force_string(key);
@@ -72,7 +68,7 @@ export const select_with_default = (
export const validate_params = ( export const validate_params = (
arg: NixValue, arg: NixValue,
required: string[] | null, required: string[] | null,
allowed: string[] | null allowed: string[] | null,
): NixAttrs => { ): NixAttrs => {
const forced_arg = force_attrs(arg); const forced_arg = force_attrs(arg);

View File

@@ -4,10 +4,10 @@
* All functionality is exported via the global `Nix` object * All functionality is exported via the global `Nix` object
*/ */
import { create_thunk, force, is_thunk, IS_THUNK } from './thunk'; import { create_thunk, force, is_thunk, IS_THUNK } from "./thunk";
import { select, select_with_default, validate_params } from './helpers'; import { select, select_with_default, validate_params } from "./helpers";
import { op } from './operators'; import { op } from "./operators";
import { builtins, IS_PRIMOP } from './builtins'; import { builtins, IS_PRIMOP } from "./builtins";
export type NixRuntime = typeof Nix; export type NixRuntime = typeof Nix;
@@ -31,5 +31,5 @@ export const Nix = {
globalThis.Nix = Nix; globalThis.Nix = Nix;
declare global { declare global {
var Nix: NixRuntime var Nix: NixRuntime;
} }

View File

@@ -3,9 +3,9 @@
* Implements all binary and unary operators used by codegen * Implements all binary and unary operators used by codegen
*/ */
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 { force_numeric, force_list, force_attrs, coerce_numeric } from "./type-assert";
/** /**
* Operator object exported as Nix.op * Operator object exported as Nix.op
@@ -33,10 +33,10 @@ export const op = {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b)); const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
if (bv === 0 || bv === 0n) { if (bv === 0 || bv === 0n) {
throw new RangeError('Division by zero'); throw new RangeError("Division by zero");
} }
return (av as any) / (bv as any) return (av as any) / (bv as any);
}, },
// Comparison operators (JavaScript natively supports bigint/number mixed comparison) // Comparison operators (JavaScript natively supports bigint/number mixed comparison)
@@ -44,13 +44,13 @@ export const op = {
// FIXME: Int and Float // FIXME: Int and Float
const av = force(a); const av = force(a);
const bv = force(b); const bv = force(b);
return av === bv return av === bv;
}, },
neq: (a: NixValue, b: NixValue): boolean => { neq: (a: NixValue, b: NixValue): boolean => {
// FIXME: Int and Float // FIXME: Int and Float
const av = force(a); const av = force(a);
const bv = force(b); const bv = force(b);
return av !== bv return av !== bv;
}, },
lt: (a: NixValue, b: NixValue): boolean => { lt: (a: NixValue, b: NixValue): boolean => {
// FIXME: Non-numeric // FIXME: Non-numeric

View File

@@ -3,13 +3,13 @@
* Implements thunks for lazy evaluation of Nix expressions * Implements thunks for lazy evaluation of Nix expressions
*/ */
import type { NixValue, NixThunkInterface } from './types'; import type { NixValue, NixThunkInterface } from "./types";
/** /**
* Symbol used to mark objects as thunks * Symbol used to mark objects as thunks
* This is exported to Rust via Nix.IS_THUNK * This is exported to Rust via Nix.IS_THUNK
*/ */
export const IS_THUNK = Symbol('is_thunk'); export const IS_THUNK = Symbol("is_thunk");
/** /**
* NixThunk class - represents a lazy, unevaluated expression * NixThunk class - represents a lazy, unevaluated expression
@@ -35,12 +35,7 @@ export class NixThunk implements NixThunkInterface {
* @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 is_thunk = (value: unknown): value is NixThunkInterface => {
return ( return value !== null && typeof value === "object" && IS_THUNK in value && value[IS_THUNK] === true;
value !== null &&
typeof value === 'object' &&
IS_THUNK in value &&
value[IS_THUNK] === true
);
}; };
/** /**

View File

@@ -3,8 +3,8 @@
* These functions force evaluation and verify the type, throwing errors on mismatch * These functions force evaluation and verify the type, throwing errors on mismatch
*/ */
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";
/** /**
* Force a value and assert it's a list * Force a value and assert it's a list
@@ -24,7 +24,7 @@ export const force_list = (value: NixValue): NixList => {
*/ */
export const force_function = (value: NixValue): NixFunction => { export const force_function = (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 ${typeof forced}`); throw new TypeError(`Expected function, got ${typeof forced}`);
} }
return forced; return forced;
@@ -36,7 +36,7 @@ export const force_function = (value: NixValue): NixFunction => {
*/ */
export const force_attrs = (value: NixValue): NixAttrs => { export const force_attrs = (value: NixValue): NixAttrs => {
const forced = force(value); const forced = force(value);
if (typeof forced !== 'object' || forced === null || Array.isArray(forced)) { if (typeof forced !== "object" || forced === null || Array.isArray(forced)) {
throw new TypeError(`Expected attribute set, got ${typeof forced}`); throw new TypeError(`Expected attribute set, got ${typeof forced}`);
} }
return forced as NixAttrs; return forced as NixAttrs;
@@ -48,7 +48,7 @@ export const force_attrs = (value: NixValue): NixAttrs => {
*/ */
export const force_string = (value: NixValue): string => { export const force_string = (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 ${typeof forced}`); throw new TypeError(`Expected string, got ${typeof forced}`);
} }
return forced; return forced;
@@ -60,7 +60,7 @@ export const force_string = (value: NixValue): string => {
*/ */
export const force_bool = (value: NixValue): boolean => { export const force_bool = (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 ${typeof forced}`); throw new TypeError(`Expected boolean, got ${typeof forced}`);
} }
return forced; return forced;
@@ -72,7 +72,7 @@ export const force_bool = (value: NixValue): boolean => {
*/ */
export const force_int = (value: NixValue): NixInt => { export const force_int = (value: NixValue): NixInt => {
const forced = force(value); const forced = force(value);
if (typeof forced === 'bigint') { if (typeof forced === "bigint") {
return forced; return forced;
} }
throw new TypeError(`Expected int, got ${typeof forced}`); throw new TypeError(`Expected int, got ${typeof forced}`);
@@ -84,7 +84,7 @@ export const force_int = (value: NixValue): NixInt => {
*/ */
export const force_float = (value: NixValue): NixFloat => { export const force_float = (value: NixValue): NixFloat => {
const forced = force(value); const forced = force(value);
if (typeof forced === 'number') { if (typeof forced === "number") {
return forced; return forced;
} }
throw new TypeError(`Expected float, got ${typeof forced}`); throw new TypeError(`Expected float, got ${typeof forced}`);
@@ -96,7 +96,7 @@ export const force_float = (value: NixValue): NixFloat => {
*/ */
export const force_numeric = (value: NixValue): NixNumber => { export const force_numeric = (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;
} }
throw new TypeError(`Expected numeric type, got ${typeof forced}`); throw new TypeError(`Expected numeric type, got ${typeof forced}`);
@@ -107,19 +107,13 @@ 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 = ( export const coerce_numeric = (a: NixNumber, b: NixNumber): [NixFloat, NixFloat] | [NixInt, NixInt] => {
a: NixNumber, const aIsInt = typeof a === "bigint";
b: NixNumber const bIsInt = typeof b === "bigint";
): [NixFloat, NixFloat] | [NixInt, NixInt] => {
const aIsInt = typeof a === 'bigint';
const bIsInt = typeof b === 'bigint';
// If either is float, convert both to float // If either is float, convert both to float
if (!aIsInt || !bIsInt) { if (!aIsInt || !bIsInt) {
return [ return [aIsInt ? Number(a) : a, bIsInt ? Number(b) : b];
aIsInt ? Number(a) : a,
bIsInt ? Number(b) : b
];
} }
// Both are integers // Both are integers

View File

@@ -32,12 +32,7 @@ export type NixPrimitive = NixNull | NixBool | NixInt | NixFloat | NixString;
* NixValue: Union type representing any possible Nix value * NixValue: Union type representing any possible Nix value
* This is the core type used throughout the runtime * This is the core type used throughout the runtime
*/ */
export type NixValue = export type NixValue = NixPrimitive | NixList | NixAttrs | NixFunction | NixThunkInterface;
| NixPrimitive
| NixList
| NixAttrs
| NixFunction
| NixThunkInterface;
// 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;