/** * Callable Types * * Unified representation for all callable values in Rill: * - ScriptCallable: Closures parsed from Rill source code * - RuntimeCallable: Rill's built-in functions (type, log, json, identity) * - ApplicationCallable: Host application-provided functions * * Public API for host applications. * * ## Implementation Notes * * [DEVIATION] Excess-argument error context fields * - Spec defines error context as { functionName, paramName, expectedType, actualType } * - Excess arguments instead uses { functionName, expectedCount, actualCount } * - Rationale: Excess arguments is an arity check, not a type check * * [ASSUMPTION] validateDefaultValueType _functionName Parameter * - Parameter accepted but unused (prefixed with _ to satisfy eslint) * - Kept for API consistency with marshalArgs signature */ import type { BodyNode, SourceLocation } from '../../types.js'; import { isDict } from './types/guards.js'; import type { TypeStructure, RillTypeValue, RillValue } from './types/structures.js'; interface RuntimeContextLike { readonly parent?: RuntimeContextLike | undefined; readonly variables: Map; pipeValue: RillValue; readonly metadata?: Record | undefined; readonly hostContext: Record; } /** * Callable function signature. * Used for both host-provided functions and runtime callables. */ export type CallableFn = (args: Record, ctx: RuntimeContextLike, location?: SourceLocation) => RillValue | Promise; /** * Unified parameter definition for all callable types (script closures and host functions). * * - type: undefined means the parameter accepts any type (any-typed). * - defaultValue: undefined means the parameter is required. * - annotations: evaluated key-value pairs; empty object ({}) when no annotations present. * - Description lives at annotations.description — no separate description field. */ export interface RillParam { readonly name: string; readonly type: TypeStructure | undefined; readonly defaultValue: RillValue | undefined; readonly annotations: Record; } /** * Unified host function definition using RillParam for parameter declarations. * * Replaces HostFunctionDefinition. Runtime does NOT validate return values * against returnType at call time. */ export interface RillFunction { readonly params: readonly RillParam[]; readonly fn: CallableFn; readonly annotations?: Record; readonly returnType: RillTypeValue; /** When true, RILL-R003 generic receiver validation is skipped for this method. */ readonly skipReceiverValidation?: boolean; } /** Common fields for all callable types */ interface CallableBase { readonly __type: 'callable'; /** * Property-style callable: auto-invoked when accessed from a dict. * For script callables, $ is bound to the containing dict. * For runtime callables, the dict is passed as first argument. */ readonly isProperty: boolean; readonly params: readonly RillParam[]; readonly annotations: Record; readonly returnType: RillTypeValue; /** Reference to containing dict (set when stored in a dict) */ boundDict?: Record; } /** * Script callable - parsed from Rill source code. * * Carries closure-level annotations captured at creation time. * Per-parameter annotations are accessible via params[i].annotations. */ export interface ScriptCallable extends CallableBase { readonly kind: 'script'; readonly body: BodyNode; /** Reference to the scope where this closure was defined (late binding) */ readonly definingScope: RuntimeContextLike; } /** Runtime callable - Rill's built-in functions (type, log, json, identity) */ export interface RuntimeCallable extends CallableBase { readonly kind: 'runtime'; readonly fn: CallableFn; } /** Application callable - host application-provided functions */ export interface ApplicationCallable extends CallableBase { readonly kind: 'application'; readonly fn: CallableFn; } /** Union of all callable types */ export type RillCallable = ScriptCallable | RuntimeCallable | ApplicationCallable; /** Type guard for any callable (delegates to types/guards.ts) */ export declare const isCallable: (value: RillValue) => value is RillCallable; /** Type guard for script callable */ export declare function isScriptCallable(value: RillValue): value is ScriptCallable; /** Type guard for runtime callable */ export declare function isRuntimeCallable(value: RillValue): value is RuntimeCallable; /** Type guard for application callable */ export declare function isApplicationCallable(value: RillValue): value is ApplicationCallable; export { callable } from './callable-factory.js'; /** * Convert a RillFunction to an ApplicationCallable. * * Validates the input and produces a callable value accepted by the loader. * Pure function with no side effects. * * @param def - Host function definition to convert * @returns ApplicationCallable with __type, kind, isProperty, and preserved annotations */ export declare function toCallable(def: RillFunction, isProperty?: boolean): ApplicationCallable; export { isDict }; /** * Deep equality for script callables. * Compares params, declared return type, body AST structure, defining scope, * and annotations. * * Two closures are equal if: * 1. Same parameter names, types, default values, and annotations * 2. Structurally identical declared return type (`:T` suffix) * 3. Structurally identical body AST (ignoring source locations) * 4. Same defining scope (reference equality) * 5. Same closure-level annotations * 6. Same parameter-level annotations */ export declare function callableEquals(a: ScriptCallable, b: ScriptCallable, valueEquals?: (a: RillValue, b: RillValue) => boolean): boolean; /** * Build a TypeStructure closure variant from a closure's parameter list. * * Called at closure creation time to build the structural type for `$fn.^input`. * - Typed params use param.type directly when present * - Untyped params (type: undefined) map to { kind: 'any' } * - Return type is always { kind: 'any' } * * Validate defaultValue type matches declared parameter type. * * Called at registration time to catch configuration errors early. * Throws Error (not RuntimeError) to indicate registration failure. * * @param param - Parameter with defaultValue to validate * @param _functionName - Function name (unused, kept for API consistency) * @throws Error if defaultValue type doesn't match param.type */ export declare function validateDefaultValueType(param: RillParam, _functionName: string): void; /** * Options for marshalArgs error reporting. */ export interface MarshalOptions { /** Function name included in error messages */ readonly functionName: string; /** Source location for error reporting */ readonly location: SourceLocation | undefined; } /** * Info passed to `HydrationPolicy.onMissingField` when a declared dict/ordered * field or tuple element has no value, no default, and is not itself a * collection type that can be synthesized empty. */ export interface HydrationMissingFieldInfo { /** Which structural kind the missing field belongs to. */ readonly kind: 'dict' | 'ordered' | 'tuple'; /** Runtime shape of the value being hydrated (e.g. 'dict', 'ordered', 'tuple'). */ readonly source: string; /** Structural kind being hydrated into; mirrors `kind` as a string. */ readonly target: string; /** Field name (dict/ordered) or stringified index (tuple). */ readonly fieldName: string; /** Element index, set only when `kind === 'tuple'`. */ readonly position: number | undefined; } /** * Behavior knobs that let a single structural walker serve two independent * callers whose missing-field and extras handling were never meant to * diverge, but drifted because each caller carried its own copy of the walk. * * - `onMissingField` never lets the walker throw directly: marshaling * (host-application argument binding) leaves the field absent and lets * Stage 3 type-check report RILL-R001; `-> type` conversion throws * RILL-R044 immediately, since a structural conversion has no later * type-check stage to fall back on. * - `keepExtras` controls whether keys/elements not declared on the target * type survive in the result (marshaling: yes, values must remain * structurally compatible with the wider caller-supplied shape) or are * dropped (conversion: yes, `-> type` narrows to exactly the declared * shape). * - `coerceOrderedFromDict` controls whether a plain dict value is accepted * as a source for an `ordered`-typed field (conversion allows dict -> * ordered per the compatibility matrix; marshaling never coerces shape, * only fills defaults on an already-ordered value). */ export interface HydrationPolicy { readonly onMissingField: (info: HydrationMissingFieldInfo) => void; readonly keepExtras: boolean; readonly coerceOrderedFromDict: boolean; } /** * Recursively walk a value against a dict/ordered/tuple TypeStructure, * filling in field-level defaults and empty collections, per a * HydrationPolicy. Returns the value unchanged when the type has no * fields/elements or the value's runtime shape does not match. * * Shared by `hydrateFieldDefaults` (argument marshaling) and conversion's * nested-field hydration (`-> type` structural conversion). See * HydrationPolicy for why these two callers need distinct behavior. * * Pure function: no class context, no evaluator, no side effects beyond * invoking `policy.onMissingField`. */ export declare function hydrateStructure(value: RillValue, type: TypeStructure, policy: HydrationPolicy): RillValue; /** * Hydrate missing dict/ordered field-level defaults into a value. * * When a param has type `dict(a: string = "x", b: number)` and the caller * passes `[b: 2]`, this fills in `a` with its default `"x"`. Fields without * defaults are left absent so Stage 3 catches them with RILL-R001. * * Pure function: no class context, no evaluator, no side effects. */ export declare function hydrateFieldDefaults(value: RillValue, type: TypeStructure): RillValue; /** * Unified marshaling entry point for all 3 invocation paths. * * Builds a named argument map from positional args, hydrates defaults, * type-checks each field, and returns a Record. * * Stages: * 1. Excess args check (RILL-R045) * 2. Default hydration + missing required check (RILL-R044) * 2.5. Dict/ordered field-level default hydration * 3. Type check per field (RILL-R001) * * Preconditions (enforced by caller): * - args contains already-evaluated RillValue[] * - pipe value already inserted as first element by caller * - boundDict already prepended as first element by caller * - params is defined (caller skips marshalArgs for untyped callables) * * @param args - Positional arguments (already evaluated) * @param params - Parameter definitions * @param options - Error context: functionName and location * @returns Named argument map keyed by param name */ export declare function marshalArgs(args: RillValue[], params: readonly RillParam[], options?: MarshalOptions): Record; /** * Validates a raw JavaScript value returned by a host function, ensuring it * is representable in the rill value model before it flows further through * the runtime. * * Deep-walks arrays and plain objects, tracking the current recursion * ancestor path (added before descending into children, removed after) * to reject cycles without flagging a diamond-shaped but acyclic * structure (the same nested object reachable from two sibling fields). * Any recognized rill value brand (atom, tuple, vector, ordered value, * type value, datetime, duration, callable, stream, iterator, field * descriptor) stops descent at that node. Anything else that cannot be * represented -- undefined, null, symbol, bigint, a raw function, Date, * Map, Set, a cyclic reference, or any other non-plain class instance -- * throws a fatal RuntimeError. This is a host-contract violation, not a * catchable script error. * * @param result - The raw value returned by the host function * @param functionName - Name of the host function, for the error message * @param location - Call-site location, for error reporting */ export declare function validateHostResult(result: unknown, functionName: string, location?: SourceLocation): void;