import { callFn, emptyStackFrame, evaluate, lex, parse } from "../js-lang"; import { Evaluate, FnPrim, Lex, Parse, EvalError, CallFn, EmptyStackFrame, ASTNode, StackFrame, } from "../ts-lang"; export type TransformArgs = { [Idx in keyof Args]: any; }; export type AssertArgs< Args extends readonly ASTNode[], ArgsConstraint extends readonly any[] > = TransformArgs extends ArgsConstraint ? ArgsConstraint : never; export const createFn = () => ( program: Program ): Evaluate>> extends infer E ? E extends [infer ProgramFn extends FnPrim] ? TransformArgs extends ArgsConstraint ? ( ...args: Args ) => CallFn : EvalError<`Program's args do not extend args constraint`> : E extends readonly [ readonly [infer Prim extends FnPrim, infer StackFrame, infer Name] ] ? TransformArgs extends ArgsConstraint ? ( ...args: Args ) => CallFn : EvalError<`Program's args do not extend args constraint`> : EvalError<"Cannot create a function from a program that does not eval to a function"> : never => { const [e] = evaluate(parse(lex(program))) as [ FnPrim | [FnPrim, StackFrame, ASTNode["name"]] ]; const fn: FnPrim = Array.isArray(e) ? e[0] : e; if (!fn.fn) { throw new Error( "Cannot create a function from a program that does not eval to a function" ); } return ((...args: any[]) => callFn(e, args, emptyStackFrame)) as any; };