@sdeverywhere/compile 0.7.9 → 0.7.11

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.
@@ -3,11 +3,14 @@
3
3
  import path from 'path'
4
4
  import B from 'bufx'
5
5
 
6
+ import { parseVensimModel } from '@sdeverywhere/parse'
7
+
6
8
  import { readXlsx } from './_shared/helpers.js'
7
9
  import { readDat } from './_shared/read-dat.js'
8
10
  import { printSubscripts, yamlSubsList } from './_shared/subscript.js'
9
- import { parseModel } from './parse/parser.js'
11
+ import { parseModel as legacyParseVensimModel } from './parse/parser.js'
10
12
  import Model from './model/model.js'
13
+ import { getDirectSubscripts } from './model/read-subscripts.js'
11
14
  import { generateCode } from './generate/code-gen.js'
12
15
 
13
16
  /**
@@ -15,24 +18,25 @@ import { generateCode } from './generate/code-gen.js'
15
18
  *
16
19
  * This is the primary entrypoint for the `sde generate` command.
17
20
  *
18
- * - If `operation` is 'generateC', the generated C code will be written to `buildDir`.
19
- * - If `operation` is 'printVarList', variables and subscripts will be written to
21
+ * - If `operations` has 'generateC', the generated C code will be written to `buildDir`.
22
+ * - If `operations` has 'printVarList', variables and subscripts will be written to
20
23
  * txt, yaml, and json files under `buildDir`.
21
- * - If `operation` is 'printRefIdTest', reference identifiers will be printed to the console.
22
- * - If `operation` is 'convertNames', no output will be generated, but the results of model
24
+ * - If `operation` has 'printRefIdTest', reference identifiers will be printed to the console.
25
+ * - If `operation` has 'convertNames', no output will be generated, but the results of model
23
26
  * analysis will be available.
24
27
  *
25
28
  * @param input The preprocessed Vensim model text.
26
29
  * @param spec The model spec (from the JSON file).
27
- * @param operation Either 'generateC', 'printVarList', 'printRefIdTest', 'convertNames',
28
- * or empty string.
30
+ * @param operations The set of operations to perform; can include 'generateC', 'printVarList',
31
+ * 'printRefIdTest', 'convertNames'. If the array is empty, the model will be read but no
32
+ * operation will be performed.
29
33
  * @param modelDirname The absolute path to the directory containing the mdl file.
30
34
  * The dat and xlsx files referenced by the spec will be relative to this directory.
31
35
  * @param modelName The model name (without the mdl extension).
32
36
  * @param buildDir The output directory where the C or list files will be written.
33
37
  * @return A string containing the generated C code.
34
38
  */
35
- export async function parseAndGenerate(input, spec, operation, modelDirname, modelName, buildDir) {
39
+ export async function parseAndGenerate(input, spec, operations, modelDirname, modelName, buildDir) {
36
40
  // Read time series from external DAT files into a single object.
37
41
  // externalDatfiles is an array of either filenames or objects
38
42
  // giving a variable name prefix as the key and a filename as the value.
@@ -63,20 +67,20 @@ export async function parseAndGenerate(input, spec, operation, modelDirname, mod
63
67
  }
64
68
 
65
69
  // Parse the model and generate code.
66
- let parseTree = parseModel(input)
67
- let code = generateCode(parseTree, { spec, operation, extData, directData, modelDirname })
70
+ let parsedModel = parseModel(input, modelDirname)
71
+ let code = generateCode(parsedModel, { spec, operations, extData, directData, modelDirname })
68
72
 
69
73
  function writeOutput(filename, text) {
70
74
  let outputPathname = path.join(buildDir, filename)
71
75
  B.write(text, outputPathname)
72
76
  }
73
77
 
74
- if (operation === 'generateC') {
78
+ if (operations.includes('generateC')) {
75
79
  // Write the generated C to a file
76
80
  writeOutput(`${modelName}.c`, code)
77
81
  }
78
82
 
79
- if (operation === 'printVarList') {
83
+ if (operations.includes('printVarList')) {
80
84
  // Write variables to a text file.
81
85
  writeOutput(`${modelName}_vars.txt`, Model.printVarList())
82
86
  // Write subscripts to a text file.
@@ -114,3 +118,52 @@ export function printNames(namesPathname, operation) {
114
118
  }
115
119
  B.printBuf()
116
120
  }
121
+
122
+ /**
123
+ * Read and parse the given model text and return the parsed model structure.
124
+ *
125
+ * TODO: Fix return type
126
+ *
127
+ * @param {string} input The string containing the model text.
128
+ * @param {string} modelDir The absolute path to the directory containing the mdl file.
129
+ * The dat, xlsx, and csv files referenced by the model will be relative to this directory.
130
+ * @param {boolean} sort Whether to sort definitions alphabetically in the preprocess step.
131
+ * @return {*} A parsed tree representation of the model.
132
+ */
133
+ export function parseModel(input, modelDir, sort = false) {
134
+ if (process.env.SDE_NONPUBLIC_USE_NEW_PARSE !== '1') {
135
+ // Use the legacy parser
136
+ return {
137
+ kind: 'vensim-legacy',
138
+ parseTree: legacyParseVensimModel(input)
139
+ }
140
+ }
141
+
142
+ // Prepare the parse context that provides access to external data files
143
+ let parseContext /*: VensimParseContext*/
144
+ if (modelDir) {
145
+ parseContext = {
146
+ getDirectSubscripts(fileName, tabOrDelimiter, firstCell, lastCell /*, prefix*/) {
147
+ // Resolve the CSV file relative the model directory
148
+ const csvPath = path.resolve(modelDir, fileName)
149
+
150
+ // Read the subscripts from the CSV file
151
+ return getDirectSubscripts(csvPath, tabOrDelimiter, firstCell, lastCell)
152
+ }
153
+ }
154
+ }
155
+
156
+ // Parse the model
157
+ // TODO: The `parseVensimModel` function currently implicitly runs the preprocess
158
+ // step on the input text. We should make this configurable (because `parseModel`
159
+ // is currently called after the legacy preprocessor has already been run).
160
+ // TODO: We currently sort the preprocessed definitions alphabetically for
161
+ // compatibility with the legacy preprocessor. Once we drop the legacy code
162
+ // we could remove this step and update the tests to use the original order.
163
+ const root = parseVensimModel(input, parseContext, sort)
164
+
165
+ return {
166
+ kind: 'vensim',
167
+ root
168
+ }
169
+ }