forma-dsl 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lang/builtins.js +15 -3
- package/src/lang/evaluate.js +19 -1
package/package.json
CHANGED
package/src/lang/builtins.js
CHANGED
|
@@ -177,10 +177,17 @@ export class Args {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
/**
|
|
180
|
-
* Rejects anything the block did not read.
|
|
180
|
+
* Rejects anything the block did not read, and says what it would have read instead.
|
|
181
181
|
*
|
|
182
182
|
* A misspelled attribute is otherwise silent, and the model renders subtly wrong with
|
|
183
|
-
* nothing to point at.
|
|
183
|
+
* nothing to point at. Naming the alternatives matters as much as the refusal: the reader
|
|
184
|
+
* is often a program with no copy of the manual, and an error that says only what is wrong
|
|
185
|
+
* leaves it guessing at spellings. What it read is tracked already, so the list costs
|
|
186
|
+
* nothing to produce.
|
|
187
|
+
*
|
|
188
|
+
* A block that branches on which of two spellings it was given — `radius` against
|
|
189
|
+
* `diameter` — only reads one of them, so the list is what this call would accept and not
|
|
190
|
+
* always the whole set.
|
|
184
191
|
*
|
|
185
192
|
* @returns {void}
|
|
186
193
|
* @throws {FormaError} If any attribute went unread.
|
|
@@ -189,7 +196,12 @@ export class Args {
|
|
|
189
196
|
const extra = Object.keys(this.values).filter((k) => !this.used.has(k));
|
|
190
197
|
if (extra.length) {
|
|
191
198
|
const plural = extra.length > 1 ? 's' : '';
|
|
192
|
-
|
|
199
|
+
const reads = [...this.used].sort((a, z) => a.localeCompare(z));
|
|
200
|
+
const instead = reads.length ? ` — ${this.type} reads ${quoteAll(reads)}` : '';
|
|
201
|
+
throw new FormaError(
|
|
202
|
+
`${this.type}: unknown attribute${plural} ${quoteAll(extra)}${instead}`,
|
|
203
|
+
this.loc,
|
|
204
|
+
);
|
|
193
205
|
}
|
|
194
206
|
}
|
|
195
207
|
}
|
package/src/lang/evaluate.js
CHANGED
|
@@ -7,6 +7,12 @@ import { BLOCKS, FUNCTIONS, CONSTANTS, TYPE_NAMES, Args, quoteAll } from './buil
|
|
|
7
7
|
import { GeometryNode } from '../core/node.js';
|
|
8
8
|
import { Transform } from '../values/transform.js';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* The blocks handled before the registry is consulted, so they are missing from `BLOCKS` and
|
|
12
|
+
* would otherwise be absent from the list an unknown block is measured against.
|
|
13
|
+
*/
|
|
14
|
+
const SPECIAL_FORMS = ['align', 'for', 'if', 'part'];
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* What a body produces: geometry to hand upwards, and any scene parts declared inside it.
|
|
12
18
|
*
|
|
@@ -447,7 +453,19 @@ export class Evaluator {
|
|
|
447
453
|
|
|
448
454
|
const definition = BLOCKS[block.type];
|
|
449
455
|
if (!definition) {
|
|
450
|
-
|
|
456
|
+
// The whole vocabulary, because the reader is often a program with no copy of the
|
|
457
|
+
// manual: a misspelling it cannot correct from the error costs a round trip it has no
|
|
458
|
+
// way to end. Declared components come first — they are the names this document itself
|
|
459
|
+
// introduced, and the likeliest thing a near miss was reaching for.
|
|
460
|
+
const known = [
|
|
461
|
+
...[...this.program.components.keys()].sort((a, z) => a.localeCompare(z)),
|
|
462
|
+
...SPECIAL_FORMS,
|
|
463
|
+
...Object.keys(BLOCKS).sort((a, z) => a.localeCompare(z)),
|
|
464
|
+
];
|
|
465
|
+
throw new FormaError(
|
|
466
|
+
`unknown block "${block.type}" — the blocks are ${quoteAll(known)}`,
|
|
467
|
+
block.loc,
|
|
468
|
+
);
|
|
451
469
|
}
|
|
452
470
|
if (block.labels.length) {
|
|
453
471
|
throw new FormaError(`"${block.type}" does not take a label`, block.loc);
|