@sdeverywhere/parse 0.1.1 → 0.1.3

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/dist/index.cjs CHANGED
@@ -27,20 +27,50 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
29
  // src/index.ts
30
- var src_exports = {};
31
- __export(src_exports, {
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ canonicalFunctionId: () => canonicalFunctionId,
33
+ canonicalId: () => canonicalId,
34
+ canonicalVarId: () => canonicalVarId,
32
35
  debugPrintExpr: () => debugPrintExpr,
33
36
  parseVensimEquation: () => parseVensimEquation,
34
37
  parseVensimExpr: () => parseVensimExpr,
35
38
  parseVensimModel: () => parseVensimModel,
36
39
  parseVensimSubscriptRange: () => parseVensimSubscriptRange,
40
+ parseXmileDimensionDef: () => parseXmileDimensionDef,
41
+ parseXmileModel: () => parseXmileModel,
42
+ parseXmileVariableDef: () => parseXmileVariableDef,
43
+ preprocessVensimModel: () => preprocessVensimModel,
37
44
  prettyPrintExpr: () => prettyPrintExpr,
38
45
  printExprStats: () => printExprStats,
39
46
  reduceConditionals: () => reduceConditionals,
40
47
  reduceExpr: () => reduceExpr,
41
48
  toPrettyString: () => toPrettyString
42
49
  });
43
- module.exports = __toCommonJS(src_exports);
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/_shared/canonical-id.js
53
+ var reTrailingMark = new RegExp("\\s+!$", "g");
54
+ var reWhitespace = new RegExp("(\\s|_)+", "g");
55
+ var reSpecialChars = new RegExp(`['"\\.,\\-\\$&%\\/\\|()]`, "g");
56
+ function canonicalId(name) {
57
+ return "_" + name.trim().replace(reTrailingMark, "!").replace(reWhitespace, "_").replace(reSpecialChars, "_").toLowerCase();
58
+ }
59
+ function canonicalVarId(name) {
60
+ const m = name.match(/([^[]+)(?:\[([^\]]+)\])?/);
61
+ if (!m) {
62
+ throw new Error(`Invalid variable name: ${name}`);
63
+ }
64
+ let id = canonicalId(m[1]);
65
+ if (m[2]) {
66
+ const subscripts = m[2].split(",").map((x) => canonicalId(x));
67
+ id += `[${subscripts.join(",")}]`;
68
+ }
69
+ return id;
70
+ }
71
+ function canonicalFunctionId(name) {
72
+ return canonicalId(name).toUpperCase();
73
+ }
44
74
 
45
75
  // src/ast/print-expr.ts
46
76
  var import_assert_never = require("assert-never");
