diff --git a/README.md b/README.md index 3d837d8..12a2453 100644 --- a/README.md +++ b/README.md @@ -76,12 +76,13 @@ failing arm of the result rather than as a throw; `ok` tells the two apart. - Do - [How to compile and evaluate a rule](https://github.com/riddler/predicator-ts/blob/main/docs/guides/how-to-compile-and-evaluate-a-rule.md): compile a rule's text, keep the instruction list, and decide the rule for one loan. - [Rendering a rule back](#rendering-a-rule-back): show an authored rule back to its author in a normalized form. - - [Statement programs](#statement-programs): run a short script that assigns into its context. + - [How to run a statement program](https://github.com/riddler/predicator-ts/blob/main/docs/guides/how-to-run-a-statement-program.md): execute a short script that changes a loan's fields, read back the loan it leaves, and handle a script that fails. - [Writing a location](#writing-a-location): write an assignment's location into your own data without running a program. - [How to add host functions](https://github.com/riddler/predicator-ts/blob/main/docs/guides/how-to-add-host-functions.md): let a rule call a function your application supplies, and handle a call that fails. - Look up - The API reference: every exported function and type, built into `docs/api/` by `mise exec -- pnpm run docs` in a clone of this repository. - [The entry points](#the-entry-points): the two import paths and what each one exports. + - [Statement programs](#statement-programs): what a program's statements can be, what `execute` and `executeValue` answer, and what a failing execution hands back. - [The value domain](#the-value-domain): each value type and its shape in TypeScript. - [Evaluation options](#evaluation-options): the options an evaluation takes, and their defaults. - [Host functions](#host-functions): how a host function's arguments and answer cross into the language's values, and how its name shadows a builtin. diff --git a/docs/guides/how-to-run-a-statement-program.md b/docs/guides/how-to-run-a-statement-program.md new file mode 100644 index 0000000..d1fa8b3 --- /dev/null +++ b/docs/guides/how-to-run-a-statement-program.md @@ -0,0 +1,88 @@ +# How to run a statement program + +This guide shows you how to execute a short script that changes a loan's +fields, and read back the loan it leaves. + +You start with `@riddler/predicator` installed and a renewal script as text: a +loan renewed fewer than twice gets one more renewal and fourteen more days, +and any other loan is marked refused. + +## Compile the script + +1. Import `compileProgram` and pass it the script's text. + + ```ts + import { compileProgram } from "@riddler/predicator"; + + const compiled = compileProgram( + "if loan.renewals < 2 { loan.renewals = loan.renewals + 1; loan.days_left = loan.days_left + 14 } else { loan.status = 'refused' }", + ); + ``` + + `compiled.ok` is `true`. + +2. Check `ok` before you use the result. + + ```ts + if (!compiled.ok) { + throw new Error(compiled.error.message); + } + ``` + + After the check, `compiled.instructions` is the instruction list, starting + `["load", "loan"], ["access", "renewals"], ["lit", 2]`. + +3. If the script does not compile, show the author where it failed. For + `compileProgram("loan.renewals + 1 = 2")`, `compiled.ok` is `false`, + `compiled.error.reason` is `"unassignable_location"` and + `compiled.error.position` is `{ line: 1, column: 19 }`, the `=` whose left + side cannot be written. + +## Execute the script for a loan + +1. Import `execute` and pass it the instruction list and a context holding the + loan. + + ```ts + import { execute } from "@riddler/predicator"; + + const loan = { renewals: 1, days_left: 3 }; + + const renewed = execute(compiled.instructions, { loan }); + ``` + + `renewed` is `{ ok: true, context: { loan: { renewals: 2, days_left: 17 } } }`: + the loan has one more renewal and fourteen more days. + +2. Read the changed loan from `renewed.context`. The `loan` you passed in is + unchanged: `loan.renewals` is still `1`. + +3. Execute it again for a loan already renewed twice. + + ```ts + const refused = execute(compiled.instructions, { + loan: { renewals: 2, days_left: 3 }, + }); + ``` + + `refused` is + `{ ok: true, context: { loan: { renewals: 2, days_left: 3, status: "refused" } } }`. + +4. If you also want a value back, end the script with that expression and call + `executeValue` instead. For the same script followed by `; loan.days_left`, + and the loan from step 1, the result's `value` is `17`. + +## Handle a script that fails + +1. Check `ok` before you read `context`. For a loan with no `days_left`, such + as `{ renewals: 1 }`, `ok` is `false`, `error.type` is + `"TypeMismatchError"` and `error.reason` is `"add"`. + +2. Decide what to keep. The failing result's `context` is + `{ loan: { renewals: 2 } }`, the writes made before the statement that + failed; to keep none of them, keep the loan you passed in. + +For what a program's statements can be, and the options `execute` takes, see +[Statement programs](https://github.com/riddler/predicator-ts/blob/main/README.md#statement-programs) +and +[Evaluation options](https://github.com/riddler/predicator-ts/blob/main/README.md#evaluation-options).