@sdeverywhere/cli 0.7.27 → 0.7.29
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/README.md +4 -25
- package/package.json +3 -3
- package/src/c/main.c +1 -1
- package/src/c/makefile +1 -1
- package/src/c/vensim.c +76 -22
- package/src/c/vensim.h +32 -3
- package/src/sde-build.js +2 -1
- package/src/sde-causes.js +7 -14
- package/src/sde-flatten.js +17 -10
- package/src/sde-generate.js +34 -15
- package/src/sde-names.js +4 -3
- package/src/sde-test.js +2 -1
package/README.md
CHANGED
|
@@ -97,7 +97,10 @@ sde bundle [--config sde.config.js] [--verbose]
|
|
|
97
97
|
sde generate --list {model}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
#### Preprocess a model to remove macros and tabbed arrays
|
|
100
|
+
#### Preprocess a model to remove macros and tabbed arrays
|
|
101
|
+
|
|
102
|
+
_NOTE:_ For most users it is not necessary to run the preprocessor manually
|
|
103
|
+
because the transpiler runs the preprocessor automatically prior to parsing.
|
|
101
104
|
|
|
102
105
|
```
|
|
103
106
|
sde generate --preprocess {model}
|
|
@@ -204,21 +207,6 @@ There is an example in the `directdata` sample model.
|
|
|
204
207
|
}
|
|
205
208
|
```
|
|
206
209
|
|
|
207
|
-
#### Specify equations to remove from the model
|
|
208
|
-
|
|
209
|
-
When SDEverywhere cannot handle a certain Vensim construct yet, you will need to remove equations that use the construct from the model, convert it by hand into something that SDEverywhere can handle, and then insert it back into the model.
|
|
210
|
-
To have the preprocessor remove equations from the model into a `removals.txt` file, specify substrings to match in the equation in the `removalKeys` section.
|
|
211
|
-
Macros and `TABBED ARRAY` equations are already automatically removed by the preprocessor.
|
|
212
|
-
|
|
213
|
-
For instance, you could key on the variable name in the equation definition.
|
|
214
|
-
|
|
215
|
-
```json
|
|
216
|
-
"removalKeys": [
|
|
217
|
-
"varname1 =",
|
|
218
|
-
"varname2 ="
|
|
219
|
-
]
|
|
220
|
-
```
|
|
221
|
-
|
|
222
210
|
#### Generating, compiling, running, and testing the C code
|
|
223
211
|
|
|
224
212
|
To generate C code using the `--spec` argument, enter the following command:
|
|
@@ -260,15 +248,6 @@ That is, the values are separated by spaces, and each pair has an index number,
|
|
|
260
248
|
The zero-based index maps into a static array of input variable pointers held in the function.
|
|
261
249
|
These are used to set the value directly into the static `double` variable in the generated code.
|
|
262
250
|
|
|
263
|
-
#### Inserting a file into the model
|
|
264
|
-
|
|
265
|
-
Some constructs like macros are not supported by SDEverywhere.
|
|
266
|
-
They are removed from the model by the preprocessor into the `removals.txt` file.
|
|
267
|
-
You can edit these constructs into a form that SDEverywhere supports and insert them back into the model.
|
|
268
|
-
Create a file called `mdl-edits.txt` in the model directory with the constructs to insert.
|
|
269
|
-
For instance, manually expand macros and place them into the `mdl-edits.txt` file.
|
|
270
|
-
The preprocessor will read this file and insert its contents unchanged into the beginning of the model.
|
|
271
|
-
|
|
272
251
|
## License
|
|
273
252
|
|
|
274
253
|
SDEverywhere is distributed under the MIT license. See `LICENSE` for more details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdeverywhere/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.29",
|
|
4
4
|
"description": "Contains the `sde` command line interface for the SDEverywhere tool suite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"sde": "src/main.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@sdeverywhere/build": "^0.3.
|
|
15
|
-
"@sdeverywhere/compile": "^0.7.
|
|
14
|
+
"@sdeverywhere/build": "^0.3.7",
|
|
15
|
+
"@sdeverywhere/compile": "^0.7.22",
|
|
16
16
|
"bufx": "^1.0.5",
|
|
17
17
|
"byline": "^5.0.0",
|
|
18
18
|
"ramda": "^0.27.0",
|
package/src/c/main.c
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
int main(int argc, char** argv) {
|
|
4
4
|
// TODO make the input buffer size dynamic
|
|
5
|
-
char inputs[
|
|
5
|
+
char inputs[500000];
|
|
6
6
|
// When true, output data without newlines or a header, suitable for embedding reference data.
|
|
7
7
|
bool raw_output = false;
|
|
8
8
|
// When true, suppress data output when using PR* macros.
|
package/src/c/makefile
CHANGED
package/src/c/vensim.c
CHANGED
|
@@ -56,25 +56,79 @@ double _ZIDZ(double a, double b) {
|
|
|
56
56
|
Lookup* __new_lookup(size_t size, bool copy, double* data) {
|
|
57
57
|
// Make a new Lookup with "size" number of pairs given in x, y order in a flattened list.
|
|
58
58
|
Lookup* lookup = malloc(sizeof(Lookup));
|
|
59
|
-
lookup->
|
|
60
|
-
lookup->
|
|
61
|
-
lookup->data_is_owned = copy;
|
|
59
|
+
lookup->original_size = size;
|
|
60
|
+
lookup->original_data_is_owned = copy;
|
|
62
61
|
if (copy) {
|
|
63
|
-
// Copy
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
// Copy the given lookup data into an internally managed buffer.
|
|
63
|
+
size_t data_length_in_bytes = size * 2 * sizeof(double);
|
|
64
|
+
lookup->original_data = malloc(data_length_in_bytes);
|
|
65
|
+
memcpy(lookup->original_data, data, data_length_in_bytes);
|
|
66
66
|
} else {
|
|
67
67
|
// Store a pointer to the lookup data (assumed to be static or owned elsewhere).
|
|
68
|
-
lookup->
|
|
68
|
+
lookup->original_data = data;
|
|
69
69
|
}
|
|
70
|
+
// Set the original data as "active".
|
|
71
|
+
lookup->active_data = lookup->original_data;
|
|
72
|
+
lookup->active_size = lookup->original_size;
|
|
73
|
+
// Set `dynamic_data` to NULL initially; it will be allocated on demand if lookup
|
|
74
|
+
// data is overridden at runtime using `__set_lookup`.
|
|
75
|
+
lookup->dynamic_data = NULL;
|
|
76
|
+
lookup->dynamic_data_length = 0;
|
|
77
|
+
lookup->dynamic_size = 0;
|
|
78
|
+
// Set `inverted_data` to NULL initially; it will be allocated on demand in case
|
|
79
|
+
// of `LOOKUP INVERT` calls.
|
|
80
|
+
lookup->inverted_data = NULL;
|
|
81
|
+
// Set the cached "last" values to the initial values.
|
|
70
82
|
lookup->last_input = DBL_MAX;
|
|
71
83
|
lookup->last_hit_index = 0;
|
|
72
84
|
return lookup;
|
|
73
85
|
}
|
|
86
|
+
void __set_lookup(Lookup* lookup, size_t size, double* data) {
|
|
87
|
+
// Set new data for the given `Lookup`. If `data` is NULL, the original data that was
|
|
88
|
+
// supplied to the `__new_lookup` call will be restored as the "active" data. Otherwise,
|
|
89
|
+
// `data` will be copied to an internal data buffer, which will be the "active" data.
|
|
90
|
+
// If `size` is greater than the size passed to previous calls, the internal data buffer
|
|
91
|
+
// will be grown as needed.
|
|
92
|
+
if (lookup == NULL) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (data != NULL) {
|
|
96
|
+
// Allocate or grow the internal buffer as needed.
|
|
97
|
+
size_t data_length_in_elems = size * 2;
|
|
98
|
+
size_t data_length_in_bytes = data_length_in_elems * sizeof(double);
|
|
99
|
+
if (data_length_in_elems > lookup->dynamic_data_length) {
|
|
100
|
+
lookup->dynamic_data = malloc(data_length_in_bytes);
|
|
101
|
+
lookup->dynamic_data_length = data_length_in_elems;
|
|
102
|
+
}
|
|
103
|
+
// Copy the given lookup data into the internally managed buffer.
|
|
104
|
+
lookup->dynamic_size = size;
|
|
105
|
+
if (data_length_in_bytes > 0) {
|
|
106
|
+
memcpy(lookup->dynamic_data, data, data_length_in_bytes);
|
|
107
|
+
}
|
|
108
|
+
// Set the dynamic data as the "active" data.
|
|
109
|
+
lookup->active_data = lookup->dynamic_data;
|
|
110
|
+
lookup->active_size = lookup->dynamic_size;
|
|
111
|
+
} else {
|
|
112
|
+
// Restore the original data as the "active" data.
|
|
113
|
+
lookup->active_data = lookup->original_data;
|
|
114
|
+
lookup->active_size = lookup->original_size;
|
|
115
|
+
}
|
|
116
|
+
// Clear the cached inverted data, if needed.
|
|
117
|
+
if (lookup->inverted_data) {
|
|
118
|
+
free(lookup->inverted_data);
|
|
119
|
+
lookup->inverted_data = NULL;
|
|
120
|
+
}
|
|
121
|
+
// Reset the cached "last" values to the initial values.
|
|
122
|
+
lookup->last_input = DBL_MAX;
|
|
123
|
+
lookup->last_hit_index = 0;
|
|
124
|
+
}
|
|
74
125
|
void __delete_lookup(Lookup* lookup) {
|
|
75
126
|
if (lookup) {
|
|
76
|
-
if (lookup->
|
|
77
|
-
free(lookup->
|
|
127
|
+
if (lookup->original_data && lookup->original_data_is_owned) {
|
|
128
|
+
free(lookup->original_data);
|
|
129
|
+
}
|
|
130
|
+
if (lookup->dynamic_data) {
|
|
131
|
+
free(lookup->dynamic_data);
|
|
78
132
|
}
|
|
79
133
|
if (lookup->inverted_data) {
|
|
80
134
|
free(lookup->inverted_data);
|
|
@@ -84,8 +138,8 @@ void __delete_lookup(Lookup* lookup) {
|
|
|
84
138
|
}
|
|
85
139
|
void __print_lookup(Lookup* lookup) {
|
|
86
140
|
if (lookup) {
|
|
87
|
-
for (size_t i = 0; i < lookup->
|
|
88
|
-
printf("(%g, %g)\n", *(lookup->
|
|
141
|
+
for (size_t i = 0; i < lookup->active_size; i++) {
|
|
142
|
+
printf("(%g, %g)\n", *(lookup->active_data + 2 * i), *(lookup->active_data + 2 * i + 1));
|
|
89
143
|
}
|
|
90
144
|
}
|
|
91
145
|
}
|
|
@@ -94,12 +148,12 @@ double __lookup(Lookup* lookup, double input, bool use_inverted_data, LookupMode
|
|
|
94
148
|
// Interpolate the y value from an array of (x,y) pairs.
|
|
95
149
|
// NOTE: The x values are assumed to be monotonically increasing.
|
|
96
150
|
|
|
97
|
-
if (lookup == NULL || lookup->
|
|
151
|
+
if (lookup == NULL || lookup->active_size == 0) {
|
|
98
152
|
return _NA_;
|
|
99
153
|
}
|
|
100
154
|
|
|
101
|
-
const double* data = use_inverted_data ? lookup->inverted_data : lookup->
|
|
102
|
-
const size_t max = (lookup->
|
|
155
|
+
const double* data = use_inverted_data ? lookup->inverted_data : lookup->active_data;
|
|
156
|
+
const size_t max = (lookup->active_size) * 2;
|
|
103
157
|
|
|
104
158
|
// Use the cached values for improved lookup performance, except in the case
|
|
105
159
|
// of `LOOKUP INVERT` (since it may not be accurate if calls flip back and forth
|
|
@@ -166,12 +220,12 @@ double __get_data_between_times(Lookup* lookup, double input, LookupMode mode) {
|
|
|
166
220
|
// Interpolate the y value from an array of (x,y) pairs.
|
|
167
221
|
// NOTE: The x values are assumed to be monotonically increasing.
|
|
168
222
|
|
|
169
|
-
if (lookup == NULL || lookup->
|
|
223
|
+
if (lookup == NULL || lookup->active_size == 0) {
|
|
170
224
|
return _NA_;
|
|
171
225
|
}
|
|
172
226
|
|
|
173
|
-
const double* data = lookup->
|
|
174
|
-
const size_t n = lookup->
|
|
227
|
+
const double* data = lookup->active_data;
|
|
228
|
+
const size_t n = lookup->active_size;
|
|
175
229
|
const size_t max = n * 2;
|
|
176
230
|
|
|
177
231
|
switch (mode) {
|
|
@@ -248,10 +302,10 @@ double __get_data_between_times(Lookup* lookup, double input, LookupMode mode) {
|
|
|
248
302
|
double _LOOKUP_INVERT(Lookup* lookup, double y) {
|
|
249
303
|
if (lookup->inverted_data == NULL) {
|
|
250
304
|
// Invert the matrix and cache it.
|
|
251
|
-
lookup->inverted_data = malloc(sizeof(double) * 2 * lookup->
|
|
252
|
-
double* pLookup = lookup->
|
|
305
|
+
lookup->inverted_data = malloc(sizeof(double) * 2 * lookup->active_size);
|
|
306
|
+
double* pLookup = lookup->active_data;
|
|
253
307
|
double* pInvert = lookup->inverted_data;
|
|
254
|
-
for (size_t i = 0; i < lookup->
|
|
308
|
+
for (size_t i = 0; i < lookup->active_size; i++) {
|
|
255
309
|
*pInvert++ = *(pLookup + 1);
|
|
256
310
|
*pInvert++ = *pLookup;
|
|
257
311
|
pLookup += 2;
|
|
@@ -261,12 +315,12 @@ double _LOOKUP_INVERT(Lookup* lookup, double y) {
|
|
|
261
315
|
}
|
|
262
316
|
|
|
263
317
|
double _GAME(Lookup* lookup, double default_value) {
|
|
264
|
-
if (lookup == NULL || lookup->
|
|
318
|
+
if (lookup == NULL || lookup->active_size == 0) {
|
|
265
319
|
// The lookup is NULL or empty, so return the default value
|
|
266
320
|
return default_value;
|
|
267
321
|
}
|
|
268
322
|
|
|
269
|
-
double x0 = lookup->
|
|
323
|
+
double x0 = lookup->active_data[0];
|
|
270
324
|
if (_time < x0) {
|
|
271
325
|
// The current time is earlier than the first data point, so return the
|
|
272
326
|
// default value
|
package/src/c/vensim.h
CHANGED
|
@@ -54,15 +54,44 @@ typedef enum {
|
|
|
54
54
|
} LookupMode;
|
|
55
55
|
|
|
56
56
|
typedef struct {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
// The pointer to the active data buffer. This will be the same as either
|
|
58
|
+
// `original_data` or `dynamic_data`, depending on whether the lookup data
|
|
59
|
+
// is overridden at runtime using `__set_lookup`.
|
|
60
|
+
double* active_data;
|
|
61
|
+
// The size (i.e., number of pairs) of the active data.
|
|
62
|
+
size_t active_size;
|
|
63
|
+
|
|
64
|
+
// The pointer to the original data buffer.
|
|
65
|
+
double* original_data;
|
|
66
|
+
// The size (i.e., number of pairs) of the original data.
|
|
67
|
+
size_t original_size;
|
|
68
|
+
// Whether the `original_data` is owned by this `Lookup` instance. If `true`,
|
|
69
|
+
// the `original_data` buffer will be freed by `__delete_lookup`.
|
|
70
|
+
bool original_data_is_owned;
|
|
71
|
+
|
|
72
|
+
// The pointer to the dynamic data buffer. This will be NULL initially,
|
|
73
|
+
// and the buffer will be allocated (or grown) by `__set_lookup`.
|
|
74
|
+
double* dynamic_data;
|
|
75
|
+
// The size (i.e., number of pairs) of the dynamic data.
|
|
76
|
+
size_t dynamic_size;
|
|
77
|
+
// The number of elements in the dynamic data buffer. The buffer will grow
|
|
78
|
+
// as needed, so this can be greater than `2 * dynamic_size`.
|
|
79
|
+
size_t dynamic_data_length;
|
|
80
|
+
|
|
81
|
+
// The inverted version of the active data buffer. This is allocated on demand
|
|
82
|
+
// only in the case of `LOOKUP INVERT` function calls.
|
|
59
83
|
double* inverted_data;
|
|
60
|
-
|
|
84
|
+
|
|
85
|
+
// The input value for the last hit. This is cached for performance so that we
|
|
86
|
+
// can reduce the amount of linear searching in the common case where `LOOKUP`
|
|
87
|
+
// input values are monotonically increasing.
|
|
61
88
|
double last_input;
|
|
89
|
+
// The index for the last hit (see `last_input`).
|
|
62
90
|
size_t last_hit_index;
|
|
63
91
|
} Lookup;
|
|
64
92
|
|
|
65
93
|
Lookup* __new_lookup(size_t size, bool copy, double* data);
|
|
94
|
+
void __set_lookup(Lookup* lookup, size_t size, double* data);
|
|
66
95
|
void __delete_lookup(Lookup* lookup);
|
|
67
96
|
void __print_lookup(Lookup* lookup);
|
|
68
97
|
|
package/src/sde-build.js
CHANGED
package/src/sde-causes.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
2
|
+
|
|
3
|
+
import { parseAndGenerate } from '@sdeverywhere/compile'
|
|
2
4
|
|
|
3
5
|
import { modelPathProps, parseSpec } from './utils.js'
|
|
4
6
|
|
|
@@ -14,22 +16,13 @@ let builder = {
|
|
|
14
16
|
let handler = argv => {
|
|
15
17
|
causes(argv.model, argv.c_varname, argv)
|
|
16
18
|
}
|
|
17
|
-
let causes = (model, varname, opts) => {
|
|
19
|
+
let causes = async (model, varname, opts) => {
|
|
18
20
|
// Get the model name and directory from the model argument.
|
|
19
|
-
let { modelDirname, modelPathname } = modelPathProps(model)
|
|
20
|
-
let extData = new Map()
|
|
21
|
-
let directData = new Map()
|
|
21
|
+
let { modelDirname, modelPathname, modelName } = modelPathProps(model)
|
|
22
22
|
let spec = parseSpec(opts.spec)
|
|
23
|
-
// Preprocess model text into parser input.
|
|
24
|
-
// TODO: The legacy `parseModel` function previously required the `preprocessModel`
|
|
25
|
-
// step to be performed first, but the new `parseModel` runs the preprocessor
|
|
26
|
-
// implicitly, so we can remove this step (and can simplify this code to use
|
|
27
|
-
// `parseAndGenerate` instead)
|
|
28
|
-
let input = preprocessModel(modelPathname, spec)
|
|
29
23
|
// Parse the model to get variable and subscript information.
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
generateCode(parsedModel, { spec, operations, extData, directData, modelDirname, varname })
|
|
24
|
+
let input = readFileSync(modelPathname, 'utf8')
|
|
25
|
+
await parseAndGenerate(input, spec, ['printRefGraph'], modelDirname, modelName, '', varname)
|
|
33
26
|
}
|
|
34
27
|
export default {
|
|
35
28
|
command,
|
package/src/sde-flatten.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
1
2
|
import path from 'path'
|
|
2
3
|
|
|
3
4
|
import B from 'bufx'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import { preprocessVensimModel } from '@sdeverywhere/compile'
|
|
6
7
|
|
|
7
8
|
import { buildDir, modelPathProps } from './utils.js'
|
|
8
9
|
|
|
@@ -44,16 +45,23 @@ const flatten = async (outFile, inFiles, opts) => {
|
|
|
44
45
|
// Get the path and short name of the input mdl file
|
|
45
46
|
const inModelProps = modelPathProps(inFile)
|
|
46
47
|
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
preprocessModel(inModelProps.modelPathname, undefined, 'runnable', false, decls)
|
|
48
|
+
// Read the mdl file content
|
|
49
|
+
const mdlContent = readFileSync(inModelProps.modelPathname, 'utf8')
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
// Preprocess the mdl file and extract the equations and subscript range definitions
|
|
52
|
+
const { defs } = preprocessVensimModel(mdlContent)
|
|
53
|
+
|
|
54
|
+
for (const def of defs) {
|
|
55
|
+
// Associate each definition with the name of the model from which it came
|
|
56
|
+
def.sourceModelName = inModelProps.modelName
|
|
57
|
+
|
|
58
|
+
// The object contains the preprocessed definition in a `def` property; copy this
|
|
59
|
+
// to a `processedDecl` property for clarity (since the existing code used that
|
|
60
|
+
// property name).
|
|
61
|
+
def.processedDecl = def.def
|
|
54
62
|
}
|
|
55
63
|
|
|
56
|
-
fileDecls[inModelProps.modelName] =
|
|
64
|
+
fileDecls[inModelProps.modelName] = defs
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
// Collapse so that we have only one equation or declaration per key.
|
|
@@ -176,9 +184,8 @@ const flatten = async (outFile, inFiles, opts) => {
|
|
|
176
184
|
B.open('pp')
|
|
177
185
|
const ENCODING = '{UTF-8}'
|
|
178
186
|
B.emitLine(ENCODING, 'pp')
|
|
179
|
-
B.emitLine('', 'pp')
|
|
180
187
|
for (const decl of sorted) {
|
|
181
|
-
B.emitLine(
|
|
188
|
+
B.emitLine(`\n${decl.processedDecl}`, 'pp')
|
|
182
189
|
}
|
|
183
190
|
|
|
184
191
|
// Write the flattened mdl file to the build directory
|
package/src/sde-generate.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
+
import { readFileSync } from 'fs'
|
|
2
3
|
import B from 'bufx'
|
|
3
4
|
|
|
4
|
-
import { parseAndGenerate,
|
|
5
|
+
import { parseAndGenerate, preprocessVensimModel } from '@sdeverywhere/compile'
|
|
5
6
|
|
|
6
7
|
import { buildDir, modelPathProps, parseSpec } from './utils.js'
|
|
7
8
|
|
|
@@ -28,11 +29,6 @@ export let builder = {
|
|
|
28
29
|
type: 'boolean',
|
|
29
30
|
alias: 'p'
|
|
30
31
|
},
|
|
31
|
-
analysis: {
|
|
32
|
-
describe: 'write a nonexecutable preprocessed model for analysis',
|
|
33
|
-
type: 'boolean',
|
|
34
|
-
alias: 'a'
|
|
35
|
-
},
|
|
36
32
|
spec: {
|
|
37
33
|
describe: 'pathname of the I/O specification JSON file',
|
|
38
34
|
type: 'string',
|
|
@@ -64,16 +60,13 @@ export let generate = async (model, opts) => {
|
|
|
64
60
|
let { modelDirname, modelName, modelPathname } = modelPathProps(model)
|
|
65
61
|
// Ensure the build directory exists.
|
|
66
62
|
let buildDirname = buildDir(opts.builddir, modelDirname)
|
|
67
|
-
// Preprocess model text into parser input. Stop now if that's all we're doing.
|
|
68
63
|
let spec = parseSpec(opts.spec)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let input = preprocessModel(modelPathname, spec, profile, writeFiles)
|
|
74
|
-
if (writeFiles) {
|
|
64
|
+
let mdlContent = readFileSync(modelPathname, 'utf8')
|
|
65
|
+
if (opts.preprocess) {
|
|
66
|
+
// Only run the preprocessor.
|
|
67
|
+
let preprocessed = preprocessModel(mdlContent)
|
|
75
68
|
let outputPathname = path.join(buildDirname, `${modelName}.mdl`)
|
|
76
|
-
B.write(
|
|
69
|
+
B.write(preprocessed, outputPathname)
|
|
77
70
|
process.exit(0)
|
|
78
71
|
}
|
|
79
72
|
// Parse the model and generate code. If no operation is specified, the code generator will
|
|
@@ -94,7 +87,7 @@ export let generate = async (model, opts) => {
|
|
|
94
87
|
if (opts.refidtest) {
|
|
95
88
|
operations.push('printRefIdTest')
|
|
96
89
|
}
|
|
97
|
-
await parseAndGenerate(
|
|
90
|
+
await parseAndGenerate(mdlContent, spec, operations, modelDirname, modelName, buildDirname)
|
|
98
91
|
}
|
|
99
92
|
|
|
100
93
|
export default {
|
|
@@ -104,3 +97,29 @@ export default {
|
|
|
104
97
|
handler,
|
|
105
98
|
generate
|
|
106
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Read and preprocess the given Vensim model content.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} mdlContent The mdl content.
|
|
105
|
+
* @return {string} The preprocessed mdl text.
|
|
106
|
+
*/
|
|
107
|
+
function preprocessModel(mdlContent) {
|
|
108
|
+
// Run the preprocessor
|
|
109
|
+
const { defs } = preprocessVensimModel(mdlContent)
|
|
110
|
+
|
|
111
|
+
// Sort the definitions alphabetically by key. This mainly exists for compatibility
|
|
112
|
+
// with the legacy `sde generate --preprocess` command, which was changed in #55 to
|
|
113
|
+
// sort definitions alphabetically.
|
|
114
|
+
defs.sort((a, b) => {
|
|
115
|
+
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Join the preprocessed definitions into a single string
|
|
119
|
+
let text = '{UTF-8}\n'
|
|
120
|
+
for (const def of defs) {
|
|
121
|
+
text += `\n${def.def}\n`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return text
|
|
125
|
+
}
|
package/src/sde-names.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
2
|
+
|
|
3
|
+
import { parseAndGenerate, printNames } from '@sdeverywhere/compile'
|
|
2
4
|
|
|
3
5
|
import { modelPathProps, parseSpec } from './utils.js'
|
|
4
6
|
|
|
@@ -26,9 +28,8 @@ let names = async (model, namesPathname, opts) => {
|
|
|
26
28
|
// Get the model name and directory from the model argument.
|
|
27
29
|
let { modelDirname, modelPathname, modelName } = modelPathProps(model)
|
|
28
30
|
let spec = parseSpec(opts.spec)
|
|
29
|
-
// Preprocess model text into parser input.
|
|
30
|
-
let input = preprocessModel(modelPathname, spec)
|
|
31
31
|
// Parse the model to get variable and subscript information.
|
|
32
|
+
let input = readFileSync(modelPathname, 'utf8')
|
|
32
33
|
await parseAndGenerate(input, spec, ['convertNames'], modelDirname, modelName, '')
|
|
33
34
|
// Read each variable name from the names file and convert it.
|
|
34
35
|
printNames(namesPathname, opts.toc ? 'to-c' : 'to-vensim')
|
package/src/sde-test.js
CHANGED