@sdeverywhere/parse 0.1.2 → 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,8 +27,8 @@ 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
32
  canonicalFunctionId: () => canonicalFunctionId,
33
33
  canonicalId: () => canonicalId,
34
34
  canonicalVarId: () => canonicalVarId,
@@ -37,6 +37,9 @@ __export(src_exports, {
37
37
  parseVensimExpr: () => parseVensimExpr,
38
38
  parseVensimModel: () => parseVensimModel,
39
39
  parseVensimSubscriptRange: () => parseVensimSubscriptRange,
40
+ parseXmileDimensionDef: () => parseXmileDimensionDef,
41
+ parseXmileModel: () => parseXmileModel,
42
+ parseXmileVariableDef: () => parseXmileVariableDef,
40
43
  preprocessVensimModel: () => preprocessVensimModel,
41
44
  prettyPrintExpr: () => prettyPrintExpr,
42
45
  printExprStats: () => printExprStats,
@@ -44,7 +47,7 @@ __export(src_exports, {
44
47
  reduceExpr: () => reduceExpr,
45
48
  toPrettyString: () => toPrettyString
46
49
  });
47
- module.exports = __toCommonJS(src_exports);
50
+ module.exports = __toCommonJS(index_exports);
48
51
 
49
52
  // src/_shared/canonical-id.js
50
53
  var reTrailingMark = new RegExp("\\s+!$", "g");
@@ -318,6 +321,12 @@ function fullIdForVarRef(varRef) {
318
321
  var import_assert_never2 = require("assert-never");
319
322
 
320
323
  // src/ast/ast-builders.ts
324
+ function subRef(dimOrSubName) {
325
+ return {
326
+ subName: dimOrSubName,
327
+ subId: canonicalId(dimOrSubName)
328
+ };
329
+ }
321
330
  function num(value, text) {
322
331
  return {
323
332
  kind: "number",
@@ -346,6 +355,13 @@ function parens(expr) {
346
355
  expr
347
356
  };
348
357
  }
358
+ function lookupDef(points, range) {
359
+ return {
360
+ kind: "lookup-def",
361
+ range,
362
+ points
363
+ };
364
+ }
349
365
  function lookupCall(varRef, arg) {
350
366
  return {
351
367
  kind: "lookup-call",
@@ -353,6 +369,14 @@ function lookupCall(varRef, arg) {
353
369
  arg
354
370
  };
355
371
  }
372
+ function call(fnName, ...args) {
373
+ return {
374
+ kind: "function-call",
375
+ fnName,
376
+ fnId: canonicalFunctionId(fnName),
377
+ args
378
+ };
379
+ }
356
380
 
357
381
  // src/ast/reduce-expr.ts
358
382
  function reduceExpr(expr, opts) {
@@ -1539,6 +1563,590 @@ Detail:
1539
1563
  equations
1540
1564
  };
1541
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
+ }
1542
2150
  // Annotate the CommonJS export names for ESM import in node:
1543
2151
  0 && (module.exports = {
1544
2152
  canonicalFunctionId,
@@ -1549,6 +2157,9 @@ Detail:
1549
2157
  parseVensimExpr,
1550
2158
  parseVensimModel,
1551
2159
  parseVensimSubscriptRange,
2160
+ parseXmileDimensionDef,
2161
+ parseXmileModel,
2162
+ parseXmileVariableDef,
1552
2163
  preprocessVensimModel,
1553
2164
  prettyPrintExpr,
1554
2165
  printExprStats,