CollectedParameters
interface| Package | @formscript/loader |
|---|---|
| Specification | 4.15, 4.16 |
| Depends on | ast, errors, lexer, parser |
@formscript/loader
Include resolution for §4.15 of the specification, and the parameters of §4.16.
A document may name another document and merge it into itself. This package
does that merge: it reads the documents an include names, splices their
statements into the form that names them, and reports what it could not do.
The merge is a transformation of the tree, not of the text. Everything downstream — the scope, the dependency graph, the output projection, the render intents — reads the tree, so a module that is merged is a module the engine understands like any other part of the form.
Usage
import { resolveIncludes } from '@formscript/loader';
import { nodeResolver } from '@formscript/loader/node';
const { document, diagnostics, files, edges } = resolveIncludes(parsed, {
path: '/forms/loan.forms',
resolver: nodeResolver,
});
resolveIncludes takes a parsed document and returns a new one. The original
is not modified.
What it does
| Rule | What happens |
|---|---|
| Spec-4.47 | the path is resolved relative to the directory of the document that names it |
| Spec-4.48 | the named file's form body is merged; its name, version, namespaces and imports are not |
| Spec-4.49 | an include before the form merges at the start of the body; an include inside the body merges at that point |
| Spec-4.50 | as alias merges the statements into a group named alias, so their paths are alias.name |
| Spec-4.51 | only entity blocks and text blocks are merged; anything else is E-BLOCK-01 |
| Spec-4.52 | a document that includes itself, directly or through others, is E-REF-04 |
| Spec-4.53 | an include inside a block merges the module at the level of that block, so the module's declarations take the path of the scope around them |
| Spec-4.54 | a param declaration names a parameter of the document, at the top level of its form body |
| Spec-4.55 | a parameter's value is the one the including form supplies, or the one it declares |
| Spec-4.56 | with { name = value } supplies values to the parameters of the included document |
| Spec-4.57 | every value of a parameter is a literal, or the name of a parameter of the including document |
| Spec-4.58 | a document's parameters are resolved before the includes it writes are expanded |
| Spec-4.59 | a reference to a parameter is replaced by the parameter's value, and the declarations are removed |
| Spec-4.61 | the name of a parameter is not used by any other declaration or binding in its document |
The alias is a group because that is what an alias has to do: it introduces
a data level, so the module's fields have the paths alias.name, and it opens
a scope, so a reference the module makes to one of its own fields still
resolves to that field.
The parameters
A module may declare parameters, and the form that includes it may give them values:
form address version = "1.0" {
param street_label = "Street"
field street {
type = string
label = street_label
}
}
include "./address.forms" as applicant with {
street_label = "Address of applicant"
}
The value is substituted when the module is merged, so the merged form holds no parameter and is a form like any other: the same fields, the same validity states, the same error codes, the same output document.
Every value is a literal, or the name of a parameter of the including document. A value is written in the including form's scope and lands in the module's scope, and a literal has no scope, so it cannot be captured by a name the module happens to use.
The edges
edges is every include that was resolved: the document that wrote it, the
document it names, the data path of the scope it was written in, and the alias
when it had one. It is what a change-impact tool needs — the path is what says
where the module's declarations land in the form that includes it.
{ from: '/forms/loan.forms', to: '/forms/address.forms', prefix: ['applicant'], alias: 'addr' }
What it reports
E-REF-03— the target could not be read;E-REF-04— the documents include each other in a cycle, reported with the path of the cycle;E-BLOCK-01— the included document declares something that may not be merged: anoutputblock, arelation, astate, anaction, asource. These declare the document rather than a part of it, and merging two of them would give one document two output schemas.E-REF-05— the include supplies a name the included document does not declare as a parameter;E-REF-06— a parameter has neither a supplied value nor a declared one;E-REF-07— the name of a parameter is also used by another declaration or binding in the document that declares it;E-REF-08— the value of a parameter is not a literal.
The diagnostics of a document that did not parse are the parser's own, and they stop the merge of that document.
Every statement that came from an included document carries the name of the file it came from, so a diagnostic inside a module names the module rather than the form that included it.
The resolver
The package reads no files itself. A SourceResolver says how a path becomes
a canonical path and how a canonical path is read, so the merge can be driven
by a file system, by a test, or by an editor holding its documents in memory.
@formscript/loader/node provides one over the file system. The resolver
must give the same file the same canonical path every time, because a cycle
is detected on the canonical path.
Public API
15 exported symbols.
collectNames
function
Every name the document uses for a declaration or a binding. The names of the parameters are not among them: a parameter is checked against them, not with th…
collectParameters
function
The parameters a document declares, and the ones that repeat a name.
IncludeEdge
interface
One include: the document that names the other, and the other.
isParameterValue
function
Whether a value may be the value of a parameter (Spec-4.57).
nodeResolver
const
A resolver over the file system. The path of an include is resolved relative to the directory of the document that names it (Spec-4.47), and the result is ab…
ParameterDeclaration
interface
A parameter of the document that declares it (Spec-4.54).
resolveIncludes
function
Merges every included document into the document that names it (Spec-4.47 to Spec-4.53). The merge is a transformation of the tree, not of the text: the stat…
ResolveOptions
interfaceResolveResult
interfaceSourceResolver
interface
Reads the documents an `include` names.
substituteConstraint
function
Rewrites the expressions of a constraint.
substituteExpression
function
Rewrites the expressions of an expression.
substituteParameters
function
Replaces every reference to a parameter with the parameter's value, and drops the parameter declarations (Spec-4.59).
substituteStatement
function
Rewrites one statement, or drops it when it is a parameter declaration.