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
claude-code
# Node.js tooling for TypeScript build
nodejs
nodePackages.npm
biome
];
};
}

View File

@@ -2,52 +2,68 @@
* Arithmetic builtin functions
*/
import type { NixBool, NixInt, NixNumber, NixValue } from '../types';
import { force_numeric, coerce_numeric, force_int } from '../type-assert';
import type { NixBool, NixInt, NixNumber, NixValue } from "../types";
import { force_numeric, coerce_numeric, force_int } from "../type-assert";
export const add = (a: NixValue) => (b: NixValue): bigint | number => {
export const add =
(a: NixValue) =>
(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 =
(a: NixValue) =>
(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 =
(a: NixValue) =>
(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 =
(a: NixValue) =>
(b: NixValue): NixNumber => {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
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
export const bitAnd = (a: NixValue) => (b: NixValue): NixInt => {
export const bitAnd =
(a: NixValue) =>
(b: NixValue): NixInt => {
const av = force_int(a);
const bv = force_int(b);
return av & bv;
};
};
export const bitOr = (a: NixValue) => (b: NixValue): NixInt => {
export const bitOr =
(a: NixValue) =>
(b: NixValue): NixInt => {
const av = force_int(a);
const bv = force_int(b);
return av | bv;
};
};
export const bitXor = (a: NixValue) => (b: NixValue): NixInt => {
export const bitXor =
(a: NixValue) =>
(b: NixValue): NixInt => {
const av = force_int(a);
const bv = force_int(b);
return av ^ bv;
};
};
export const lessThan = (a: NixValue) => (b: NixValue): NixBool =>
export const lessThan =
(a: NixValue) =>
(b: NixValue): NixBool =>
force_numeric(a) < force_numeric(b);

View File

@@ -2,20 +2,26 @@
* Attribute set operation builtin functions
*/
import type { NixValue, NixAttrs, NixList } from '../types';
import { force_attrs, force_string, force_function, force_list } from '../type-assert';
import type { NixValue, NixAttrs, NixList } from "../types";
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 attrValues = (set: NixValue): NixValue[] => Object.values(force_attrs(set));
export const getAttr = (s: NixValue) => (set: NixValue): NixValue =>
export const getAttr =
(s: NixValue) =>
(set: NixValue): NixValue =>
force_attrs(set)[force_string(s)];
export const hasAttr = (s: NixValue) => (set: NixValue): boolean =>
export const hasAttr =
(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 =
(f: NixValue) =>
(attrs: NixValue): NixAttrs => {
const new_attrs: NixAttrs = {};
const forced_attrs = force_attrs(attrs);
const forced_f = force_function(f);
@@ -23,7 +29,7 @@ export const mapAttrs = (f: NixValue) => (attrs: NixValue): NixAttrs => {
new_attrs[key] = forced_f(key)(forced_attrs[key]);
}
return new_attrs;
};
};
export const listToAttrs = (e: NixValue): NixAttrs => {
const attrs: NixAttrs = {};
@@ -35,7 +41,9 @@ export const listToAttrs = (e: NixValue): NixAttrs => {
return attrs;
};
export const intersectAttrs = (e1: NixValue) => (e2: NixValue): NixAttrs => {
export const intersectAttrs =
(e1: NixValue) =>
(e2: NixValue): NixAttrs => {
const f1 = force_attrs(e1);
const f2 = force_attrs(e2);
const attrs: NixAttrs = {};
@@ -45,16 +53,20 @@ export const intersectAttrs = (e1: NixValue) => (e2: NixValue): NixAttrs => {
}
}
return attrs;
};
};
export const catAttrs = (attr: NixValue) => (list: NixValue): NixList => {
export const catAttrs =
(attr: NixValue) =>
(list: NixValue): NixList => {
const key = force_string(attr);
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 =
(f: NixValue) =>
(list: NixValue): NixAttrs => {
const attrs: NixAttrs = {};
const forced_f = force_function(f);
const forced_list = force_list(list);
@@ -64,4 +76,4 @@ export const groupBy = (f: NixValue) => (list: NixValue): NixAttrs => {
(attrs[key] as NixList).push(elem);
}
return attrs;
};
};

View File

@@ -2,24 +2,24 @@
* Conversion and serialization builtin functions (unimplemented)
*/
import type { NixValue } from '../types';
import type { NixValue } from "../types";
export const fromJSON = (e: NixValue): never => {
throw 'Not implemented: fromJSON';
throw "Not implemented: fromJSON";
};
export const fromTOML = (e: NixValue): never => {
throw 'Not implemented: fromTOML';
throw "Not implemented: fromTOML";
};
export const toJSON = (e: NixValue): never => {
throw 'Not implemented: toJSON';
throw "Not implemented: toJSON";
};
export const toXML = (e: NixValue): never => {
throw 'Not implemented: toXML';
throw "Not implemented: toXML";
};
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
*/
import type { NixValue } from '../types';
import { force } from '../thunk';
import type { NixValue } from "../types";
import { force } from "../thunk";
export const seq = (e1: NixValue) => (e2: NixValue): NixValue => {
export const seq =
(e1: NixValue) =>
(e2: NixValue): NixValue => {
force(e1); // Force evaluation of e1
return e2;
};
};
export const deepSeq = (e1: NixValue) => (e2: NixValue): never => {
throw 'Not implemented: deepSeq';
};
export const deepSeq =
(e1: NixValue) =>
(e2: NixValue): never => {
throw "Not implemented: deepSeq";
};
export const abort = (s: NixValue): never => {
throw `evaluation aborted with the following error message: '${force(s)}'`;
@@ -27,9 +31,11 @@ export const trace = (e1: NixValue, e2: NixValue): NixValue => {
return e2;
};
export const warn = (e1: NixValue) => (e2: NixValue): NixValue => {
export const warn =
(e1: NixValue) =>
(e2: NixValue): NixValue => {
console.log(`evaluation warning: ${force(e1)}`);
return e2;
};
};
export const breakFunc = (v: NixValue): NixValue => v;

View File

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

View File

@@ -3,72 +3,76 @@
* 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 => {
throw 'Not implemented: import';
throw "Not implemented: import";
};
export const scopedImport = (scope: NixValue) => (path: NixValue): never => {
throw 'Not implemented: scopedImport';
};
export const scopedImport =
(scope: NixValue) =>
(path: NixValue): never => {
throw "Not implemented: scopedImport";
};
export const fetchClosure = (args: NixValue): never => {
throw 'Not implemented: fetchClosure';
throw "Not implemented: fetchClosure";
};
export const fetchGit = (args: NixValue): never => {
throw 'Not implemented: fetchGit';
throw "Not implemented: fetchGit";
};
export const fetchTarball = (args: NixValue): never => {
throw 'Not implemented: fetchTarball';
throw "Not implemented: fetchTarball";
};
export const fetchTree = (args: NixValue): never => {
throw 'Not implemented: fetchTree';
throw "Not implemented: fetchTree";
};
export const fetchurl = (args: NixValue): never => {
throw 'Not implemented: fetchurl';
throw "Not implemented: fetchurl";
};
export const readDir = (path: NixValue): never => {
throw 'Not implemented: readDir';
throw "Not implemented: readDir";
};
export const readFile = (path: NixValue): never => {
throw 'Not implemented: readFile';
throw "Not implemented: readFile";
};
export const readFileType = (path: NixValue): never => {
throw 'Not implemented: readFileType';
throw "Not implemented: readFileType";
};
export const pathExists = (path: NixValue): never => {
throw 'Not implemented: pathExists';
throw "Not implemented: pathExists";
};
export const path = (args: NixValue): never => {
throw 'Not implemented: path';
throw "Not implemented: path";
};
export const toFile = (name: NixValue, s: NixValue): never => {
throw 'Not implemented: toFile';
throw "Not implemented: toFile";
};
export const toPath = (name: NixValue, s: NixValue): never => {
throw 'Not implemented: toPath';
throw "Not implemented: toPath";
};
export const filterSource = (args: NixValue): never => {
throw 'Not implemented: filterSource';
throw "Not implemented: filterSource";
};
export const findFile = (search: NixValue) => (lookup: NixValue): never => {
throw 'Not implemented: findFile';
};
export const findFile =
(search: NixValue) =>
(lookup: NixValue): never => {
throw "Not implemented: findFile";
};
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
*/
import type { NixValue, NixList, NixAttrs } from '../types';
import { force } from '../thunk';
import { force_list, force_function, force_numeric, force_int } from '../type-assert';
import type { NixValue, NixList, NixAttrs } from "../types";
import { force } from "../thunk";
import { force_list, force_function, force_numeric, force_int } from "../type-assert";
export const map = (f: NixValue) => (list: NixValue): NixList =>
export const map =
(f: NixValue) =>
(list: NixValue): NixList =>
force_list(list).map(force_function(f));
export const filter = (f: NixValue) => (list: NixValue): NixList =>
export const filter =
(f: NixValue) =>
(list: NixValue): NixList =>
force_list(list).filter(force_function(f));
export const length = (e: NixValue): bigint => {
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);
};
@@ -23,10 +27,14 @@ export const head = (list: NixValue): NixValue => force_list(list)[0];
export const tail = (list: NixValue): NixList => force_list(list).slice(1);
export const elem = (x: NixValue) => (xs: NixValue): boolean =>
export const elem =
(x: NixValue) =>
(xs: NixValue): boolean =>
force_list(xs).includes(force(x));
export const elemAt = (xs: NixValue) => (n: NixValue): NixValue => {
export const elemAt =
(xs: NixValue) =>
(n: NixValue): NixValue => {
const list = force_list(xs);
const idx = Number(force_int(n));
@@ -35,7 +43,7 @@ export const elemAt = (xs: NixValue) => (n: NixValue): NixValue => {
}
return list[idx];
};
};
export const concatLists = (lists: NixValue): NixList => {
return force_list(lists).reduce((acc: NixList, cur: NixValue) => {
@@ -43,21 +51,28 @@ export const concatLists = (lists: NixValue): NixList => {
}, []);
};
export const concatMap = (f: NixValue) => (lists: NixValue): NixList => {
export const concatMap =
(f: NixValue) =>
(lists: NixValue): 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 => {
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 => {
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) => {
@@ -65,9 +80,11 @@ export const sort = (cmp: NixValue) => (list: NixValue): NixList => {
if (force(forced_cmp(b)(a))) return 1;
return 0;
});
};
};
export const partition = (pred: NixValue) => (list: NixValue): NixAttrs => {
export const partition =
(pred: NixValue) =>
(list: NixValue): NixAttrs => {
const forced_list = force_list(list);
const forced_pred = force_function(pred);
const attrs: NixAttrs = {
@@ -82,9 +99,11 @@ export const partition = (pred: NixValue) => (list: NixValue): NixAttrs => {
}
}
return attrs;
};
};
export const genList = (f: NixValue) => (len: NixValue): NixList => {
export const genList =
(f: NixValue) =>
(len: NixValue): NixList => {
const func = force_function(f);
const length = force_int(len);
@@ -92,11 +111,15 @@ export const genList = (f: NixValue) => (len: NixValue): NixList => {
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 =
(pred: NixValue) =>
(list: NixValue): boolean =>
force_list(list).every(force_function(pred));
export const any = (pred: NixValue) => (list: NixValue): boolean =>
export const any =
(pred: NixValue) =>
(list: NixValue): boolean =>
force_list(list).some(force_function(pred));

View File

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

View File

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

View File

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

View File

@@ -2,26 +2,36 @@
* Type checking builtin functions
*/
import type { NixAttrs, NixBool, NixFloat, NixFunction, NixInt, NixList, NixNull, NixString, NixValue } from '../types';
import { force } from '../thunk';
import type {
NixAttrs,
NixBool,
NixFloat,
NixFunction,
NixInt,
NixList,
NixNull,
NixString,
NixValue,
} from "../types";
import { force } from "../thunk";
export const isAttrs = (e: NixValue): e is NixAttrs => {
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 => {
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 => {
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));
@@ -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 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 => {
const val = force(e);
if (typeof val === 'bigint') return 'int';
if (typeof val === 'number') return 'float';
if (typeof val === 'boolean') return 'bool';
if (typeof val === 'string') return 'string';
if (val === null) return 'null';
if (Array.isArray(val)) return 'list';
if (typeof val === 'function') return 'lambda';
if (typeof val === 'object') return 'set';
if (typeof val === "bigint") return "int";
if (typeof val === "number") return "float";
if (typeof val === "boolean") return "bool";
if (typeof val === "string") return "string";
if (val === null) return "null";
if (Array.isArray(val)) return "list";
if (typeof val === "function") return "lambda";
if (typeof val === "object") return "set";
throw new TypeError(`Unknown Nix type: ${typeof val}`);
};

View File

@@ -3,8 +3,8 @@
* Implements attribute selection, parameter validation, and lazy sets
*/
import type { NixValue, NixAttrs } from './types';
import { force_attrs, force_string } from './type-assert';
import type { NixValue, NixAttrs } from "./types";
import { force_attrs, force_string } from "./type-assert";
/**
* 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
* @returns obj[key] if exists, otherwise default_val
*/
export const select_with_default = (
obj: NixValue,
key: NixValue,
default_val: NixValue
): NixValue => {
export const select_with_default = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => {
const forced_obj = force_attrs(obj);
const forced_key = force_string(key);
@@ -72,7 +68,7 @@ export const select_with_default = (
export const validate_params = (
arg: NixValue,
required: string[] | null,
allowed: string[] | null
allowed: string[] | null,
): NixAttrs => {
const forced_arg = force_attrs(arg);

View File

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

View File

@@ -3,9 +3,9 @@
* Implements all binary and unary operators used by codegen
*/
import type { NixValue, NixList, NixAttrs } from './types';
import { force } from './thunk';
import { force_numeric, force_list, force_attrs, coerce_numeric } from './type-assert';
import type { NixValue, NixList, NixAttrs } from "./types";
import { force } from "./thunk";
import { force_numeric, force_list, force_attrs, coerce_numeric } from "./type-assert";
/**
* Operator object exported as Nix.op
@@ -33,10 +33,10 @@ export const op = {
const [av, bv] = coerce_numeric(force_numeric(a), force_numeric(b));
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)
@@ -44,13 +44,13 @@ export const op = {
// FIXME: Int and Float
const av = force(a);
const bv = force(b);
return av === bv
return av === bv;
},
neq: (a: NixValue, b: NixValue): boolean => {
// FIXME: Int and Float
const av = force(a);
const bv = force(b);
return av !== bv
return av !== bv;
},
lt: (a: NixValue, b: NixValue): boolean => {
// FIXME: Non-numeric

View File

@@ -3,13 +3,13 @@
* 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
* 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
@@ -35,12 +35,7 @@ export class NixThunk implements NixThunkInterface {
* @returns true if value is a NixThunk
*/
export const is_thunk = (value: unknown): value is NixThunkInterface => {
return (
value !== null &&
typeof value === 'object' &&
IS_THUNK in value &&
value[IS_THUNK] === true
);
return value !== null && typeof value === "object" && IS_THUNK in value && value[IS_THUNK] === true;
};
/**

View File

@@ -3,8 +3,8 @@
* These functions force evaluation and verify the type, throwing errors on mismatch
*/
import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber } from './types';
import { force } from './thunk';
import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber } from "./types";
import { force } from "./thunk";
/**
* 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 => {
const forced = force(value);
if (typeof forced !== 'function') {
if (typeof forced !== "function") {
throw new TypeError(`Expected function, got ${typeof forced}`);
}
return forced;
@@ -36,7 +36,7 @@ export const force_function = (value: NixValue): NixFunction => {
*/
export const force_attrs = (value: NixValue): NixAttrs => {
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}`);
}
return forced as NixAttrs;
@@ -48,7 +48,7 @@ export const force_attrs = (value: NixValue): NixAttrs => {
*/
export const force_string = (value: NixValue): string => {
const forced = force(value);
if (typeof forced !== 'string') {
if (typeof forced !== "string") {
throw new TypeError(`Expected string, got ${typeof forced}`);
}
return forced;
@@ -60,7 +60,7 @@ export const force_string = (value: NixValue): string => {
*/
export const force_bool = (value: NixValue): boolean => {
const forced = force(value);
if (typeof forced !== 'boolean') {
if (typeof forced !== "boolean") {
throw new TypeError(`Expected boolean, got ${typeof forced}`);
}
return forced;
@@ -72,7 +72,7 @@ export const force_bool = (value: NixValue): boolean => {
*/
export const force_int = (value: NixValue): NixInt => {
const forced = force(value);
if (typeof forced === 'bigint') {
if (typeof forced === "bigint") {
return 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 => {
const forced = force(value);
if (typeof forced === 'number') {
if (typeof forced === "number") {
return 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 => {
const forced = force(value);
if (typeof forced === 'bigint' || typeof forced === 'number') {
if (typeof forced === "bigint" || typeof forced === "number") {
return 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
* @returns [a, b] tuple of coerced values
*/
export const coerce_numeric = (
a: NixNumber,
b: NixNumber
): [NixFloat, NixFloat] | [NixInt, NixInt] => {
const aIsInt = typeof a === 'bigint';
const bIsInt = typeof b === 'bigint';
export const coerce_numeric = (a: NixNumber, b: NixNumber): [NixFloat, NixFloat] | [NixInt, NixInt] => {
const aIsInt = typeof a === "bigint";
const bIsInt = typeof b === "bigint";
// If either is float, convert both to float
if (!aIsInt || !bIsInt) {
return [
aIsInt ? Number(a) : a,
bIsInt ? Number(b) : b
];
return [aIsInt ? Number(a) : a, bIsInt ? Number(b) : b];
}
// 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
* This is the core type used throughout the runtime
*/
export type NixValue =
| NixPrimitive
| NixList
| NixAttrs
| NixFunction
| NixThunkInterface;
export type NixValue = NixPrimitive | NixList | NixAttrs | NixFunction | NixThunkInterface;
// Operator function signatures
export type BinaryOp<T = NixValue, U = NixValue, R = NixValue> = (a: T, b: U) => R;