@sdeverywhere/compile 0.7.6 → 0.7.8

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdeverywhere/compile",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "description": "The core Vensim to C compiler for the SDEverywhere tool suite.",
5
5
  "type": "module",
6
6
  "files": [
@@ -163,7 +163,17 @@ const char* getHeader() {
163
163
  }
164
164
 
165
165
  void storeOutputData() {
166
- ${outputSection(outputVars)}
166
+ ${specOutputSection(outputVars)}
167
+ }
168
+
169
+ void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t subIndex2) {
170
+ #if SDE_USE_OUTPUT_INDICES
171
+ switch (varIndex) {
172
+ ${fullOutputSection(Model.varIndexInfo())}
173
+ default:
174
+ break;
175
+ }
176
+ #endif
167
177
  }
168
178
  `
169
179
  }
@@ -250,11 +260,16 @@ ${postStep}
250
260
  }
251
261
  function internalVarsSection() {
252
262
  // Declare internal variables to run the model.
263
+ let decls
253
264
  if (outputAllVars) {
254
- return `const int numOutputs = ${expandedVarNames().length};`
265
+ decls = `const int numOutputs = ${expandedVarNames().length};`
255
266
  } else {
256
- return `const int numOutputs = ${spec.outputVars.length};`
267
+ decls = `const int numOutputs = ${spec.outputVars.length};`
257
268
  }
269
+ decls += `\n#define SDE_USE_OUTPUT_INDICES 0`
270
+ decls += `\n#define SDE_MAX_OUTPUT_INDICES 1000`
271
+ decls += `\nconst int maxOutputIndices = SDE_USE_OUTPUT_INDICES ? SDE_MAX_OUTPUT_INDICES : 0;`
272
+ return decls
258
273
  }
