@vertekum/schema-builder 0.1.4 → 0.1.5
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/CHANGELOG.md +6 -0
- package/README.md +37 -89
- package/docs/build.md +81 -0
- package/docs/emission.md +129 -0
- package/docs/expressions.md +150 -0
- package/docs/language.md +95 -0
- package/docs/lint.md +59 -0
- package/docs/modules.md +105 -0
- package/docs/scales.md +88 -0
- package/package.json +2 -1
- package/src/api.ts +11 -4
- package/src/ast.ts +13 -4
- package/src/build.ts +58 -20
- package/src/cli.ts +51 -1
- package/src/error.ts +8 -2
- package/src/lint.ts +80 -0
- package/src/parser.ts +24 -3
- package/src/resolve.ts +11 -1
package/src/parser.ts
CHANGED
|
@@ -133,6 +133,16 @@ class Parser {
|
|
|
133
133
|
token.column,
|
|
134
134
|
);
|
|
135
135
|
}
|
|
136
|
+
// `<scale>*` — the open-set mark after a closed `>`/`]` lands here as a stray
|
|
137
|
+
// statement tail; name the fix rather than complaining about the line ending.
|
|
138
|
+
if (this.peek().kind === 'star') {
|
|
139
|
+
const star = this.peek();
|
|
140
|
+
throw new DfnError(
|
|
141
|
+
"'*' marks a set open and sits inside the reference or group it opens — <name*> or [a | b *]",
|
|
142
|
+
star.line,
|
|
143
|
+
star.column,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
136
146
|
if (this.peek().kind !== 'eof') this.expect('newline', 'end of line');
|
|
137
147
|
this.skipNewlines();
|
|
138
148
|
}
|
|
@@ -189,10 +199,11 @@ class Parser {
|
|
|
189
199
|
|
|
190
200
|
private term(): Step['term'] {
|
|
191
201
|
const token = this.next();
|
|
202
|
+
const at = { line: token.line, column: token.column };
|
|
192
203
|
switch (token.kind) {
|
|
193
204
|
case 'ident':
|
|
194
205
|
case 'number':
|
|
195
|
-
return { kind: 'name', value: token.value };
|
|
206
|
+
return { kind: 'name', value: token.value, ...at };
|
|
196
207
|
case 'range': {
|
|
197
208
|
const payload = token.range as NonNullable<Token['range']>;
|
|
198
209
|
const { min, max, mode, step, quantum, pad } = payload;
|
|
@@ -218,7 +229,7 @@ class Parser {
|
|
|
218
229
|
token.column,
|
|
219
230
|
);
|
|
220
231
|
}
|
|
221
|
-
return { kind: 'range', min, max, mode: 'stepped', step, pad };
|
|
232
|
+
return { kind: 'range', min, max, mode: 'stepped', step, pad, ...at };
|
|
222
233
|
}
|
|
223
234
|
if (step <= 1) {
|
|
224
235
|
throw new DfnError(
|
|
@@ -235,6 +246,7 @@ class Parser {
|
|
|
235
246
|
step,
|
|
236
247
|
quantum,
|
|
237
248
|
pad,
|
|
249
|
+
...at,
|
|
238
250
|
};
|
|
239
251
|
}
|
|
240
252
|
case 'langle': {
|
|
@@ -289,6 +301,7 @@ class Parser {
|
|
|
289
301
|
open,
|
|
290
302
|
pick,
|
|
291
303
|
omit,
|
|
304
|
+
...at,
|
|
292
305
|
};
|
|
293
306
|
}
|
|
294
307
|
case 'lbracket': {
|
|
@@ -296,8 +309,16 @@ class Parser {
|
|
|
296
309
|
const open = this.peek().kind === 'star';
|
|
297
310
|
if (open) this.next();
|
|
298
311
|
this.expect('rbracket', "']'");
|
|
299
|
-
return { kind: 'group', node, open };
|
|
312
|
+
return { kind: 'group', node, open, ...at };
|
|
300
313
|
}
|
|
314
|
+
case 'star':
|
|
315
|
+
// The most common misplacement: `color.*` or `a | *`. The mark exists — it just
|
|
316
|
+
// sits inside the set it opens, so say that instead of a bare grammar complaint.
|
|
317
|
+
throw new DfnError(
|
|
318
|
+
"'*' marks a set open and sits inside the reference or group it opens — <name*> or [a | b *]",
|
|
319
|
+
token.line,
|
|
320
|
+
token.column,
|
|
321
|
+
);
|
|
301
322
|
default:
|
|
302
323
|
throw new DfnError(
|
|
303
324
|
`expected a term, got '${token.value || token.kind}'`,
|
package/src/resolve.ts
CHANGED
|
@@ -55,7 +55,17 @@ export function resolveModule(
|
|
|
55
55
|
} catch {
|
|
56
56
|
throw new DfnError(`cannot read ${absolute}`, 1, 1);
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
let module: Module;
|
|
59
|
+
try {
|
|
60
|
+
module = parse(source);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// Stamp the file: a sweep parses many modules, and `13:10 …` without a file name is
|
|
63
|
+
// exactly the kind of message this pipeline should never emit.
|
|
64
|
+
if (error instanceof DfnError && !error.file) {
|
|
65
|
+
throw new DfnError(error.detail, error.line, error.column, absolute);
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
59
69
|
|
|
60
70
|
const imports = new Map<string, ResolvedModule>();
|
|
61
71
|
for (const { spec, alias } of module.uses) {
|