@primmel/primmel 1.6.0 → 1.7.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.
@@ -187,6 +187,7 @@ function createDuplicateIdChecker() {
187
187
  function parse(mmelString, parsers, options = {}) {
188
188
  const tokens = tokenizeWithPositions(mmelString);
189
189
  const dupChecker = createDuplicateIdChecker();
190
+ const constructs = [];
190
191
  let ctx = {
191
192
  root: "",
192
193
  metadata: null,
@@ -298,6 +299,15 @@ function parse(mmelString, parsers, options = {}) {
298
299
  ctx.issues.push(issue);
299
300
  }
300
301
  }
302
+ if (options.withProvenance) {
303
+ constructs.push({
304
+ field: cfg.field ?? keyword,
305
+ id: idTok.value,
306
+ keyword,
307
+ start: tok.start,
308
+ end: payloadTok.end
309
+ });
310
+ }
301
311
  updateCtx = cfg.parse(idTok.value, payloadTok.value);
302
312
  } else {
303
313
  if (i >= tokens.length) {
@@ -306,6 +316,15 @@ function parse(mmelString, parsers, options = {}) {
306
316
  );
307
317
  }
308
318
  const payloadTok = tokens[i++];
319
+ if (options.withProvenance) {
320
+ constructs.push({
321
+ field: cfg.field ?? keyword,
322
+ id: "",
323
+ keyword,
324
+ start: tok.start,
325
+ end: payloadTok.end
326
+ });
327
+ }
309
328
  updateCtx = cfg.parse(payloadTok.value);
310
329
  }
311
330
  const next = updateCtx(ctx);
@@ -314,6 +333,9 @@ function parse(mmelString, parsers, options = {}) {
314
333
  }
315
334
  ctx = next;
316
335
  }
336
+ if (options.withProvenance) {
337
+ ctx.constructs = constructs;
338
+ }
317
339
  return ctx;
318
340
  }
319
341
 
