@sdeverywhere/compile 0.7.22 → 0.7.24
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/package.json
CHANGED
package/src/_shared/helpers.js
CHANGED
|
@@ -24,6 +24,8 @@ let nextLevelVarSeq = 1
|
|
|
24
24
|
let nextAuxVarSeq = 1
|
|
25
25
|
// parsed csv data cache
|
|
26
26
|
let csvData = new Map()
|
|
27
|
+
// parsed xlsx data cache
|
|
28
|
+
let xlsxData = new Map()
|
|
27
29
|
|
|
28
30
|
// Newer versions of the xlsx package require manually setting the `fs` instance
|
|
29
31
|
// before using the `XLSX.readFile` function
|
|
@@ -38,6 +40,7 @@ export function resetHelperState() {
|
|
|
38
40
|
nextLevelVarSeq = 1
|
|
39
41
|
nextAuxVarSeq = 1
|
|
40
42
|
csvData.clear()
|
|
43
|
+
xlsxData.clear()
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export let canonicalName = name => {
|
|
@@ -200,7 +203,15 @@ export let isIterable = obj => {
|
|
|
200
203
|
}
|
|
201
204
|
// Command helpers
|
|
202
205
|
export let readXlsx = pathname => {
|
|
203
|
-
|
|
206
|
+
// Read the XLSX file at the pathname and parse it.
|
|
207
|
+
// Return a `XLSX.WorkBook` object that can be used to access the data.
|
|
208
|
+
// Cache parsed files to support multiple reads from different equations.
|
|
209
|
+
let xlsx = xlsxData.get(pathname)
|
|
210
|
+
if (!xlsx) {
|
|
211
|
+
xlsx = XLSX.readFile(pathname, { cellDates: true })
|
|
212
|
+
xlsxData.set(pathname, xlsx)
|
|
213
|
+
}
|
|
214
|
+
return xlsx
|
|
204
215
|
}
|
|
205
216
|
export let readCsv = (pathname, delimiter = ',') => {
|
|
206
217
|
// Read the CSV file at the pathname and parse it with the given delimiter.
|
|
@@ -208,7 +219,7 @@ export let readCsv = (pathname, delimiter = ',') => {
|
|
|
208
219
|
// If there is a header row, it is returned as the first row.
|
|
209
220
|
// Cache parsed files to support multiple reads from different equations.
|
|
210
221
|
let csv = csvData.get(pathname)
|
|
211
|
-
if (csv
|
|
222
|
+
if (!csv) {
|
|
212
223
|
const CSV_PARSE_OPTS = {
|
|
213
224
|
delimiter,
|
|
214
225
|
columns: false,
|
package/src/model/model.js
CHANGED
|
@@ -71,6 +71,10 @@ function read(parsedModel, spec, extData, directData, modelDirname, opts) {
|
|
|
71
71
|
// Some arrays need to be separated into variables with individual indices to
|
|
72
72
|
// prevent eval cycles. They are manually added to the spec file.
|
|
73
73
|
let specialSeparationDims = spec.specialSeparationDims
|
|
74
|
+
// All arrays that have one of the specified dimension ID lists are
|
|
75
|
+
// separated on those dimensions. This allows variables to be separated
|
|
76
|
+
// without listing each one.
|
|
77
|
+
let separateAllVarsWithDims = spec.separateAllVarsWithDims
|
|
74
78
|
|
|
75
79
|
// Dimensions must be defined before reading variables that use them.
|
|
76
80
|
readDimensionDefs(parsedModel, modelDirname)
|
|
@@ -79,7 +83,7 @@ function read(parsedModel, spec, extData, directData, modelDirname, opts) {
|
|
|
79
83
|
if (opts?.stopAfterResolveSubscripts) return
|
|
80
84
|
|
|
81
85
|
// Read variables from the model parse tree.
|
|
82
|
-
const vars = readVariables(parsedModel, specialSeparationDims)
|
|
86
|
+
const vars = readVariables(parsedModel, specialSeparationDims, separateAllVarsWithDims)
|
|
83
87
|
|
|
84
88
|
// Include a placeholder variable for the exogenous `Time` variable
|
|
85
89
|
const timeVar = new Variable()
|
|
@@ -114,6 +114,9 @@ class Context {
|
|
|
114
114
|
const parsedModel = { kind: 'vensim', root: parseVensimModel(eqnText) }
|
|
115
115
|
|
|
116
116
|
// Create one or more `Variable` instances from the equations
|
|
117
|
+
// TODO: Technically we should pass `spec.separateAllVarsWithDims` here so that any
|
|
118
|
+
// generated apply-to-all variables will be separated according to the spec like other
|
|
119
|
+
// normal variables
|
|
117
120
|
const vars = readVariables(parsedModel)
|
|
118
121
|
|
|
119
122
|
// Add the variables to the `Model`
|
|
@@ -12,17 +12,21 @@ import Variable from './variable.js'
|
|
|
12
12
|
*
|
|
13
13
|
* @param {*} parsedModel TODO: Use ParsedVensimModel type here
|
|
14
14
|
* @param {Object.<string, string>} [specialSeparationDims] The variable names that need to be
|
|
15
|
-
* separated because of circular references.
|
|
16
|
-
* name to separate on.
|
|
15
|
+
* separated because of circular references. This is an optional mapping from "C" variable name
|
|
16
|
+
* to "C" dimension name to separate on.
|
|
17
|
+
* @param {string[][]} [separateAllVarsWithDims] An optional array containing arrays of dimension
|
|
18
|
+
* identifiers for which variables will be separated. For example, if the LHS is `x[DimA,DimB,DimC]`
|
|
19
|
+
* and this array contains a list `['_dima', '_dimb']`, then the LHS will be separated on both
|
|
20
|
+
* DimA and DimB.
|
|
17
21
|
* @returns {*} An array containing all `Variable` instances that were generated from
|
|
18
22
|
* the model equations.
|
|
19
23
|
*/
|
|
20
|
-
export function readVariables(parsedModel, specialSeparationDims) {
|
|
24
|
+
export function readVariables(parsedModel, specialSeparationDims, separateAllVarsWithDims) {
|
|
21
25
|
const variables = []
|
|
22
26
|
|
|
23
27
|
// Add one or more `Variable` definitions for each parsed equation
|
|
24
28
|
for (const eqn of parsedModel.root.equations) {
|
|
25
|
-
variables.push(...variablesForEquation(eqn, specialSeparationDims || {}))
|
|
29
|
+
variables.push(...variablesForEquation(eqn, specialSeparationDims || {}, separateAllVarsWithDims || []))
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
return variables
|
|
@@ -37,10 +41,12 @@ export function readVariables(parsedModel, specialSeparationDims) {
|
|
|
37
41
|
* @param {*} eqn The parsed equation.
|
|
38
42
|
* @param {Object.<string, string>} specialSeparationDims The variable names that need to be
|
|
39
43
|
* separated because of circular references.
|
|
44
|
+
* @param {string[][]} separateAllVarsWithDims An array containing arrays of dimension
|
|
45
|
+
* identifiers for which variables will be separated.
|
|
40
46
|
* @returns {*} An array containing all `Variable` instances that were generated from
|
|
41
47
|
* the given equation.
|
|
42
48
|
*/
|
|
43
|
-
function variablesForEquation(eqn, specialSeparationDims) {
|
|
49
|
+
function variablesForEquation(eqn, specialSeparationDims, separateAllVarsWithDims) {
|
|
44
50
|
// Start a new variable defined by this equation
|
|
45
51
|
const variable = new Variable()
|
|
46
52
|
|
|
@@ -115,6 +121,23 @@ function variablesForEquation(eqn, specialSeparationDims) {
|
|
|
115
121
|
if (!Array.isArray(separationDims)) {
|
|
116
122
|
separationDims = [separationDims]
|
|
117
123
|
}
|
|
124
|
+
// Alternatively, if the variable was not in `specialSeparationDims`, separate
|
|
125
|
+
// on dims from `separateAllVarsWithDims` if the var matches one of the dim lists.
|
|
126
|
+
if (separationDims.length === 0) {
|
|
127
|
+
for (let dimList of separateAllVarsWithDims) {
|
|
128
|
+
// Technically we allow each entry in the spec array to be either a single
|
|
129
|
+
// dim ID string or an array of dim IDs, so convert to array if needed
|
|
130
|
+
if (!Array.isArray(dimList)) {
|
|
131
|
+
dimList = [dimList]
|
|
132
|
+
}
|
|
133
|
+
// The list entry from the spec is only considered a match if every dimension
|
|
134
|
+
// in the spec list appears on the LHS
|
|
135
|
+
if (dimList.every(dim => subIds.includes(dim))) {
|
|
136
|
+
separationDims = dimList
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
118
141
|
positionsToExpand = subscriptPositionsToExpand(subIds, exceptSubIdSets, separationDims, variable.modelFormula)
|
|
119
142
|
}
|
|
120
143
|
|