feat: __functor

This commit is contained in:
2026-01-11 14:53:05 +08:00
parent 0538463bf0
commit 160b59b8bf
4 changed files with 50 additions and 10 deletions

View File

@@ -3,10 +3,11 @@
*/ */
import type { NixValue, NixAttrs, NixBool, NixString } from "./types"; import type { NixValue, NixAttrs, NixBool, NixString } from "./types";
import { forceAttrs, forceString } from "./type-assert"; import { forceAttrs, forceFunction, forceString, typeName } from "./type-assert";
import { isAttrs } from "./builtins/type-check"; import { isAttrs } from "./builtins/type-check";
import { coerceToString, StringCoercionMode } from "./builtins/conversion"; import { coerceToString, StringCoercionMode } from "./builtins/conversion";
import { type NixStringContext, mkStringWithContext } from "./string-context"; import { type NixStringContext, mkStringWithContext } from "./string-context";
import { force } from "./thunk";
/** /**
* Concatenate multiple values into a string with context * Concatenate multiple values into a string with context
@@ -144,3 +145,20 @@ export const validateParams = (
return forced_arg; return forced_arg;
}; };
export const call = (func: NixValue, arg: NixValue): NixValue => {
const forcedFunc = force(func);
if (typeof forcedFunc === "function") {
return forcedFunc(arg);
}
if (
typeof forcedFunc === "object" &&
!Array.isArray(forcedFunc) &&
forcedFunc !== null &&
"__functor" in forcedFunc
) {
const functor = forceFunction(forcedFunc.__functor);
return forceFunction(functor(forcedFunc))(arg);
}
throw new Error(`attempt to call something which is not a function but ${typeName(forcedFunc)}`);
};

View File

@@ -5,7 +5,15 @@
*/ */
import { createThunk, force, isThunk, IS_THUNK } from "./thunk"; import { createThunk, force, isThunk, IS_THUNK } from "./thunk";
import { select, selectWithDefault, validateParams, resolvePath, hasAttr, concatStringsWithContext } from "./helpers"; import {
select,
selectWithDefault,
validateParams,
resolvePath,
hasAttr,
concatStringsWithContext,
call,
} from "./helpers";
import { op } from "./operators"; import { op } from "./operators";
import { builtins, PRIMOP_METADATA } from "./builtins"; import { builtins, PRIMOP_METADATA } from "./builtins";
import { coerceToString, StringCoercionMode } from "./builtins/conversion"; import { coerceToString, StringCoercionMode } from "./builtins/conversion";
@@ -23,6 +31,7 @@ export const Nix = {
IS_THUNK, IS_THUNK,
HAS_CONTEXT, HAS_CONTEXT,
call,
hasAttr, hasAttr,
select, select,
selectWithDefault, selectWithDefault,

View File

@@ -3,12 +3,21 @@
* 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, NixString } from "./types"; import type {
NixValue,
NixList,
NixAttrs,
NixFunction,
NixInt,
NixFloat,
NixNumber,
NixString,
} from "./types";
import { isStringWithContext } from "./types"; import { isStringWithContext } from "./types";
import { force } from "./thunk"; import { force } from "./thunk";
import { getStringValue } from "./string-context"; import { getStringValue } from "./string-context";
const typeName = (value: NixValue): string => { export const typeName = (value: NixValue): string => {
const val = force(value); const val = force(value);
if (typeof val === "bigint") return "int"; if (typeof val === "bigint") return "int";

View File

@@ -49,17 +49,13 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
Ir::Func(x) => x.compile(ctx), Ir::Func(x) => x.compile(ctx),
Ir::AttrSet(x) => x.compile(ctx), Ir::AttrSet(x) => x.compile(ctx),
Ir::List(x) => x.compile(ctx), Ir::List(x) => x.compile(ctx),
&Ir::Call(Call { func, arg }) => { Ir::Call(x) => x.compile(ctx),
let func = ctx.get_ir(func).compile(ctx);
let arg = ctx.get_ir(arg).compile(ctx);
format!("Nix.force({func})({arg})")
}
Ir::Arg(x) => format!("arg{}", x.0), Ir::Arg(x) => format!("arg{}", x.0),
Ir::Let(x) => x.compile(ctx), Ir::Let(x) => x.compile(ctx),
Ir::Select(x) => x.compile(ctx), Ir::Select(x) => x.compile(ctx),
&Ir::Thunk(expr_id) => { &Ir::Thunk(expr_id) => {
let inner = ctx.get_ir(expr_id).compile(ctx); let inner = ctx.get_ir(expr_id).compile(ctx);
format!("Nix.createThunk(()=>({}))", inner) format!("Nix.createThunk(()=>({}),\"expr{}\")", inner, expr_id.0)
} }
&Ir::ExprRef(expr_id) => { &Ir::ExprRef(expr_id) => {
format!("expr{}", expr_id.0) format!("expr{}", expr_id.0)
@@ -171,6 +167,14 @@ impl Func {
} }
} }
impl<Ctx: CodegenContext> Compile<Ctx> for Call {
fn compile(&self, ctx: &Ctx) -> String {
let func = ctx.get_ir(self.func).compile(ctx);
let arg = ctx.get_ir(self.arg).compile(ctx);
format!("Nix.call({func}, {arg})")
}
}
impl<Ctx: CodegenContext> Compile<Ctx> for Let { impl<Ctx: CodegenContext> Compile<Ctx> for Let {
fn compile(&self, ctx: &Ctx) -> String { fn compile(&self, ctx: &Ctx) -> String {
let info = &self.binding_sccs; let info = &self.binding_sccs;