@@ -432,8 +454,12 @@ function forEachAttribute(data, visitor, errCtx) {
432
454
  while (i < t.length) {
433
455
  if (t[i] === "ref") {
434
456
  const nameParts2 = [t[i++]];
435
- if (i < t.length) nameParts2.push(t[i++]);
436
- if (i < t.length) nameParts2.push(t[i++]);
457
+ if (i < t.length) {
458
+ nameParts2.push(t[i++]);
459
+ }
460
+ if (i < t.length) {
461
+ nameParts2.push(t[i++]);
462
+ }
437
463
  let blockContent = "";
438
464
  if (i < t.length && t[i].charAt(0) === "{") {
439
465
  blockContent = unwrapBlock(t[i++]);
@@ -642,7 +668,9 @@ var parseDataClass = function(id, data) {
642
668
  const ref = { predicate: m[1], target: m[2] };
643
669
  if (details) {
644
670
  const noteMatch = /note\s+"([^"]*)"/.exec(details) ?? /note\s+(\S+)/.exec(details);
645
- if (noteMatch) ref.note = noteMatch[1];
671
+ if (noteMatch) {
672
+ ref.note = noteMatch[1];
673
+ }
646
674
  }
647
675
  result.ref ??= [];
648
676
  result.ref.push(ref);
@@ -792,7 +820,9 @@ var dumpDataClass = function(dataclass) {
792
820
  if (dataclass.ref && dataclass.ref.length > 0) {
793
821
  for (const r of dataclass.ref) {
794
822
  out += " ref " + r.predicate + ' "' + escapeString(r.target) + '"';
795
- if (r.note) out += ' { note "' + escapeString(r.note) + '" }';
823
+ if (r.note) {
824
+ out += ' { note "' + escapeString(r.note) + '" }';
825
+ }
796
826
  out += "\n";
797
827
  }
798
828
  }
@@ -7525,6 +7555,8 @@ var parseTerm = function(id, data) {
7525
7555
  result.note = unwrapped(value);
7526
7556
  } else if (command === "source") {
7527
7557
  result.source = unwrapped(value);
7558
+ } else if (command === "overlay") {
7559
+ result.overlay = value() === "true";
7528
7560
  } else if (command === "scope_note") {
7529
7561
  result.scopeNote = unwrapped(value);
7530
7562
  } else if (command === "language") {
@@ -13228,6 +13260,55 @@ function packageFiles(dir) {
13228
13260
  }
13229
13261
  return files;
13230
13262
  }
13263
+ function countNewlines(text) {
13264
+ let n = 0;
13265
+ for (let i = 0; i < text.length; i++) {
13266
+ if (text.charAt(i) === "\n") {
13267
+ n++;
13268
+ }
13269
+ }
13270
+ return n;
13271
+ }
13272
+ function chunkify(files) {
13273
+ const chunks = [];
13274
+ let offset = 0;
13275
+ let line = 1;
13276
+ for (const f of files) {
13277
+ if (f.role !== "content") {
13278
+ continue;
13279
+ }
13280
+ const text = preprocessIncludes(f.path);
13281
+ chunks.push({ path: f.path, text, start: offset, lineStart: line });
13282
+ offset += text.length + 2;
13283
+ line += countNewlines(text) + 2;
13284
+ }
13285
+ return chunks;
13286
+ }
13287
+ function joinChunks(chunks) {
13288
+ return chunks.map((c) => c.text).join("\n\n");
13289
+ }
13290
+ function mapConstructs(parsed, chunks, packageId) {
13291
+ const out = {};
13292
+ let ci = 0;
13293
+ for (const c of parsed) {
13294
+ while (ci + 1 < chunks.length && c.start.offset >= chunks[ci + 1].start) {
13295
+ ci++;
13296
+ }
13297
+ const chunk = chunks[ci];
13298
+ const local = (p) => ({
13299
+ line: p.line - chunk.lineStart + 1,
13300
+ col: p.col,
13301
+ offset: p.offset - chunk.start
13302
+ });
13303
+ const source = {
13304
+ file: chunk.path,
13305
+ ...packageId !== void 0 ? { package: packageId } : {},
13306
+ span: { start: local(c.start), end: local(c.end) }
13307
+ };
13308
+ (out[c.field] ??= {})[c.id] = source;
13309
+ }
13310
+ return out;
13311
+ }
13231
13312
  function readPackageManifest(path) {
13232
13313
  const manifestPath = join(resolve3(path), "package.primmel");
13233
13314
  if (!existsSync2(manifestPath)) {
@@ -13246,24 +13327,21 @@ function readPackageManifest(path) {
13246
13327
  }
13247
13328
  return manifest;
13248
13329
  }
13249
- function loadPackageInternal(dir, options = {}) {
13330
+ function loadPackageInternal(dir, options = {}, withProvenance = false) {
13250
13331
  const files = packageFiles(dir);
13251
13332
  if (files.length === 0) {
13252
13333
  throw new Error(`loadPackage: no package files found in ${resolve3(dir)}`);
13253
13334
  }
13254
13335
  let manifest = null;
13255
- const chunks = [];
13256
- for (const f of files) {
13257
- if (f.role === "manifest") {
13258
- manifest = readPackageManifest(dir);
13259
- } else {
13260
- chunks.push(preprocessIncludes(f.path));
13261
- }
13336
+ if (files.some((f) => f.role === "manifest")) {
13337
+ manifest = readPackageManifest(dir);
13262
13338
  }
13339
+ const chunks = chunkify(files);
13263
13340
  if (manifest && options.resolvePackage && effectiveUses(manifest).length > 0) {
13264
- return composePackage(dir, options, manifest);
13341
+ return composePackage(dir, options, manifest, withProvenance);
13265
13342
  }
13266
- const ctx = parse(chunks.join("\n\n"), PARSER_CONFIG, options);
13343
+ const parseOptions = withProvenance ? { ...options, withProvenance: true } : options;
13344
+ const ctx = parse(joinChunks(chunks), PARSER_CONFIG, parseOptions);
13267
13345
  if (manifest) {
13268
13346
  ctx.packageManifest = manifest;
13269
13347
  const deprecated = extendsDeprecationIssue(manifest);
@@ -13272,7 +13350,15 @@ function loadPackageInternal(dir, options = {}) {
13272
13350
  }
13273
13351
  }
13274
13352
  const standard = resolve(ctx, RESOLVER_CONFIG);
13275
- return { standard, issues: ctx.issues };
13353
+ const result = { standard, issues: ctx.issues };
13354
+ if (withProvenance) {
13355
+ const manifestFile = files.find((f) => f.role === "manifest")?.path;
13356
+ result.provenance = {
13357
+ ...manifestFile !== void 0 ? { manifest: manifestFile } : {},
13358
+ constructs: mapConstructs(ctx.constructs ?? [], chunks, manifest?.id)
13359
+ };
13360
+ }
13361
+ return result;
13276
13362
  }
13277
13363
  function loadPackageWithIssues(dir, options = {}) {
13278
13364
  return loadPackageInternal(dir, options);
@@ -13371,15 +13457,11 @@ var MERGE_FIELDS = [
13371
13457
  "texts"
13372
13458
  ];
13373
13459
  function parsePackageContent(dir, options) {
13374
- const chunks = [];
13375
- for (const f of packageFiles(dir)) {
13376
- if (f.role === "content") {
13377
- chunks.push(preprocessIncludes(f.path));
13378
- }
13379
- }
13380
- return parse(chunks.join("\n\n"), PARSER_CONFIG, options);
13460
+ const chunks = chunkify(packageFiles(dir));
13461
+ const ctx = parse(joinChunks(chunks), PARSER_CONFIG, options);
13462
+ return { ctx, chunks };
13381
13463
  }
13382
- function composePackage(rootDir, options, rootManifest) {
13464
+ function composePackage(rootDir, options, rootManifest, withProvenance = false) {
13383
13465
  const locate = options.resolvePackage;
13384
13466
  const rootId = rootManifest.id;
13385
13467
  const dirs = /* @__PURE__ */ new Map([[rootId, resolve3(rootDir)]]);
@@ -13430,9 +13512,13 @@ function composePackage(rootDir, options, rootManifest) {
13430
13512
  order.push(id);
13431
13513
  };
13432
13514
  visit(rootId);
13515
+ const parseOptions = withProvenance ? { ...options, withProvenance: true } : options;
13433
13516
  const ctxs = /* @__PURE__ */ new Map();
13517
+ const pkgChunks = /* @__PURE__ */ new Map();
13434
13518
  for (const id of order) {
13435
- ctxs.set(id, parsePackageContent(dirs.get(id), options));
13519
+ const parsed = parsePackageContent(dirs.get(id), parseOptions);
13520
+ ctxs.set(id, parsed.ctx);
13521
+ pkgChunks.set(id, parsed.chunks);
13436
13522
  }
13437
13523
  const provenance = /* @__PURE__ */ new Map();
13438
13524
  for (const field of MERGE_FIELDS) {
@@ -13454,10 +13540,13 @@ function composePackage(rootDir, options, rootManifest) {
13454
13540
  )) {
13455
13541
  const prior = pm.get(key);
13456
13542
  if (prior !== void 0) {
13457
- throw new CompositionError(
13458
- "uses-no-redefine",
13459
- `package "${id}" redefines ${String(field)} id "${key}" already declared by package "${prior}" \u2014 an overlay may reference upstream ids, never redefine them (uses-no-redefine)`
13460
- );
13543
+ const isOverlay = field === "terms" && typeof value === "object" && value !== null && value.overlay === true;
13544
+ if (!isOverlay) {
13545
+ throw new CompositionError(
13546
+ "uses-no-redefine",
13547
+ `package "${id}" redefines ${String(field)} id "${key}" already declared by package "${prior}" \u2014 an overlay may reference upstream ids, never redefine them (uses-no-redefine)`
13548
+ );
13549
+ }
13461
13550
  }
13462
13551
  target[key] = value;
13463
13552
  pm.set(key, id);
@@ -13514,11 +13603,32 @@ function composePackage(rootDir, options, rootManifest) {
13514
13603
  }
13515
13604
  acc.packageManifest = rootManifest;
13516
13605
  const standard = resolve(acc, RESOLVER_CONFIG);
13517
- return {
13606
+ const result = {
13518
13607
  standard,
13519
13608
  issues: [...acc.issues, ...compositionIssues],
13520
13609
  composition: { root: rootId, order }
13521
13610
  };
13611
+ if (withProvenance) {
13612
+ const constructs = {};
13613
+ for (const id of order) {
13614
+ const perPackage = mapConstructs(
13615
+ ctxs.get(id).constructs ?? [],
13616
+ pkgChunks.get(id),
13617
+ id
13618
+ );
13619
+ for (const [field, byId2] of Object.entries(perPackage)) {
13620
+ const target = constructs[field] ??= {};
13621
+ for (const [constructId, source] of Object.entries(byId2)) {
13622
+ target[constructId] = source;
13623
+ }
13624
+ }
13625
+ }
13626
+ result.provenance = {
13627
+ manifest: join(resolve3(rootDir), "package.primmel"),
13628
+ constructs
13629
+ };
13630
+ }
13631
+ return result;
13522
13632
  }
13523
13633
 
13524
13634
  // src/ser-des/prm.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primmel/primmel",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Primmel — parser, types, and serializer for the Primmel modelling language (successor to MMEL)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",