259
274
  function arrayDimensionsSection() {
260
275
  // Emit a declaration for each array dimension's index numbers.
@@ -312,12 +327,34 @@ ${postStep}
312
327
  //
313
328
  // Input/output section helpers
314
329
  //
315
- function outputSection(varNames) {
330
+ function specOutputSection(varNames) {
316
331
  // Emit output calls using varNames in C format.
317
332
  let code = R.map(varName => ` outputVar(${varName});`)
318
333
  let section = R.pipe(code, lines)
319
334
  return section(varNames)
320
335
  }
336
+ function fullOutputSection(varIndexInfo) {
337
+ // Emit output calls for all variables.
338
+ const code = R.map(info => {
339
+ let varAccess = info.varName
340
+ if (info.subscriptCount > 0) {
341
+ varAccess += '[subIndex0]'
342
+ }
343
+ if (info.subscriptCount > 1) {
344
+ varAccess += '[subIndex1]'
345
+ }
346
+ if (info.subscriptCount > 2) {
347
+ varAccess += '[subIndex2]'
348
+ }
349
+ let c = ''
350
+ c += ` case ${info.varIndex}:\n`
351
+ c += ` outputVar(${varAccess});\n`
352
+ c += ` break;`
353
+ return c
354
+ })
355
+ const section = R.pipe(code, lines)
356
+ return section(varIndexInfo)
357
+ }
321
358
  function inputsFromStringImpl() {
322
359
  // If there was an I/O spec file, then emit code to parse input variables.
323
360
  // The user can replace this with a parser for a different serialization format.
@@ -330,8 +330,14 @@ function removeUnusedVariables(spec) {
330
330
  if (!referencedRefIds.has(refId)) {
331
331
  referencedRefIds.add(refId)
332
332
  const refVar = varWithRefId(refId)
333
- recordUsedVariable(refVar)
334
- recordRefsOfVariable(refVar)
333
+ if (refVar) {
334
+ recordUsedVariable(refVar)
335
+ recordRefsOfVariable(refVar)
336
+ } else {
337
+ console.error(`No var found for ${refId}`)
338
+ console.error(v)
339
+ process.exit(1)
340
+ }
335
341
  }
336
342
  }
337
343
  }
@@ -1010,6 +1016,111 @@ function printDepsGraph(graph, varType) {
1010
1016
  console.error(`${dep[0]} → ${dep[1]}`)
1011
1017
  }
1012
1018
  }
1019
+
1020
+ function allListedVars() {
1021
+ // Put variables into the order that they are evaluated by SDE in the generated model
1022
+ let vars = []
1023
+ vars.push(...constVars())
1024
+ vars.push(...lookupVars())
1025
+ vars.push(...dataVars())
1026
+ // The special exogenous `Time` variable may have already been removed by
1027
+ // `removeUnusedVariables` if it is not referenced explicitly in the model,
1028
+ // so we will only include it in the listing if it is defined here
1029
+ const timeVar = varWithName('_time')
1030
+ if (timeVar) {
1031
+ vars.push(timeVar)
1032
+ }
1033
+ vars.push(...initVars())
1034
+ vars.push(...auxVars())
1035
+ // TODO: Also levelVars not covered by initVars?
1036
+
1037
+ // Filter out data/lookup variables and variables that are generated/used internally
1038
+ const isInternal = v => {
1039
+ return v.refId.startsWith('__level') || v.refId.startsWith('__aux')
1040
+ }
1041
+
1042
+ return R.filter(v => !isInternal(v), vars)
1043
+ }
1044
+
1045
+ function filteredListedVars() {
1046
+ // Extract a subset of the available info for each variable and sort all variables
1047
+ // according to the order that they are evaluated by SDE in the generated model
1048
+ return R.map(v => filterVar(v), allListedVars())
1049
+ }
1050
+
1051
+ function varIndexInfoMap() {
1052
+ // Return a map containing information for each listed variable:
1053
+ // varName
1054
+ // varIndex
1055
+ // subscriptCount
1056
+
1057
+ // Get the filtered variables in the order that they are evaluated by SDE in the
1058
+ // generated model
1059
+ const sortedVars = filteredListedVars()
1060
+
1061
+ // Get the set of unique variable names, and assign a 1-based index
1062
+ // to each; this matches the index number used in `storeOutput()`
1063
+ // in the generated C code
1064
+ const infoMap = new Map()
1065
+ let varIndex = 1
1066
+ for (const v of sortedVars) {
1067
+ if (v.varType === 'data' || v.varType === 'lookup') {
1068
+ // Omit the index for data and lookup variables; at this time, the data for these
1069
+ // cannot be output like for other types of variables
1070
+ continue
1071
+ }
1072
+ const varName = v.varName
1073
+ if (!infoMap.get(varName)) {
1074
+ infoMap.set(varName, {
1075
+ varName,
1076
+ varIndex,
1077
+ subscriptCount: v.families ? v.families.length : 0
1078
+ })
1079
+ varIndex++
1080
+ }
1081
+ }
1082
+
1083
+ return infoMap
1084
+ }
1085
+
1086
+ function varIndexInfo() {
1087
+ // Return an array, sorted by `varName`, containing information for each
1088
+ // listed variable:
1089
+ // varName
1090
+ // varIndex
1091
+ // subscriptCount
1092
+ return Array.from(varIndexInfoMap().values())
1093
+ }
1094
+
1095
+ function jsonList() {
1096
+ // Return a stringified JSON object containing variable and subscript information
1097
+ // for the model.
1098
+
1099
+ // Get the set of available subscripts
1100
+ const allDims = [...allDimensions()]
1101
+ const sortedDims = allDims.sort((a, b) => a.name.localeCompare(b.name))
1102
+
1103
+ // Extract a subset of the available info for each variable and put them in eval order
1104
+ const sortedVars = filteredListedVars()
1105
+
1106
+ // Assign a 1-based index for each variable that has data that can be accessed.
1107
+ // This matches the index number used in `storeOutput()` in the generated C code.
1108
+ const infoMap = varIndexInfoMap()
1109
+ for (const v of sortedVars) {
1110
+ const varInfo = infoMap.get(v.varName)
1111
+ if (varInfo) {
1112
+ v.varIndex = varInfo.varIndex
1113
+ }
1114
+ }
1115
+
1116
+ // Convert to JSON
1117
+ const obj = {
1118
+ dimensions: sortedDims,
1119
+ variables: sortedVars
1120
+ }
1121
+ return JSON.stringify(obj, null, 2)
1122
+ }
1123
+
1013
1124
  export default {
1014
1125
  addConstantExpr,
1015
1126
  addEquation,
@@ -1026,6 +1137,7 @@ export default {
1026
1137
  initVars,
1027
1138
  isInputVar,
1028
1139
  isNonAtoAName,
1140
+ jsonList,
1029
1141
  levelVars,
1030
1142
  lookupVars,
1031
1143
  printRefGraph,
@@ -1036,6 +1148,7 @@ export default {
1036
1148
  refIdsWithName,
1037
1149
  splitRefId,
1038
1150
  variables,
1151
+ varIndexInfo,
1039
1152
  varNames,
1040
1153
  varsWithName,
1041
1154
  varWithName,
@@ -17,7 +17,7 @@ import { generateCode } from './generate/code-gen.js'
17
17
  *
18
18
  * - If `operation` is 'generateC', the generated C code will be written to `buildDir`.
19
19
  * - If `operation` is 'printVarList', variables and subscripts will be written to
20
- * txt and yaml files under `buildDir`.
20
+ * txt, yaml, and json files under `buildDir`.
21
21
  * - If `operation` is 'printRefIdTest', reference identifiers will be printed to the console.
22
22
  * - If `operation` is 'convertNames', no output will be generated, but the results of model
23
23
  * analysis will be available.
@@ -85,6 +85,8 @@ export async function parseAndGenerate(input, spec, operation, modelDirname, mod
85
85
  writeOutput(`${modelName}_vars.yaml`, Model.yamlVarList())
86
86
  // Write subscripts to a YAML file.
87
87
  writeOutput(`${modelName}_subs.yaml`, yamlSubsList())
88
+ // Write variables and subscripts to a JSON file.
89
+ writeOutput(`${modelName}.json`, Model.jsonList())
88
90
  }
89
91
 
90
92
  return code