import { ArrayType, AstType, BooleanType, DictType, EastFunction, EastType, IntegerType, NonNullable, Nullable, SetType, StringType, StructType, SubType, Variable, VariantType } from '../east'; import { FunctionStatement } from './FunctionTaskDescription'; /** * A `Procedure` to build modular logic in Elara. * * A procedure is a routine (or function) that can be called from within a {@link FunctionBuilder} or {@link ProcessBuilder}. * It is useful for factoring your code and repeating repetition. * * Methods on the `Procedure` enable you to directly transform data from any number of named `input` parameters * and `return` a single computed value. The `body` of the function works similarly to most imperative * languages (like JavaScript/TypeScript) allowing for definition of temporary variables (with `let`), reassignment * of defined variables (with `assign`), branching (with `if`, `ifNull` and `match`), loops (with `while`, `forArray`, `forSet`, `forDict`), * as well as logging and raising errors (with `log`, `warn` and `error`). Inside any of these statements you can use * East {@link Expression}s to access and manipulate data in scope. * * @category Procedure * * @example * ```typescript * // create a procedure to multiply two numbers * const my_procedure = new Procedure("my_procedure") * .input("x", FloatType) * .input("y", FloatType) * .output(FloatType) * .body(b => b * .return(vars => Multiply(vars.x, vars.y)) * ); * * // create the predict_amount function * const x = new SourceBuilder("x").value({ value: 3.14 }); * const y = new SourceBuilder("y").value({ value: 42.0 }); * * const f = new FunctionBuilder("f") * .input("x", x.outputStream()) * .input("y", y.outputStream()) * .procedure(my_procedure) * .body(block => block * .let("output", (vars, procs) => procs.my_procedure(Struct({ x: vars.x, y: vars.y }))) * .return({ output: vars => vars.output }) * ); * ``` */ export declare class Procedure = {}> { name: Name; private inputs; /** * Create a `Procedure` containing modular logic in Elara. * * A procedure is a routine (or function) that can be called from within a {@link FunctionBuilder} or {@link ProcessBuilder}. * It is useful for factoring your code and repeating repetition. * * Methods on the `Procedure` enable you to directly transform data from any number of named `input` parameters * and `return` a single computed value. The `body` of the function works similarly to most imperative * languages (like JavaScript/TypeScript) allowing for definition of temporary variables (with `let`), reassignment * of defined variables (with `assign`), branching (with `if`, `ifNull` and `match`), loops (with `while`, `forArray`, `forSet`, `forDict`), * as well as logging and raising errors (with `log`, `warn` and `error`). Inside any of these statements you can use * East {@link Expression}s to access and manipulate data in scope. * * @category Procedure * * @example * ```typescript * // create a procedure to multiply two numbers * const my_procedure = new Procedure("my_procedure") * .input("x", FloatType) * .input("y", FloatType) * .output(FloatType) * .body(b => b * .return(vars => Multiply(vars.x, vars.y)) * ); * * // create the predict_amount function * const x = new SourceBuilder("x").value({ value: 3.14 }); * const y = new SourceBuilder("y").value({ value: 42.0 }); * * const f = new FunctionBuilder("f") * .input("x", x.outputStream()) * .input("y", y.outputStream()) * .procedure(my_procedure) * .body(block => block * .let("output", (vars, procs) => procs.my_procedure(Struct({ x: vars.x, y: vars.y }))) * .return({ output: vars => vars.output }) * ); * ``` */ constructor(name: Name); /** @internal */ constructor(name: Name, inputs: Record); /** * Define an input parameter to a {@link Procedure}. * * @param name the name of the input parameter * @param type the {@link EastType} of the input parameter * * @category Procedure * * @example * ```typescript * // create a procedure to multiply two numbers * const my_procedure = new Procedure("my_procedure") * .input("x", FloatType) * .input("y", FloatType) * .output(FloatType) * .body(b => b * .return(vars => Multiply(vars.x, vars.y)) * ); * ``` */ input(name: InputName, type: T): Procedure; /** * Define the type of the output of a {@link Procedure}. * * @param type the {@link EastType} of the output value * * @category Procedure * * @example * ```typescript * // create a procedure to multiply two numbers * const my_procedure = new Procedure("my_procedure") * .input("x", FloatType) * .input("y", FloatType) * .output(FloatType) * .body(b => b * .return(vars => Multiply(vars.x, vars.y)) * ); * ``` */ output(type: T): ProcedureBodyFinalizer; } export declare class ProcedureBodyFinalizer, Output extends EastType, Procs extends Record> { name: Name; inputs: Inputs; output: Output; procedures: Procs; /** @internal */ constructor(name: Name, inputs: Inputs, output: Output, procedures: Procs); /** * Import another procedure that can be called from this {@link Procedure}. * * Note that procedures can be called recursively, and mutual recursion is supported. * * @param procedure the {@link EastType} of the output value * * @category Procedure */ procedure, EastType, Record> | ProcedureFinalizer, Record, EastType>>(procedure: Proc): ProcedureBodyFinalizer; output: Proc["output"]; }; }>; /** * Define the body of a {@link Procedure}, containing the logic to perform in order to execute the procedure. * * @param body a builder that you can attach statements * * @category Procedure * * @example * ```typescript * // create a procedure to multiply two numbers * const my_procedure = new Procedure("my_procedure") * .input("x", FloatType) * .input("y", FloatType) * .output(FloatType) * .body(b => b * .return(vars => Multiply(vars.x, vars.y)) * ); * ``` */ body; }, Procs & { [K in Name]: { inputs: StructType; output: Output; }; }, Output>) => TerminalBlockBuilder>(body: B): ProcedureFinalizer; } export declare class ProcedureFinalizer, Procs extends Record, Output extends EastType> { name: Name; inputs: Inputs; procedures: Procs; output: Output; statements: FunctionStatement[]; /** @internal */ constructor(name: Name, inputs: Inputs, procedures: Procs, output: Output, statements: FunctionStatement[]); } /** * A block of code, such as the "body" of a procedure, consisting of a series of statements like `let` and `return`, * or even nested blocks of code (using `if` or `while`). The block of code is executed much like that * in typical imperative languages, like JavaScript/TypeScript. * * Generally an empty block of code is injected on which you can use define * statements using the builder pattern (or fluent code pattern) and return the result. * * @category Procedure * * @example * ```typescript * // create a procedure to multiply two numbers * const my_procedure = new Procedure("my_procedure") * .input("x", FloatType) * .input("y", FloatType) * .output(FloatType) * .body(b => b * .return(vars => Multiply(vars.x, vars.y)) * ); * ``` **/ declare class BlockBuilder = Record, Procs extends Record = {}, Output extends EastType = EastType> { protected vars: Vars; protected procedures: Procs; protected statements: FunctionStatement[]; protected output: Output; protected n_loops: number; protected label: string | null; terminal: false; /** @internal */ constructor(vars: Vars, procedures: Procs, statements: FunctionStatement[], output: Output, n_loops: number, label: string | null); /** * Define a new variable in the function and assign a value equal to the result of an East {@link Expression}. * * @category Procedure * * @example * ```typescript * // Create a function to add two integer streams, equivalent to the Javascript function * // * // function f(a, b) { * // let c = a + b; * // return { c }; * // } * // * // f(1n, 2n) == 3n * * const a = new SourceBuilder("a").value({ value: 1n }) * const b = new SourceBuilder("b").value({ value: 2n }) * * const f = new FunctionBuilder("f") * .input("a", a.outputStream()) * .input("b", b.outputStream()) * .body(block => block * .let("c", vars => Add(vars.a, vars.b)) * .return({ c: vars => vars.c }) * ) * ``` **/ let) => EastFunction); }) => EastFunction>(name: Name, value: F): BlockBuilder["type"]>; }, Procs, Output>; /** * Reassign the value of an existing variable in scope to the result of an East {@link Expression}. * The new value will be visible in later code. * * @category Procedure * * @example * ```typescript * // Create a function to sum up elements of an integer array, equivalent to the Javascript function: * // * // function sum(array) { * // let ret = 0n; * // for (const x of array) { * // ret = ret + x; * // } * // return { ret }; * // } * // * // sum([1n, 2n]) == 3n * * const array = new SourceBuilder("array").value({ value: [1n, 2n] }) * * const sum = new FunctionBuilder("sum") * .input("array", array.outputStream()) * .body(block => block * .let("ret", vars => Const(0n)) * .forArray( * vars => vars.array, * (for_block, x) => for_block * .assign("ret", vars => Add(vars.ret, x)) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ assign) => EastFunction); }) => { type: SubType; ast_type: AstType; }>(name: Name, value: F): BlockBuilder; /** * Insert a new element into an array, set or dictionary. The collection will be mutated in place. * * For arrays, you can insert the `"first"` or `"last"` element (i.e. push to the front or end of the array). * For sets, you can insert a new key. If the key already exists an error will result. * For dictionaries, you can insert a new key and associated value. If the key already exists an error will result (see also {@link update}). * * @category Procedure * * @example * ```typescript * // Create a function to insert a new element at the end of an array: * // * // function pushLast(array, x) { * // array.push(x) * // return { array }; * // } * // * // pushLast([1n, 2n], 3n) == [1n, 2n, 3n] * * const array = new SourceBuilder("array").value({ value: [1n, 2n] }) * const x = new SourceBuilder("x").value({ value: 3n }) * * const pushLast = new FunctionBuilder("pushLast") * .input("array", array.outputStream()) * .input("x", x.outputStream()) * .body(block => block * .insert( * vars => vars.array, * "last", * vars => vars.x, * ) * .return({ array: vars => vars.array }) * ) * ``` **/ insert) => EastFunction); }) => EastFunction>(collection: C, key: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction["type"]["value"]["key"]>, value: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => { type: SubType["type"]["value"]["value"]>; ast_type: AstType; }): BlockBuilder; insert) => EastFunction); }) => EastFunction>(collection: C, key: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction["type"]["value"]>): BlockBuilder; insert) => EastFunction); }) => EastFunction>(collection: C, key: 'first' | 'last', value: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => { type: SubType["type"]["value"]>; ast_type: AstType; }): BlockBuilder; /** * Update an existing value in an array or dictionary. The collection will be mutated in place. * * For arrays, you can update any element by integer index (the first element has index `0n`). If the index is out of bounds an error will result. * For dictionaries, you can update any existing key to a new associated value. If the key does not exist an error will result (see also {@link `insert`}). * In both cases, you are given access the existing value when computing the new value. * * @category Procedure * * @example * ```typescript * // Create a function to increment the first element of an array * // * // function incrementFirst(array) { * // array[0] += 1n; * // return { array }; * // } * // * // incrementFirst([1n, 2n, 3n]) == [2n, 2n, 3n] * * const array = new SourceBuilder("array").value({ value: [1n, 2n, 3n] }) * * const incrementFirst = new FunctionBuilder("incrementFirst") * .input("array", array.outputStream()) * .body(block => block * .update( * vars => vars.array, * vars => Const(0n), * (vars, old_value) => Add(old_value, 1n), * ) * .return({ array: vars => vars.array }) * ) * ``` **/ update) => EastFunction); }) => EastFunction>(collection: C, key: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction["type"]["value"]["key"]>, value: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }, old_value: Variable["type"]["value"]["value"]>) => { type: SubType["type"]["value"]["value"]>; ast_type: AstType; }): BlockBuilder; update) => EastFunction); }) => EastFunction>(collection: C, key: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction, value: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }, old_value: Variable["type"]["value"]>) => { type: SubType["type"]["value"]>; ast_type: AstType; }): BlockBuilder; /** * Delete an existing element from an array, set or dictionary. The collection will be mutated in place. * * For arrays, you can delete the `"first"` or `"last"` element (i.e. pop the front or end of the array). * For sets, you can delete an existing key. If the key doesn't exist an error will result. * For dictionaries, you can delete an existing key (and associated value). If the key doesn't exist an error will result. * * @category Procedure * * @example * ```typescript * // Create a function to delete and return the first element of an array: * // * // function popFirst(array, x) { * // const ret = array[0]; * // array.shift(x); * // return { ret, array }; * // } * // * // pop([1n, 2n, 3n]) == { ret: 1n, array: [2n, 3n] } * * const array = new SourceBuilder("array").value({ value: [1n, 2n, 3n] }) * * const popFirst = new FunctionBuilder("popFirst") * .input("array", array.outputStream()) * .body(block => block * .let("ret", vars => Get(vars.array, 0n)) * .delete( * vars => vars.array, * "first", * ) * .return({ ret: vars => vars.ret, array: vars => vars.array }) * ) * ``` **/ delete) => EastFunction); }) => EastFunction>(collection: C, key: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction["type"]["value"]["key"]>): BlockBuilder; delete) => EastFunction); }) => EastFunction>(collection: C, key: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction["type"]["value"]>): BlockBuilder; delete) => EastFunction); }) => EastFunction>(collection: C, key: 'first' | 'last'): BlockBuilder; /** * Delete all elements from an array, set or dictionary. The collection will be mutated in place. * * @category Procedure * * @example * ```typescript * // Create a function to delete and return the first element of an array: * // * // function clear(array) { * // array.len = 0; * // return { array }; * // } * // * // clear([1n, 2n, 3n]) == { array: [] } * * const array = new SourceBuilder("array").value({ value: [1n, 2n, 3n] }) * * const clear = new FunctionBuilder("clear") * .input("array", array.outputStream()) * .body(block => block * .clear(vars => vars.array) * .return({ array: vars => vars.array }) * ) * ``` **/ clear(collection: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction): BlockBuilder; /** * Evaluate a Boolean {@link Expression} and conditionally execute blocks of code depending * on whether the result is `true` or `false`. * * This method creates function block "builders" which you can add statements to. Defining a block for the `false` branch is optional. * * @category Procedure * * @example * ```typescript * // Create a function to take the square root of a floating-point number, checking that it is positive, equivalent to the following Javascript code: * // * // function sqrt(x) { * // if (x < 0) { * // throw new Error(`Input cannot be negative, got ${x}`); * // } else { * // return { ret: Math.sqrt(x) }; * // } * // } * // * // sqrt(9.0) == 3.0 * * const x = new SourceBuilder("x").value({ value: 9.0 }) * * const sqrt = new FunctionBuilder("sqrt") * .input("x", x.outputStream()) * .body(block => block * .if( * vars => Less(vars.x, 0.0), * true_block => true_block * .error(vars => StringJoin`Input cannot be negative, got ${vars.x}`) * false_block => false_block * .return({ ret: vars => Sqrt(vars.x) }) * ) * ) * ``` **/ if) => TerminalBlockBuilder, F extends (block: BlockBuilder) => TerminalBlockBuilder>(predicate: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction, true_block: T, false_block: F): TerminalBlockBuilder; if) => BlockBuilder | TerminalBlockBuilder, F extends (block: BlockBuilder) => BlockBuilder | TerminalBlockBuilder>(predicate: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction, true_block: T, false_block: F): BlockBuilder; if) => BlockBuilder | TerminalBlockBuilder>(predicate: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction, true_block: T): BlockBuilder; /** * Evaluate a nullable {@link Expression} and conditionally execute blocks of code depending * on whether the result is `null` or otherwise. * * This method creates function block "builders" which you can add statements to. Defining a block for the non-null branch is optional. In this branch the "unwrapped" value (with a non-nullable East type) is provided for use inside the block, which is useful for satisfying type constraints. * * @category Procedure * * @example * ```typescript * // Create a function to take the square root of a (nullable) floating-point number, checking that it is not `null`, equivalent to the following Javascript code: * // * // function sqrt(x) { * // if (x === null) { * // throw new Error(`Input cannot be null`); * // } else { * // return { ret: Math.sqrt(x) }; * // } * // } * // * // sqrt(9.0) == 3.0 * * const x = new SourceBuilder("x").value({ value: 9.0, type: Nullable(FloatType) }) * * const sqrt = new FunctionBuilder("sqrt") * .input("x", x.outputStream()) * .body(block => block * .ifNull( * vars => vars.x, * null_block => null_block * .error(vars => StringJoin`Input cannot be negative, got ${vars.x}`) * (non_null_block, non_null_x) => non_null_block * .return({ ret: vars => Sqrt(non_null_x) }) * ) * ) * ``` **/ ifNull) => EastFunction); }) => EastFunction, T extends (block: BlockBuilder) => TerminalBlockBuilder, F extends (block: BlockBuilder, value: Variable["type"]>>) => TerminalBlockBuilder>(input: I, true_block: T, false_block: F): TerminalBlockBuilder; ifNull) => EastFunction); }) => EastFunction, T extends (block: BlockBuilder) => BlockBuilder | TerminalBlockBuilder, F extends (block: BlockBuilder, value: Variable["type"]>>) => BlockBuilder | TerminalBlockBuilder>(input: I, true_block: T, false_block: F): BlockBuilder; ifNull) => BlockBuilder | TerminalBlockBuilder>(input: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction, true_block: T): BlockBuilder; /** * Evaluate a variant {@link Expression} and conditionally execute blocks of code depending on the variant case/tag.. * * This method creates function block "builders" which you can add statements to. Defining a block for every case/tag is optional. The "unwrapped" associated variant data (with the "unwrapped" East type for that case/tag) is provided for use inside the block. This one of the primary ways to access the associated data within a variant (along with the `Match` expression). * * @category Procedure * * @example * ```typescript * // Create a function to take the square root of an "optional" floating-point variant, checking that it is not `None`, equivalent to the following Javascript code: * // * // function sqrt(x) { * // match(x, { * // None: () => { * // throw new Error(`Input cannot be .None`); * // }, * // Some: { * // unwrapped_x => return { ret: Math.sqrt(unwrapped_x) }; * // }, * // }) * // } * // * // sqrt(some(9.0)) == 3.0 * * const x = new SourceBuilder("x").value({ value: some(9.0), type: OptionType(FloatType) }) * * const sqrt = new FunctionBuilder("sqrt") * .input("x", x.outputStream()) * .body(block => block * .match( * vars => vars.x, * { * None: block => block * .error("Input cannot be .None"), * Some: (block, unwrapped_x) => block * .return({ ret: Sqrt(unwrapped_x) }) * } * ) * ) * ``` **/ match) => EastFunction); }) => EastFunction, T extends { [K in keyof ReturnType["type"]["value"]]: (block: BlockBuilder, data: Variable["type"]["value"][K]>) => TerminalBlockBuilder; }>(variant: V, cases: T): TerminalBlockBuilder; match) => EastFunction); }) => EastFunction, T extends { [K in keyof ReturnType["type"]["value"]]?: (block: BlockBuilder, data: Variable["type"]["value"][K]>) => BlockBuilder | TerminalBlockBuilder; }>(variant: V, cases: T): BlockBuilder; /** * Loop over a block of code while a provided Boolean expression evaluates to `true`. The loop when terminate when the expression returns `false`. * * Care must be taken to avoid infinite loops, or the function task may run indefinitely (using up resources on the Elara cluster). * * @category Procedure * * @example * ```typescript * // Create a function to sum up elements of an integer array, equivalent to the Javascript function: * // * // function sum(array) { * // let ret = 0n; * // let i = 0; * // while (i < array.length) { * // ret = ret + x; * // i = i + 1; * // } * // return { ret }; * // } * // * // sum([1n, 2n]) == 3n * * const array = new SourceBuilder("array").value({ value: [1n, 2n] }) * * const sum = new FunctionBuilder("sum") * .input("array", array.outputStream()) * .body(block => block * .let("ret", vars => Const(0n)) * .let("i", vars => Const(0n)) * .while( * vars => Less(vars.i, Size(vars.array)), * while_block => while_block * .assign("ret", vars => Add(vars.ret, x)) * .assign("i", vars => Add(vars.i, 1n)) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ while) => BlockBuilder | TerminalBlockBuilder>(predicate: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction, block: B): BlockBuilder; /** * Loop over all entries in an array, providing the value (and optionally the array index, starting at `0n`) to a block of code to execute. * * @category Procedure * * @example * ```typescript * // Create a function to sum up elements of an integer array, equivalent to the Javascript function * // * // function sum(array) { * // let ret = 0n; * // for (let x of array) { * // ret = ret + x; * // } * // return { ret }; * // } * // * // sum([1n, 2n]) == 3n * * const array = new SourceBuilder("array").value({ value: [1n, 2n] }) * * const sum = new FunctionBuilder("sum") * .input("array", array.outputStream()) * .body(block => block * .let("ret", vars => Const(0n)) * .forArray( * vars => vars.array, * (for_block, x, index) => for_block * .assign("ret", vars => Add(vars.ret, x)) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ forArray) => EastFunction); }) => EastFunction, B extends (block: BlockBuilder, value: Variable["type"]["value"]>, key: Variable) => BlockBuilder | TerminalBlockBuilder>(collection: C, block: B): BlockBuilder; /** * Loop over all entries in a dictionary, providing the value (and optionally the dictionary key) to a block of code to execute. * * Note that the iteration order is sorted by key value. * * @category Procedure * * @example * ```typescript * // Create a function to sum up elements of a dictionary of integer values, equivalent to the Javascript function: * // * // function sumDict(dict) { * // let ret = 0n; * // for (let [key, value] of dict) { * // ret = ret + value; * // } * // return { ret }; * // } * // * // sumDict(new Map([["a", 1n], ["b", 2n]]) == 3n * * const dict = new SourceBuilder("dict").value({ new Map([["a", 1n], ["b", 2n]]) }) * * const sum = new FunctionBuilder("sum") * .input("dict", dict.outputStream()) * .body(block => block * .let("ret", vars => Const(0n)) * .forDict( * vars => vars.array, * (for_block, value, key) => for_block * .assign("ret", vars => Add(vars.ret, value)) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ forDict) => EastFunction); }) => EastFunction, B extends (block: BlockBuilder, value: Variable["type"]["value"]["value"]>, key: Variable["type"]["value"]["key"]>) => BlockBuilder | TerminalBlockBuilder>(collection: C, block: B): BlockBuilder; /** * Loop over all keys in a set, providing the key to a block of code to execute. * * Note that the iteration order is sorted by key value. * * @category Procedure * * @example * ```typescript * // Create a function to log all the elements of a set, equivalent to the Javascript function * // * // function logAll(set) { * // for (let x of set) { * // console.log(`Set contains: ${x}`); * // } * // return { }; * // } * // * // logAll(new Set(["a", "b"])); * * const set = new SourceBuilder("set").value({ value: new Set(["a", "b"]) }) * * const logAll = new FunctionBuilder("logAll") * .input("set", set.outputStream()) * .body(block => block * .forSet( * vars => vars.set, * (for_block, x) => for_block * .log(vars => StringJoin`Set contains: ${x}`) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ forSet) => EastFunction); }) => EastFunction, B extends (block: BlockBuilder, key: Variable["type"]["value"]>) => BlockBuilder | TerminalBlockBuilder>(collection: C, block: B): BlockBuilder; /** * Continue from the beginning of the next iteration of a `while`, `forArray`, `forSet` or `forDict` loop. * * This method expects the `BlockBuilder` for the loop you wish to continue. Note that it is possible to continue from within nested loops. * * @category Procedure * * @example * ```typescript * // Create a function to sum up the positive elements of an integer array, equivalent to the Javascript function * // * // function sum(array) { * // let ret = 0n; * // for (let x of array) { * // if (x < 0) { * // continue; * // } * // ret = ret + x; * // } * // return { ret }; * // } * // * // sum([1n, 2n]) == 3n * * const array = new SourceBuilder("array").value({ value: [1n, 2n] }) * * const sum = new FunctionBuilder("sum") * .input("array", array.outputStream()) * .body(block => block * .let("ret", vars => Const(0n)) * .forArray( * vars => vars.array, * (for_block, x, index) => for_block * .if( * vars => Less(vars.x, 0n), * block => block * .continue(for_block) * ) * .assign("ret", vars => Add(vars.ret, x)) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ continue(block: BlockBuilder): BlockBuilder; /** * Break from a `while`, `forArray`, `forSet` or `forDict` loop. * * This method expects the `BlockBuilder` for the loop you wish to terminate. Note that it is possible to break from within nested loops. * * @category Procedure * * @example * ```typescript * // Create a function to sum up elements of an integer array, equivalent to the Javascript function: * // * // function sum(array) { * // let ret = 0n; * // let i = 0; * // while (true) { * // ret = ret + x; * // i = i + 1; * // if (i >= array.length) { * // break; * // } * // } * // return { ret }; * // } * // * // sum([1n, 2n]) == 3n * * const array = new SourceBuilder("array").value({ value: [1n, 2n] }) * * const sum = new FunctionBuilder("sum") * .input("array", array.outputStream()) * .body(block => block * .let("ret", vars => Const(0n)) * .let("i", vars => Const(0n)) * .while( * vars => Const(true), * while_block => while_block * .assign("ret", vars => Add(vars.ret, x)) * .assign("i", vars => Add(vars.i, 1n)) * .if( * vars => GreaterEqual(vars.i, Size(vars.array)), * block => block * .break(while_block) * ) * ) * .return({ ret: vars => vars.ret }) * ) * ``` **/ break(block: BlockBuilder): BlockBuilder; /** * Produce a log message that will be attached to the executing function task. * * @category Procedure * * @example * ```typescript * // Create a function to take the square root of a floating-point number, checking that it is positive, equivalent to the following Javascript code: * // * // function sqrt(x) { * // if (x < 0) { * // console.log(`Input cannot be negative, got ${x}`); * // } * // return { ret: Math.sqrt(x) }; * // } * // * // sqrt(9.0) == 3.0 * * const x = new SourceBuilder("x").value({ value: 9.0 }) * * const sqrt = new FunctionBuilder("sqrt") * .input("x", x.outputStream()) * .body(block => block * .if( * vars => Less(vars.x, 0.0), * true_block => true_block * .log(vars => StringJoin`Input cannot be negative, got ${vars.x}`) * ) * .return({ ret: vars => Sqrt(vars.x) }) * ) * ``` **/ log(message: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction): BlockBuilder; /** * Produce a warning that will be attached to the executing function task along with a message. The task will emit the messsage and continue executing. * * @category Procedure * * @example * ```typescript * // Create a function to take the square root of a floating-point number, checking that it is positive, equivalent to the following Javascript code: * // * // function sqrt(x) { * // if (x < 0) { * // console.warn(`Input cannot be negative, got ${x}`); * // } * // return { ret: Math.sqrt(x) }; * // } * // * // sqrt(9.0) == 3.0 * * const x = new SourceBuilder("x").value({ value: 9.0 }) * * const sqrt = new FunctionBuilder("sqrt") * .input("x", x.outputStream()) * .body(block => block * .if( * vars => Less(vars.x, 0.0), * true_block => true_block * .warn(vars => StringJoin`Input cannot be negative, got ${vars.x}`) * ) * .return({ ret: vars => Sqrt(vars.x) }) * ) * ``` **/ warn(message: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction): BlockBuilder; /** * If this statement is executed, produce an error that will stop the executing function task from completing along with an error message. * * @category Procedure * * @example * ```typescript * // Create a function to take the square root of a floating-point number, checking that it is positive, equivalent to the following Javascript code: * // * // function sqrt(x) { * // if (x < 0) { * // throw new Error(`Input cannot be negative, got ${x}`); * // } else { * // return { ret: Math.sqrt(x) }; * // } * // } * // * // sqrt(9.0) == 3.0 * * const x = new SourceBuilder("x").value({ value: 9.0 }) * * const sqrt = new FunctionBuilder("sqrt") * .input("x", x.outputStream()) * .body(block => block * .if( * vars => Less(vars.x, 0.0), * true_block => true_block * .error(vars => StringJoin`Input cannot be negative, got ${vars.x}`) * false_block => false_block * .return({ ret: vars => Sqrt(vars.x) }) * ) * ) * ``` **/ error(message: (vars: Vars, procs: { [K in keyof Procs]: ((inputs: EastFunction) => EastFunction); }) => EastFunction): TerminalBlockBuilder; /** * Return a set of results from the function. Each result will populate an individual datastream. * * @category Procedure * * @example * ```typescript * // Create a function to add two integer streams, equivalent to the Javascript function * // * // function f(a, b) { * // let c = a + b; * // return { c }; * // } * // * // f(1n, 2n) == 3n * * const a = new SourceBuilder("a").vaue({ value: 1n }) * const b = new SourceBuilder("b").value({ value: 2n }) * * const f = new FunctionBuilder("f") * .input("a", a.outputStream()) * .input("b", b.outputStream()) * .body(block => block * .let("c", vars => Add(vars.a, vars.b)) * .return({ c: vars => vars.c }) * ) * ``` **/ ["return"]) => EastFunction); }) => { type: SubType; ast_type: AstType; }>(output: F): TerminalBlockBuilder; } /** @internal */ declare class TerminalBlockBuilder { protected statements: FunctionStatement[]; protected output: Output; protected n_loops: number; protected label: string | null; terminal: true; /** @internal */ constructor(statements: FunctionStatement[], output: Output, n_loops: number, label: string | null); } export {};