@@ -290,15 +320,13 @@ function fullIdForVarRef(varRef) {
290
320
  // src/ast/reduce-expr.ts
291
321
  var import_assert_never2 = require("assert-never");
292
322
 
293
- // src/_shared/names.js
294
- function canonicalName(name) {
295
- return "_" + name.trim().replace(/"/g, "_").replace(/\s+!$/g, "!").replace(/\s/g, "_").replace(/,/g, "_").replace(/-/g, "_").replace(/\./g, "_").replace(/\$/g, "_").replace(/'/g, "_").replace(/&/g, "_").replace(/%/g, "_").replace(/\//g, "_").replace(/\|/g, "_").toLowerCase();
296
- }
297
- function cFunctionName(name) {
298
- return canonicalName(name).toUpperCase();
299
- }
300
-
301
323
  // src/ast/ast-builders.ts
324
+ function subRef(dimOrSubName) {
325
+ return {
326
+ subName: dimOrSubName,
327
+ subId: canonicalId(dimOrSubName)
328
+ };
329
+ }
302
330
  function num(value, text) {
303
331
  return {
304
332
  kind: "number",
@@ -327,6 +355,13 @@ function parens(expr) {
327
355
  expr
328
356
  };
329
357
  }
358
+ function lookupDef(points, range) {
359
+ return {
360
+ kind: "lookup-def",
361
+ range,
362
+ points
363
+ };
364
+ }
330
365
  function lookupCall(varRef, arg) {
331
366
  return {
332
367
  kind: "lookup-call",
@@ -334,6 +369,14 @@ function lookupCall(varRef, arg) {
334
369
  arg
335
370
  };
336
371
  }
372
+ function call(fnName, ...args) {
373
+ return {
374
+ kind: "function-call",
375
+ fnName,
376
+ fnId: canonicalFunctionId(fnName),
377
+ args
378
+ };
379
+ }
337
380
 
338
381
  // src/ast/reduce-expr.ts
339
382
  function reduceExpr(expr, opts) {
@@ -673,7 +716,7 @@ var SubscriptRangeReader = class extends import_antlr4_vensim2.ModelVisitor {
673
716
  const ids = ctx.Id();
674
717
  if (ids.length === 1) {
675
718
  const dimName = ids[0].getText();
676
- const dimId = canonicalName(dimName);
719
+ const dimId = canonicalId(dimName);
677
720
  super.visitSubscriptRange(ctx);
678
721
  return {
679
722
  dimName,
@@ -683,7 +726,7 @@ var SubscriptRangeReader = class extends import_antlr4_vensim2.ModelVisitor {
683
726
  subscriptRefs: this.subscriptNames.map((subName) => {
684
727
  return {
685
728
  subName,
686
- subId: canonicalName(subName)
729
+ subId: canonicalId(subName)
687
730
  };
688
731
  }),
689
732
  subscriptMappings: this.subscriptMappings,
@@ -691,9 +734,9 @@ var SubscriptRangeReader = class extends import_antlr4_vensim2.ModelVisitor {
691
734
  };
692
735
  } else if (ids.length === 2) {
693
736
  const dimName = ids[0].getText();
694
- const dimId = canonicalName(dimName);
737
+ const dimId = canonicalId(dimName);
695
738
  const familyName = ids[1].getText();
696
- const familyId = canonicalName(familyName);
739
+ const familyId = canonicalId(familyName);
697
740
  return {
698
741
  dimName,
699
742
  dimId,
@@ -733,11 +776,11 @@ var SubscriptRangeReader = class extends import_antlr4_vensim2.ModelVisitor {
733
776
  super.visitSubscriptMapping(ctx);
734
777
  this.subscriptMappings.push({
735
778
  toDimName,
736
- toDimId: canonicalName(toDimName),
779
+ toDimId: canonicalId(toDimName),
737
780
  subscriptRefs: this.mappedSubscriptNames.map((subName) => {
738
781
  return {
739
782
  subName,
740
- subId: canonicalName(subName)
783
+ subId: canonicalId(subName)
741
784
  };
742
785
  })
743
786
  });
@@ -747,7 +790,7 @@ var SubscriptRangeReader = class extends import_antlr4_vensim2.ModelVisitor {
747
790
  }
748
791
  visitCall(ctx) {
749
792
  const fnName = ctx.Id().getText();
750
- const fnId = cFunctionName(fnName);
793
+ const fnId = canonicalFunctionId(fnName);
751
794
  if (fnId === "_GET_DIRECT_SUBSCRIPT") {
752
795
  super.visitCall(ctx);
753
796
  } else {
@@ -843,7 +886,7 @@ var ExprReader = class extends import_antlr4_vensim3.ModelVisitor {
843
886
  //
844
887
  visitCall(ctx) {
845
888
  const vensimFnName = ctx.Id().getText();
846
- const fnId = cFunctionName(vensimFnName);
889
+ const fnId = canonicalFunctionId(vensimFnName);
847
890
  this.callStack.push({ fn: fnId, args: [] });
848
891
  super.visitCall(ctx);
849
892
  const callInfo = this.callStack.pop();
@@ -866,14 +909,14 @@ var ExprReader = class extends import_antlr4_vensim3.ModelVisitor {
866
909
  }
867
910
  visitVar(ctx) {
868
911
  const vensimVarName = ctx.Id().getText().trim();
869
- const varId = canonicalName(vensimVarName);
912
+ const varId = canonicalId(vensimVarName);
870
913
  this.subscripts = void 0;
871
914
  super.visitVar(ctx);
872
915
  const subscriptNames = this.subscripts;
873
916
  const subscriptRefs = subscriptNames?.map((name) => {
874
917
  return {
875
918
  subName: name,
876
- subId: canonicalName(name)
919
+ subId: canonicalId(name)
877
920
  };
878
921
  });
879
922
  this.subscripts = void 0;
@@ -923,7 +966,7 @@ var ExprReader = class extends import_antlr4_vensim3.ModelVisitor {
923
966
  }
924
967
  visitLookupCall(ctx) {
925
968
  const lookupVarName = ctx.Id().getText();
926
- const lookupVarId = canonicalName(lookupVarName);
969
+ const lookupVarId = canonicalId(lookupVarName);
927
970
  if (ctx.subscriptList()) {
928
971
  ctx.subscriptList().accept(this);
929
972
  }
@@ -931,7 +974,7 @@ var ExprReader = class extends import_antlr4_vensim3.ModelVisitor {
931
974
  const subscriptRefs = subscriptNames?.map((name) => {
932
975
  return {
933
976
  subName: name,
934
- subId: canonicalName(name)
977
+ subId: canonicalId(name)
935
978
  };
936
979
  });
937
980
  this.subscripts = void 0;
@@ -1128,13 +1171,13 @@ var EquationReader = class extends import_antlr4_vensim4.ModelVisitor {
1128
1171
  }
1129
1172
  visitLhs(ctx) {
1130
1173
  const lhsVarName = ctx.Id().getText();
1131
- const lhsVarId = canonicalName(lhsVarName);
1174
+ const lhsVarId = canonicalId(lhsVarName);
1132
1175
  super.visitLhs(ctx);
1133
1176
  const subscriptNames = this.subscripts;
1134
1177
  const subscriptRefs = subscriptNames?.map((name) => {
1135
1178
  return {
1136
1179
  subName: name,
1137
- subId: canonicalName(name)
1180
+ subId: canonicalId(name)
1138
1181
  };
1139
1182
  });
1140
1183
  const exceptSubscriptSets = this.exceptSubscriptSets;
@@ -1142,7 +1185,7 @@ var EquationReader = class extends import_antlr4_vensim4.ModelVisitor {
1142
1185
  return subscriptSet.map((name) => {
1143
1186
  return {
1144
1187
  subName: name,
1145
- subId: canonicalName(name)
1188
+ subId: canonicalId(name)
1146
1189
  };
1147
1190
  });
1148
1191
  });
@@ -1222,16 +1265,41 @@ function parseVensimEquation(input) {
1222
1265
 
1223
1266
  // src/vensim/preprocess-vensim.ts
1224
1267
  var import_split_string = __toESM(require("split-string"), 1);
1225
- function preprocessVensimModel(input) {
1268
+ function preprocessVensimModel(input, options) {
1269
+ const removalKeys = options?.removalKeys;
1270
+ function shouldRemove(text) {
1271
+ if (text.includes("TABBED ARRAY")) {
1272
+ return true;
1273
+ }
1274
+ if (removalKeys) {
1275
+ for (const key of removalKeys) {
1276
+ if (text.includes(key)) {
1277
+ return true;
1278
+ }
1279
+ }
1280
+ }
1281
+ return false;
1282
+ }
1283
+ const macrosResult = removeMacros(input);
1284
+ input = macrosResult.processed;
1226
1285
  const rawDefs = splitDefs(input);
1227
1286
  const vensimDefs = [];
1287
+ const removedBlocks = [];
1228
1288
  for (const rawDef of rawDefs) {
1289
+ if (shouldRemove(rawDef.text)) {
1290
+ removedBlocks.push(rawDef.text.trim() + "|");
1291
+ continue;
1292
+ }
1229
1293
  const vensimDef = processDef(rawDef);
1230
1294
  if (vensimDef) {
1231
1295
  vensimDefs.push(vensimDef);
1232
1296
  }
1233
1297
  }
1234
- return vensimDefs;
1298
+ return {
1299
+ defs: vensimDefs,
1300
+ removedMacros: macrosResult.removed,
1301
+ removedBlocks
1302
+ };
1235
1303
  }
1236
1304
  function splitDefs(input) {
1237
1305
  const defTexts = (0, import_split_string.default)(input, { separator: "|", quotes: ['"'], keep: () => true });
@@ -1273,6 +1341,9 @@ function splitDefs(input) {
1273
1341
  function splitLines(input) {
1274
1342
  return input.split(/\r\n|\n|\r/);
1275
1343
  }
1344
+ function splitExceptInQuoted(input, sep) {
1345
+ return (0, import_split_string.default)(input, { separator: sep, quotes: ['"'] });
1346
+ }
1276
1347
  function processBackslashes(input) {
1277
1348
  const inputLines = splitLines(input);
1278
1349
  let output = "";
@@ -1318,22 +1389,27 @@ function replaceDelimitedStrings(str, open, close, newStr) {
1318
1389
  function reduceWhitespace(input) {
1319
1390
  return input.replace(/\s\s+/g, " ").trim();
1320
1391
  }
1392
+ var reWhitespace2 = new RegExp("(\\s|_)+", "g");
1321
1393
  function keyForDef(def) {
1322
1394
  let key = def;
1323
1395
  key = key.replace(/:INTERPOLATE:/g, "");
1396
+ let kind;
1324
1397
  if (key.includes("=")) {
1398
+ kind = "eqn";
1325
1399
  key = key.split("=")[0].trim();
1326
1400
  } else if (key.includes(":")) {
1401
+ kind = "dim";
1327
1402
  key = key.split(":")[0].trim();
1328
1403
  } else {
1404
+ kind = "decl";
1329
1405
  }
1406
+ key = splitExceptInQuoted(key, "(")[0];
1330
1407
  key = key.replace(/"/g, "");
1331
- key = key.split("(")[0];
1332
1408
  key = key.trim();
1333
- key = key.replace(/\[\s*/g, "[");
1334
- key = key.replace(/\s*\]/g, "]");
1409
+ key = key.replace(/(?<=\[).*?(?=\])/g, (match) => match.replace(/\s/g, ""));
1410
+ key = key.replace(reWhitespace2, "_");
1335
1411
  key = key.toLowerCase();
1336
- return key;
1412
+ return { key, kind };
1337
1413
  }
1338
1414
  function processDef(rawDef) {
1339
1415
  let input = rawDef.text;
@@ -1351,7 +1427,7 @@ function processDef(rawDef) {
1351
1427
  ${input}`);
1352
1428
  }
1353
1429
  const rawDefText = reduceWhitespace(parts[0]);
1354
- const key = keyForDef(rawDefText);
1430
+ const { key, kind } = keyForDef(rawDefText);
1355
1431
  const def = `${rawDefText} ~~|`;
1356
1432
  const units = reduceWhitespace(parts[1]);
1357
1433
  const comment = reduceWhitespace(parts[2]);
@@ -1359,12 +1435,25 @@ ${input}`);
1359
1435
  return {
1360
1436
  key,
1361
1437
  def,
1438
+ kind,
1362
1439
  line: rawDef.line,
1363
1440
  units,
1364
1441
  comment,
1365
1442
  ...group ? { group } : {}
1366
1443
  };
1367
1444
  }
1445
+ function removeMacros(input) {
1446
+ const removed = [];
1447
+ const processed = input.replace(/:MACRO:.*:END OF MACRO:/gms, (match) => {
1448
+ removed.push(match);
1449
+ const numBreaks = match.split(/\r\n|\n|\r/gms).length - 1;
1450
+ return numBreaks > 0 ? "\n".repeat(numBreaks) : "";
1451
+ });
1452
+ return {
1453
+ processed,
1454
+ removed
1455
+ };
1456
+ }
1368
1457
 
1369
1458
  // src/vensim/impl/model-reader.js
1370
1459
  var import_antlr4_vensim5 = require("antlr4-vensim");
@@ -1423,7 +1512,7 @@ var ModelReader = class extends import_antlr4_vensim5.ModelVisitor {
1423
1512
  function parseVensimModel(input, context, sort = false) {
1424
1513
  const dimensions = [];
1425
1514
  const equations = [];
1426
- const defs = preprocessVensimModel(input);
1515
+ const { defs } = preprocessVensimModel(input);
1427
1516
  if (sort) {
1428
1517
  defs.sort((a, b) => {
1429
1518
  return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
@@ -1474,13 +1563,604 @@ Detail:
1474
1563
  equations
1475
1564
  };
1476
1565
  }
1566
+
1567
+ // src/xmile/xml.ts
1568
+ var import_parse_xml = require("@rgrove/parse-xml");
1569
+ function firstElemOf(parent, tagName) {
1570
+ return parent?.children.find((n) => {
1571
+ if (n.type === import_parse_xml.XmlNode.TYPE_ELEMENT) {
1572
+ const e = n;
1573
+ return e.name === tagName;
1574
+ } else {
1575
+ return void 0;
1576
+ }
1577
+ });
1578
+ }
1579
+ function firstTextOf(parent) {
1580
+ return parent?.children.find((n) => {
1581
+ return n.type === import_parse_xml.XmlNode.TYPE_TEXT;
1582
+ });
1583
+ }
1584
+ function elemsOf(parent, tagNames) {
1585
+ if (parent === void 0) {
1586
+ return [];
1587
+ }
1588
+ const elems = [];
1589
+ for (const n of parent.children) {
1590
+ if (n.type === import_parse_xml.XmlNode.TYPE_ELEMENT) {
1591
+ const e = n;
1592
+ if (tagNames.includes(e.name)) {
1593
+ elems.push(e);
1594
+ }
1595
+ }
1596
+ }
1597
+ return elems;
1598
+ }
1599
+ function xmlError(elem, msg) {
1600
+ return `${msg}: ${JSON.stringify(elem.toJSON(), null, 2)}`;
1601
+ }
1602
+
1603
+ // src/xmile/parse-xmile-dimension-def.ts
1604
+ function parseXmileDimensionDef(dimElem) {
1605
+ const dimName = dimElem.attributes?.name;
1606
+ if (dimName === void 0) {
1607
+ throw new Error(xmlError(dimElem, "<dim> name attribute is required for dimension definition"));
1608
+ }
1609
+ const elemElems = elemsOf(dimElem, ["elem"]);
1610
+ if (elemElems.length === 0) {
1611
+ throw new Error(xmlError(dimElem, "<dim> must contain one or more <elem> elements"));
1612
+ }
1613
+ const subscriptRefs = [];
1614
+ for (const elem of elemElems) {
1615
+ const subName = elem.attributes?.name;
1616
+ if (subName === void 0) {
1617
+ throw new Error(xmlError(dimElem, "<elem> name attribute is required for dimension element definition"));
1618
+ }
1619
+ const subId = canonicalId(subName);
1620
+ subscriptRefs.push({
1621
+ subId,
1622
+ subName
1623
+ });
1624
+ }
1625
+ const comment = firstElemOf(dimElem, "doc")?.text || "";
1626
+ const dimId = canonicalId(dimName);
1627
+ return {
1628
+ dimName,
1629
+ dimId,
1630
+ // TODO: For Vensim `DimA <-> DimB` aliases, the family name would be `DimB`
1631
+ familyName: dimName,
1632
+ familyId: dimId,
1633
+ subscriptRefs,
1634
+ // TODO: Does XMILE support mappings?
1635
+ subscriptMappings: [],
1636
+ comment
1637
+ };
1638
+ }
1639
+
1640
+ // src/xmile/parse-xmile-model.ts
1641
+ var import_parse_xml2 = require("@rgrove/parse-xml");
1642
+
1643
+ // src/xmile/parse-xmile-variable-def.ts
1644
+ function parseXmileVariableDef(varElem) {
1645
+ let varName = parseRequiredAttr(varElem, varElem, "name");
1646
+ varName = varName.replace(/\\n/g, " ");
1647
+ const varId = canonicalId(varName);
1648
+ const units = firstElemOf(varElem, "units")?.text || "";
1649
+ const comment = firstElemOf(varElem, "doc")?.text || "";
1650
+ function exprEquation(subscriptRefs, expr) {
1651
+ return {
1652
+ lhs: {
1653
+ varDef: {
1654
+ kind: "variable-def",
1655
+ varName,
1656
+ varId,
1657
+ subscriptRefs
1658
+ }
1659
+ },
1660
+ rhs: {
1661
+ kind: "expr",
1662
+ expr
1663
+ },
1664
+ units,
1665
+ comment
1666
+ };
1667
+ }
1668
+ function lookupEquation(subscriptRefs, lookup) {
1669
+ return {
1670
+ lhs: {
1671
+ varDef: {
1672
+ kind: "variable-def",
1673
+ varName,
1674
+ varId,
1675
+ subscriptRefs
1676
+ }
1677
+ },
1678
+ rhs: {
1679
+ kind: "lookup",
1680
+ lookupDef: lookup
1681
+ },
1682
+ units,
1683
+ comment
1684
+ };
1685
+ }
1686
+ if (varElem.name === "gf") {
1687
+ const lookup = parseGfElem(varElem, varElem);
1688
+ return [lookupEquation(void 0, lookup)];
1689
+ }
1690
+ const dimensionsElem = firstElemOf(varElem, "dimensions");
1691
+ const equationDefs = [];
1692
+ if (dimensionsElem === void 0) {
1693
+ const gfElem = firstElemOf(varElem, "gf");
1694
+ if (gfElem) {
1695
+ if (varElem.name !== "flow" && varElem.name !== "aux") {
1696
+ throw new Error(xmlError(varElem, "<gf> is only allowed for <flow> and <aux> variables"));
1697
+ }
1698
+ const lookup = parseGfElem(varElem, gfElem);
1699
+ equationDefs.push(lookupEquation(void 0, lookup));
1700
+ } else {
1701
+ const expr = parseEqnElem(varElem, varElem);
1702
+ if (expr) {
1703
+ equationDefs.push(exprEquation(void 0, expr));
1704
+ }
1705
+ }
1706
+ } else {
1707
+ const dimElems = elemsOf(dimensionsElem, ["dim"]);
1708
+ const dimNames = [];
1709
+ for (const dimElem of dimElems) {
1710
+ const dimName = dimElem.attributes?.name;
1711
+ if (dimName === void 0) {
1712
+ throw new Error(xmlError(varElem, "<dim> name attribute is required in <dimensions> for variable definition"));
1713
+ }
1714
+ dimNames.push(dimName);
1715
+ }
1716
+ const elementElems = elemsOf(varElem, ["element"]);
1717
+ if (elementElems.length === 0) {
1718
+ const dimRefs = dimNames.map(subRef);
1719
+ const expr = parseEqnElem(varElem, varElem);
1720
+ if (expr) {
1721
+ equationDefs.push(exprEquation(dimRefs, expr));
1722
+ }
1723
+ } else {
1724
+ for (const elementElem of elementElems) {
1725
+ const subscriptAttr = elementElem.attributes?.subscript;
1726
+ if (subscriptAttr === void 0) {
1727
+ throw new Error(xmlError(varElem, "<element> subscript attribute is required in variable definition"));
1728
+ }
1729
+ const subscriptNames = subscriptAttr.split(",").map((s) => s.trim());
1730
+ const subRefs = [];
1731
+ for (const subscriptName of subscriptNames) {
1732
+ if (!isNaN(parseInt(subscriptAttr))) {
1733
+ throw new Error(xmlError(varElem, "Numeric subscript indices are not currently supported"));
1734
+ }
1735
+ subRefs.push(subRef(subscriptName));
1736
+ }
1737
+ const expr = parseEqnElem(varElem, elementElem);
1738
+ if (expr) {
1739
+ equationDefs.push(exprEquation(subRefs, expr));
1740
+ }
1741
+ }
1742
+ }
1743
+ }
1744
+ return equationDefs;
1745
+ }
1746
+ function parseEqnElem(varElem, parentElem) {
1747
+ const varTagName = varElem.name;
1748
+ const eqnElem = firstElemOf(parentElem, "eqn");
1749
+ const eqnText = eqnElem ? firstTextOf(eqnElem) : void 0;
1750
+ switch (varTagName) {
1751
+ case "aux": {
1752
+ if (eqnText === void 0) {
1753
+ return void 0;
1754
+ }
1755
+ const initEqnElem = firstElemOf(parentElem, "init_eqn");
1756
+ const initEqnText = initEqnElem ? firstTextOf(initEqnElem) : void 0;
1757
+ if (initEqnText !== void 0) {
1758
+ const eqnExpr = parseExpr(eqnText.text);
1759
+ const initEqnExpr = parseExpr(initEqnText.text);
1760
+ return call("ACTIVE INITIAL", eqnExpr, initEqnExpr);
1761
+ }
1762
+ return parseExpr(eqnText.text);
1763
+ }
1764
+ case "stock": {
1765
+ if (eqnText === void 0) {
1766
+ throw new Error(xmlError(varElem, "An <eqn> is required for a <stock> variable"));
1767
+ }
1768
+ const inflowElems = elemsOf(parentElem, ["inflow"]);
1769
+ const outflowElems = elemsOf(parentElem, ["outflow"]);
1770
+ const inflowTexts = inflowElems.map((inflowElem) => {
1771
+ const inflowText = firstTextOf(inflowElem);
1772
+ if (inflowText === void 0) {
1773
+ throw new Error(xmlError(varElem, "An <inflow> must be non-empty for a <stock> variable"));
1774
+ }
1775
+ return inflowText.text;
1776
+ });
1777
+ const outflowTexts = outflowElems.map((outflowElem) => {
1778
+ const outflowText = firstTextOf(outflowElem);
1779
+ if (outflowText === void 0) {
1780
+ throw new Error(xmlError(varElem, "An <outflow> must be non-empty for a <stock> variable"));
1781
+ }
1782
+ return outflowText.text;
1783
+ });
1784
+ if (firstElemOf(parentElem, "conveyor")) {
1785
+ throw new Error(xmlError(varElem, "Currently <conveyor> is not supported for a <stock> variable"));
1786
+ }
1787
+ if (firstElemOf(parentElem, "queue")) {
1788
+ throw new Error(xmlError(varElem, "Currently <queue> is not supported for a <stock> variable"));
1789
+ }
1790
+ const inflowParts = inflowTexts.join(" + ");
1791
+ let outflowParts = outflowTexts.join(" - ");
1792
+ if (outflowTexts.length > 0) {
1793
+ if (inflowParts.length > 0) {
1794
+ outflowParts = `- ${outflowParts}`;
1795
+ } else {
1796
+ outflowParts = `-${outflowParts}`;
1797
+ }
1798
+ }
1799
+ const flowsExpr = parseExpr(`${inflowParts} ${outflowParts}`);
1800
+ const initExpr = parseExpr(eqnText.text);
1801
+ return call("INTEG", flowsExpr, initExpr);
1802
+ }
1803
+ case "flow":
1804
+ if (eqnText === void 0) {
1805
+ throw new Error(xmlError(varElem, "Currently <eqn> or <gf> is required for a <flow> variable"));
1806
+ }
1807
+ if (firstElemOf(parentElem, "multiplier")) {
1808
+ throw new Error(xmlError(varElem, "Currently <multiplier> is not supported for a <flow> variable"));
1809
+ }
1810
+ if (firstElemOf(parentElem, "overflow")) {
1811
+ throw new Error(xmlError(varElem, "Currently <overflow> is not supported for a <flow> variable"));
1812
+ }
1813
+ if (firstElemOf(parentElem, "leak")) {
1814
+ throw new Error(xmlError(varElem, "Currently <leak> is not supported for a <flow> variable"));
1815
+ }
1816
+ return parseExpr(eqnText.text);
1817
+ default:
1818
+ throw new Error(xmlError(varElem, `Unhandled variable type '${varTagName}'`));
1819
+ }
1820
+ }
1821
+ function parseExpr(exprText) {
1822
+ exprText = convertConditionalExpressions(exprText);
1823
+ exprText = exprText.replace(/\[([^\]]*)\*([^\]]*)\]/g, "[$1_SDE_WILDCARD_!$2]");
1824
+ return parseVensimExpr(exprText);
1825
+ }
1826
+ function parseGfElem(varElem, gfElem) {
1827
+ const typeAttr = parseOptionalAttr(gfElem, "type");
1828
+ if (typeAttr && typeAttr !== "continuous") {
1829
+ throw new Error(xmlError(varElem, 'Currently "continuous" is the only type supported for <gf>'));
1830
+ }
1831
+ const yptsElem = firstElemOf(gfElem, "ypts");
1832
+ if (yptsElem === void 0) {
1833
+ throw new Error(xmlError(varElem, "<ypts> must be defined for a <gf>"));
1834
+ }
1835
+ const ypts = parseGfPts(varElem, yptsElem);
1836
+ if (ypts.length === 0) {
1837
+ throw new Error(xmlError(varElem, "<ypts> must have at least one element"));
1838
+ }
1839
+ const xptsElem = firstElemOf(gfElem, "xpts");
1840
+ const xscaleElem = firstElemOf(gfElem, "xscale");
1841
+ if (xptsElem && xscaleElem) {
1842
+ throw new Error(xmlError(varElem, "<gf> must contain <xpts> or <xscale> but not both"));
1843
+ } else if (xptsElem === void 0 && xscaleElem === void 0) {
1844
+ throw new Error(xmlError(varElem, "<gf> must contain either <xpts> or <xscale>"));
1845
+ }
1846
+ let xpts;
1847
+ if (xptsElem) {
1848
+ xpts = parseGfPts(varElem, xptsElem);
1849
+ if (xpts.length === 0) {
1850
+ throw new Error(xmlError(varElem, "<xpts> must have at least one element"));
1851
+ }
1852
+ } else {
1853
+ const xMin = parseFloatAttr(varElem, xscaleElem, "min");
1854
+ const xMax = parseFloatAttr(varElem, xscaleElem, "max");
1855
+ if (xMin > xMax) {
1856
+ throw new Error(xmlError(varElem, "<xscale> max attribute must be > min attribute"));
1857
+ }
1858
+ xpts = Array(ypts.length);
1859
+ const xRange = xMax - xMin;
1860
+ if (ypts.length === 1) {
1861
+ xpts[0] = 0;
1862
+ } else {
1863
+ for (let i = 0; i < ypts.length; i++) {
1864
+ const frac = i / (ypts.length - 1);
1865
+ xpts[i] = xMin + xRange * frac;
1866
+ }
1867
+ }
1868
+ }
1869
+ if (xpts.length !== ypts.length) {
1870
+ throw new Error(xmlError(varElem, "<xpts> and <ypts> must have the same number of elements"));
1871
+ }
1872
+ const points = [];
1873
+ for (let i = 0; i < xpts.length; i++) {
1874
+ points.push([xpts[i], ypts[i]]);
1875
+ }
1876
+ return lookupDef(points);
1877
+ }
1878
+ function parseGfPts(varElem, ptsElem) {
1879
+ const ptsText = firstTextOf(ptsElem)?.text;
1880
+ if (ptsText === void 0) {
1881
+ return [];
1882
+ }
1883
+ const sep = ptsElem.attributes?.sep || ",";
1884
+ const elems = ptsText.split(sep);
1885
+ const nums = [];
1886
+ for (const elem of elems) {
1887
+ const numText = elem.trim();
1888
+ const num2 = parseFloat(numText);
1889
+ if (isNaN(num2)) {
1890
+ console.log(JSON.stringify(ptsElem));
1891
+ throw new Error(xmlError(varElem, `Invalid number value '${numText}' in <${ptsElem.name}>'`));
1892
+ }
1893
+ nums.push(num2);
1894
+ }
1895
+ return nums;
1896
+ }
1897
+ function parseRequiredAttr(varElem, elem, attrName) {
1898
+ let s = elem.attributes && elem.attributes[attrName];
1899
+ s = s?.trim();
1900
+ if (s === void 0 || s.length === 0) {
1901
+ throw new Error(xmlError(varElem, `<${elem.name}> ${attrName} attribute is required`));
1902
+ }
1903
+ return s;
1904
+ }
1905
+ function parseOptionalAttr(elem, attrName) {
1906
+ const s = elem.attributes && elem.attributes[attrName];
1907
+ return s?.trim();
1908
+ }
1909
+ function parseFloatAttr(varElem, elem, attrName) {
1910
+ const s = parseRequiredAttr(varElem, elem, attrName);
1911
+ const num2 = parseFloat(s);
1912
+ if (isNaN(num2)) {
1913
+ throw new Error(xmlError(varElem, `Invalid number value '${s}' for <${elem.name}> ${attrName} attribute'`));
1914
+ }
1915
+ return num2;
1916
+ }
1917
+ function convertConditionalExpressions(exprText) {
1918
+ const normalizedText = exprText.trim().replace(/\s+/g, " ");
1919
+ const ifMatch = normalizedText.match(/\bIF\s+(.+)$/i);
1920
+ if (!ifMatch) {
1921
+ return exprText;
1922
+ }
1923
+ const ifIndex = normalizedText.search(/\bIF\s+/i);
1924
+ const beforeIf = normalizedText.substring(0, ifIndex);
1925
+ const afterIf = normalizedText.substring(ifIndex + 3).trim();
1926
+ const thenMatch = afterIf.match(/^(.+?)\s+THEN\s+(.+)$/i);
1927
+ if (!thenMatch) {
1928
+ return exprText;
1929
+ }
1930
+ const condition = thenMatch[1].trim();
1931
+ const afterThen = thenMatch[2];
1932
+ let elseIndex = -1;
1933
+ let parenCount = 0;
1934
+ let inQuotes = false;
1935
+ let quoteChar = "";
1936
+ for (let i = 0; i < afterThen.length; i++) {
1937
+ const char = afterThen[i];
1938
+ if ((char === '"' || char === "'") && (i === 0 || afterThen[i - 1] !== "\\")) {
1939
+ if (!inQuotes) {
1940
+ inQuotes = true;
1941
+ quoteChar = char;
1942
+ } else if (char === quoteChar) {
1943
+ inQuotes = false;
1944
+ quoteChar = "";
1945
+ }
1946
+ continue;
1947
+ }
1948
+ if (inQuotes) {
1949
+ continue;
1950
+ }
1951
+ if (char === "(") {
1952
+ parenCount++;
1953
+ } else if (char === ")") {
1954
+ parenCount--;
1955
+ }
1956
+ if (parenCount === 0 && !inQuotes) {
1957
+ const elseMatch = afterThen.substring(i).match(/^ELSE\s+(.+)$/i);
1958
+ if (elseMatch) {
1959
+ elseIndex = i;
1960
+ break;
1961
+ }
1962
+ }
1963
+ }
1964
+ if (elseIndex === -1) {
1965
+ return exprText;
1966
+ }
1967
+ const trueExpr = afterThen.substring(0, elseIndex).trim();
1968
+ let falseExpr = afterThen.substring(elseIndex + 5).trim();
1969
+ let endIndex = -1;
1970
+ parenCount = 0;
1971
+ inQuotes = false;
1972
+ quoteChar = "";
1973
+ for (let i = 0; i < falseExpr.length; i++) {
1974
+ const char = falseExpr[i];
1975
+ if ((char === '"' || char === "'") && (i === 0 || falseExpr[i - 1] !== "\\")) {
1976
+ if (!inQuotes) {
1977
+ inQuotes = true;
1978
+ quoteChar = char;
1979
+ } else if (char === quoteChar) {
1980
+ inQuotes = false;
1981
+ quoteChar = "";
1982
+ }
1983
+ continue;
1984
+ }
1985
+ if (inQuotes) {
1986
+ continue;
1987
+ }
1988
+ if (char === "(") {
1989
+ parenCount++;
1990
+ } else if (char === ")") {
1991
+ if (parenCount === 0) {
1992
+ endIndex = i;
1993
+ break;
1994
+ }
1995
+ parenCount--;
1996
+ }
1997
+ }
1998
+ if (endIndex !== -1) {
1999
+ falseExpr = falseExpr.substring(0, endIndex).trim();
2000
+ }
2001
+ const convertedTrueExpr = convertConditionalExpressions(trueExpr);
2002
+ const convertedFalseExpr = convertConditionalExpressions(falseExpr);
2003
+ const convertedCondition = condition.replace(/(?<!".*?)\b AND \b(?!.*?")/gi, " :AND: ").replace(/(?<!".*?)\b OR \b(?!.*?")/gi, " :OR: ").replace(/(?<!".*?)\b\s?NOT \b(?!.*?")/gi, " :NOT: ").replace(/^\((.+)\)$/, "$1");
2004
+ const elseStartInAfterIf = afterIf.indexOf(" ELSE ") + 6;
2005
+ const falseExprStartInAfterIf = elseStartInAfterIf;
2006
+ const falseExprEndInAfterIf = falseExprStartInAfterIf + falseExpr.length;
2007
+ const conditionalEndInNormalizedText = ifIndex + 3 + falseExprEndInAfterIf;
2008
+ const afterConditional = normalizedText.substring(conditionalEndInNormalizedText).trim();
2009
+ return `${beforeIf}IF THEN ELSE(${convertedCondition}, ${convertedTrueExpr}, ${convertedFalseExpr})${afterConditional}`;
2010
+ }
2011
+
2012
+ // src/xmile/parse-xmile-model.ts
2013
+ function parseXmileModel(input) {
2014
+ let xml;
2015
+ try {
2016
+ xml = (0, import_parse_xml2.parseXml)(input, { includeOffsets: true });
2017
+ } catch (e) {
2018
+ const msg = `Failed to parse XMILE model definition:
2019
+
2020
+ ${e.message}`;
2021
+ throw new Error(msg);
2022
+ }
2023
+ const simulationSpec = parseSimSpecs(xml.root, input);
2024
+ const dimensions = parseDimensionDefs(xml.root, input);
2025
+ const equations = parseVariableDefs(xml.root, input);
2026
+ return {
2027
+ simulationSpec,
2028
+ dimensions,
2029
+ equations
2030
+ };
2031
+ }
2032
+ function parseSimSpecs(rootElem, originalXml) {
2033
+ const simSpecsElem = firstElemOf(rootElem, "sim_specs");
2034
+ if (simSpecsElem === void 0) {
2035
+ throw new Error(xmlError(rootElem, "<sim_specs> element is required for XMILE model definition"));
2036
+ }
2037
+ function getSimSpecValue(name, required) {
2038
+ const elem = firstElemOf(simSpecsElem, name);
2039
+ if (required && elem === void 0) {
2040
+ const error = new Error(xmlError(simSpecsElem, `<${name}> element is required in XMILE sim specs`));
2041
+ throwXmileParseError(error, originalXml, simSpecsElem, "model");
2042
+ }
2043
+ if (elem === void 0) {
2044
+ return void 0;
2045
+ }
2046
+ const value = Number(elem.text);
2047
+ if (!isNaN(value)) {
2048
+ return value;
2049
+ } else {
2050
+ const error = new Error(xmlError(elem, `Invalid numeric value for <${name}> element: ${elem.text}`));
2051
+ throwXmileParseError(error, originalXml, simSpecsElem, "model");
2052
+ }
2053
+ }
2054
+ const startTime = getSimSpecValue("start", true);
2055
+ const endTime = getSimSpecValue("stop", true);
2056
+ let timeStep = getSimSpecValue("dt", false);
2057
+ if (timeStep === void 0) {
2058
+ timeStep = 1;
2059
+ }
2060
+ return {
2061
+ startTime,
2062
+ endTime,
2063
+ timeStep
2064
+ };
2065
+ }
2066
+ function parseDimensionDefs(rootElem, originalXml) {
2067
+ const dimensionDefs = [];
2068
+ const dimensionsElem = firstElemOf(rootElem, "dimensions");
2069
+ if (dimensionsElem) {
2070
+ const dimElems = elemsOf(dimensionsElem, ["dim"]);
2071
+ for (const dimElem of dimElems) {
2072
+ try {
2073
+ dimensionDefs.push(parseXmileDimensionDef(dimElem));
2074
+ } catch (e) {
2075
+ throwXmileParseError(e, originalXml, dimElem, "dimension");
2076
+ }
2077
+ }
2078
+ }
2079
+ return dimensionDefs;
2080
+ }
2081
+ function parseVariableDefs(rootElem, originalXml) {
2082
+ const modelElem = firstElemOf(rootElem, "model");
2083
+ if (modelElem === void 0) {
2084
+ return [];
2085
+ }
2086
+ const equations = [];
2087
+ const variablesElem = firstElemOf(modelElem, "variables");
2088
+ if (variablesElem) {
2089
+ const varElems = elemsOf(variablesElem, ["aux", "stock", "flow", "gf"]);
2090
+ for (const varElem of varElems) {
2091
+ try {
2092
+ const eqns = parseXmileVariableDef(varElem);
2093
+ if (eqns) {
2094
+ equations.push(...eqns);
2095
+ }
2096
+ } catch (e) {
2097
+ throwXmileParseError(e, originalXml, varElem, "variable");
2098
+ }
2099
+ }
2100
+ }
2101
+ return equations;
2102
+ }
2103
+ function throwXmileParseError(originalError, originalXml, elem, elemKind) {
2104
+ let linePart = "";
2105
+ const lineNumInOriginalXml = getLineNumber(originalXml, elem.start);
2106
+ if (lineNumInOriginalXml !== -1) {
2107
+ const cause = originalError.cause;
2108
+ if (cause?.code === "VensimParseError") {
2109
+ if (cause.line) {
2110
+ const lineNum = cause.line - 1 + lineNumInOriginalXml;
2111
+ linePart += ` at line ${lineNum}`;
2112
+ if (cause.column) {
2113
+ linePart += `, col ${cause.column}`;
2114
+ }
2115
+ }
2116
+ } else {
2117
+ linePart += ` at line ${lineNumInOriginalXml}`;
2118
+ }
2119
+ }
2120
+ const elemString = extractXmlLines(originalXml, elem.start, elem.end);
2121
+ const msg = `Failed to parse XMILE ${elemKind} definition${linePart}:
2122
+ ${elemString}
2123
+
2124
+ Detail:
2125
+ ${originalError.message}`;
2126
+ throw new Error(msg);
2127
+ }
2128
+ function getLineNumber(xmlString, byteOffset) {
2129
+ if (byteOffset === -1 || byteOffset >= xmlString.length) {
2130
+ return -1;
2131
+ }
2132
+ const substring = xmlString.substring(0, byteOffset);
2133
+ return substring.split("\n").length;
2134
+ }
2135
+ function extractXmlLines(originalXml, startOffset, endOffset) {
2136
+ if (startOffset === -1 || endOffset === -1 || startOffset >= originalXml.length || endOffset > originalXml.length) {
2137
+ return "[Unable to extract XML lines - invalid offsets]";
2138
+ }
2139
+ let lineStart = startOffset;
2140
+ while (lineStart > 0 && originalXml[lineStart - 1] !== "\n") {
2141
+ lineStart--;
2142
+ }
2143
+ let lineEnd = endOffset;
2144
+ while (lineEnd < originalXml.length && originalXml[lineEnd] !== "\n") {
2145
+ lineEnd++;
2146
+ }
2147
+ const relevantXml = originalXml.substring(lineStart, lineEnd);
2148
+ return relevantXml;
2149
+ }
1477
2150
  // Annotate the CommonJS export names for ESM import in node:
1478
2151
  0 && (module.exports = {
2152
+ canonicalFunctionId,
2153
+ canonicalId,
2154
+ canonicalVarId,
1479
2155
  debugPrintExpr,
1480
2156
  parseVensimEquation,
1481
2157
  parseVensimExpr,
1482
2158
  parseVensimModel,
1483
2159
  parseVensimSubscriptRange,
2160
+ parseXmileDimensionDef,
2161
+ parseXmileModel,
2162
+ parseXmileVariableDef,
2163
+ preprocessVensimModel,
1484
2164
  prettyPrintExpr,
1485
2165
  printExprStats,
1486
2166
  reduceConditionals,