@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/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
- const module = parse(source);
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) {