@sdeverywhere/compile 0.7.17 → 0.7.18
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 +1 -3
- package/src/generate/expand-var-names.js +0 -10
- package/src/generate/{code-gen.js → gen-code-c.js} +188 -89
- package/src/generate/gen-code-js.js +616 -0
- package/src/generate/gen-code.js +36 -0
- package/src/generate/gen-equation.js +16 -6
- package/src/generate/gen-expr.js +205 -48
- package/src/generate/gen-lookup-from-direct.js +14 -6
- package/src/generate/gen-lookup-from-external.js +20 -5
- package/src/generate/gen-lookup-from-points.js +28 -6
- package/src/index.js +38 -3
- package/src/model/model.js +92 -148
- package/src/model/read-equation-fn-game.js +50 -0
- package/src/model/read-equations.js +9 -10
- package/src/model/read-variables.js +2 -2
- package/src/model/variable.js +6 -21
- package/src/parse-and-generate.js +22 -19
- package/src/preprocess/preprocessor.js +2 -2
- package/src/generate/equation-gen.js +0 -1268
- package/src/generate/model-lhs-reader.js +0 -88
- package/src/model/equation-reader.js +0 -723
- package/src/model/expr-reader.js +0 -202
- package/src/model/subscript-range-reader.js +0 -143
- package/src/model/var-name-reader.js +0 -40
- package/src/model/variable-reader.js +0 -172
- package/src/parse/model-reader.js +0 -141
- package/src/parse/parser.js +0 -36
package/package.json
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdeverywhere/compile",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.18",
|
|
4
4
|
"description": "The core Vensim to C compiler for the SDEverywhere tool suite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@sdeverywhere/parse": "^0.1.0",
|
|
9
|
-
"antlr4": "4.12.0",
|
|
10
|
-
"antlr4-vensim": "0.6.2",
|
|
11
9
|
"bufx": "^1.0.5",
|
|
12
10
|
"byline": "^5.0.0",
|
|
13
11
|
"csv-parse": "^5.3.3",
|
|
@@ -5,8 +5,6 @@ import { sub, isDimension } from '../_shared/subscript.js'
|
|
|
5
5
|
|
|
6
6
|
import Model from '../model/model.js'
|
|
7
7
|
|
|
8
|
-
import ModelLHSReader from './model-lhs-reader.js'
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* Return an array of names for all variable in the model, sorted alphabetically and expanded to
|
|
12
10
|
* include the full set of subscripted variants for variables that include subscripts.
|
|
@@ -43,14 +41,6 @@ export function expandVarNames(canonical) {
|
|
|
43
41
|
* @returns {string[]} An array of expanded names for the given variable.
|
|
44
42
|
*/
|
|
45
43
|
function namesForVar(v) {
|
|
46
|
-
if (process.env.SDE_NONPUBLIC_USE_NEW_PARSE === '0') {
|
|
47
|
-
// TODO: When the old parsing code is active, use the old ModelLHSReader. This code path
|
|
48
|
-
// will be removed when the old parsing code is removed.
|
|
49
|
-
let modelLHSReader = new ModelLHSReader()
|
|
50
|
-
modelLHSReader.read(v.modelLHS)
|
|
51
|
-
return modelLHSReader.names()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
44
|
if (v.parsedEqn === undefined) {
|
|
55
45
|
// XXX: The special `Time` variable does not have a `parsedEqn`, so use the raw LHS
|
|
56
46
|
return [v.modelLHS]
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import * as R from 'ramda'
|
|
2
2
|
|
|
3
|
-
import { asort, lines, strlist, mapIndexed } from '../_shared/helpers.js'
|
|
3
|
+
import { asort, canonicalVensimName, lines, strlist, mapIndexed } from '../_shared/helpers.js'
|
|
4
4
|
import { sub, allDimensions, allMappings, subscriptFamilies } from '../_shared/subscript.js'
|
|
5
5
|
import Model from '../model/model.js'
|
|
6
6
|
|
|
7
7
|
import { generateEquation } from './gen-equation.js'
|
|
8
|
-
import EquationGen from './equation-gen.js'
|
|
9
8
|
import { expandVarNames } from './expand-var-names.js'
|
|
10
9
|
|
|
11
|
-
export function
|
|
10
|
+
export function generateC(parsedModel, opts) {
|
|
12
11
|
return codeGenerator(parsedModel, opts).generate()
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -20,11 +19,7 @@ let codeGenerator = (parsedModel, opts) => {
|
|
|
20
19
|
let outputAllVars = spec.outputVarNames === undefined || spec.outputVarNames.length === 0
|
|
21
20
|
// Function to generate a section of the code
|
|
22
21
|
let generateSection = R.map(v => {
|
|
23
|
-
|
|
24
|
-
return new EquationGen(v, extData, directData, mode, modelDirname).generate()
|
|
25
|
-
} else {
|
|
26
|
-
return generateEquation(v, mode, extData, directData, modelDirname)
|
|
27
|
-
}
|
|
22
|
+
return generateEquation(v, mode, extData, directData, modelDirname, 'c')
|
|
28
23
|
})
|
|
29
24
|
let section = R.pipe(generateSection, R.flatten, lines)
|
|
30
25
|
function generate() {
|
|
@@ -86,51 +81,54 @@ ${section(Model.dataVars())}
|
|
|
86
81
|
//
|
|
87
82
|
function emitInitLookupsCode() {
|
|
88
83
|
mode = 'init-lookups'
|
|
89
|
-
let code =
|
|
84
|
+
let code = `\
|
|
85
|
+
// Internal state
|
|
90
86
|
bool lookups_initialized = false;
|
|
91
87
|
bool data_initialized = false;
|
|
88
|
+
|
|
92
89
|
`
|
|
93
90
|
code += chunkedFunctions(
|
|
94
91
|
'initLookups',
|
|
95
92
|
Model.lookupVars(),
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
93
|
+
`\
|
|
94
|
+
// Initialize lookups.
|
|
95
|
+
if (lookups_initialized) {
|
|
96
|
+
return;
|
|
97
|
+
}`,
|
|
98
|
+
' lookups_initialized = true;'
|
|
102
99
|
)
|
|
103
100
|
code += chunkedFunctions(
|
|
104
101
|
'initData',
|
|
105
102
|
Model.dataVars(),
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
103
|
+
`\
|
|
104
|
+
// Initialize data.
|
|
105
|
+
if (data_initialized) {
|
|
106
|
+
return;
|
|
107
|
+
}`,
|
|
108
|
+
' data_initialized = true;'
|
|
112
109
|
)
|
|
113
110
|
return code
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
function emitInitConstantsCode() {
|
|
117
114
|
mode = 'init-constants'
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
return chunkedFunctions(
|
|
116
|
+
'initConstants',
|
|
117
|
+
Model.constVars(),
|
|
118
|
+
' // Initialize constants.',
|
|
119
|
+
' initLookups();\n initData();'
|
|
120
|
+
)
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
function emitInitLevelsCode() {
|
|
124
124
|
mode = 'init-levels'
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
`
|
|
125
|
+
return chunkedFunctions(
|
|
126
|
+
'initLevels',
|
|
127
|
+
Model.initVars(),
|
|
128
|
+
`\
|
|
130
129
|
// Initialize variables with initialization values, such as levels, and the variables they depend on.
|
|
131
130
|
_time = _initial_time;`
|
|
132
|
-
)
|
|
133
|
-
`
|
|
131
|
+
)
|
|
134
132
|
}
|
|
135
133
|
|
|
136
134
|
//
|
|
@@ -139,23 +137,85 @@ ${chunkedFunctions(
|
|
|
139
137
|
function emitEvalCode() {
|
|
140
138
|
mode = 'eval'
|
|
141
139
|
|
|
142
|
-
return
|
|
143
|
-
${chunkedFunctions('evalAux', Model.auxVars(), ' // Evaluate auxiliaries in order from the bottom up.')}
|
|
144
|
-
|
|
145
|
-
${chunkedFunctions('evalLevels', Model.levelVars(), ' // Evaluate levels.')}
|
|
146
|
-
`
|
|
140
|
+
return `\
|
|
141
|
+
${chunkedFunctions('evalAux', Model.auxVars(), ' // Evaluate auxiliaries in order from the bottom up.')}\
|
|
142
|
+
${chunkedFunctions('evalLevels', Model.levelVars(), ' // Evaluate levels.')}`
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
//
|
|
150
146
|
// Input/output section
|
|
151
147
|
//
|
|
152
148
|
function emitIOCode() {
|
|
149
|
+
// Configure the body of the `setLookup` function depending on the value
|
|
150
|
+
// of the `customLookups` property in the spec file
|
|
151
|
+
// TODO: The fprintf calls should be replaced with a mechanism that throws
|
|
152
|
+
// an error (we could add a wrapper function at the JS level)
|
|
153
|
+
let setLookupBody
|
|
154
|
+
if (spec.customLookups === true || Array.isArray(spec.customLookups)) {
|
|
155
|
+
setLookupBody = `\
|
|
156
|
+
switch (varIndex) {
|
|
157
|
+
${setLookupImpl(Model.varIndexInfo(), spec.customLookups)}
|
|
158
|
+
default:
|
|
159
|
+
fprintf(stderr, "No lookup found for var index %zu in setLookup\\n", varIndex);
|
|
160
|
+
break;
|
|
161
|
+
}`
|
|
162
|
+
} else {
|
|
163
|
+
let msg = 'The setLookup function was not enabled for the generated model. '
|
|
164
|
+
msg += 'Set the customLookups property in the spec/config file to allow for overriding lookups at runtime.'
|
|
165
|
+
setLookupBody = `\
|
|
166
|
+
fprintf(stderr, "${msg}\\n");`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Configure the output variables that appear in the generated `getHeader`
|
|
170
|
+
// and `storeOutputData` functions
|
|
153
171
|
let headerVarNames = outputAllVars ? expandedVarNames(true) : spec.outputVarNames
|
|
154
172
|
let outputVarIds = outputAllVars ? expandedVarNames() : spec.outputVars
|
|
173
|
+
|
|
174
|
+
// Configure the body of the `storeOutput` function depending on the value
|
|
175
|
+
// of the `customOutputs` property in the spec file
|
|
176
|
+
let storeOutputBody
|
|
177
|
+
if (spec.customOutputs === true || Array.isArray(spec.customOutputs)) {
|
|
178
|
+
storeOutputBody = `\
|
|
179
|
+
switch (varIndex) {
|
|
180
|
+
${customOutputSection(Model.varIndexInfo(), spec.customOutputs)}
|
|
181
|
+
default:
|
|
182
|
+
fprintf(stderr, "No variable found for var index %zu in storeOutput\\n", varIndex);
|
|
183
|
+
break;
|
|
184
|
+
}`
|
|
185
|
+
} else {
|
|
186
|
+
let msg = 'The storeOutput function was not enabled for the generated model. '
|
|
187
|
+
msg +=
|
|
188
|
+
'Set the customOutputs property in the spec/config file to allow for capturing arbitrary variables at runtime.'
|
|
189
|
+
storeOutputBody = `\
|
|
190
|
+
fprintf(stderr, "${msg}\\n");`
|
|
191
|
+
}
|
|
192
|
+
|
|
155
193
|
mode = 'io'
|
|
156
|
-
return
|
|
194
|
+
return `\
|
|
195
|
+
void setInputs(const char* inputData) {
|
|
196
|
+
${inputsFromStringImpl()}
|
|
197
|
+
}
|
|
157
198
|
|
|
158
|
-
void setInputsFromBuffer(double* inputData) {
|
|
199
|
+
void setInputsFromBuffer(double* inputData) {
|
|
200
|
+
${inputsFromBufferImpl()}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
void replaceLookup(Lookup** lookup, double* points, size_t numPoints) {
|
|
204
|
+
if (lookup == NULL) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (*lookup != NULL) {
|
|
208
|
+
__delete_lookup(*lookup);
|
|
209
|
+
*lookup = NULL;
|
|
210
|
+
}
|
|
211
|
+
if (points != NULL) {
|
|
212
|
+
*lookup = __new_lookup(numPoints, /*copy=*/true, points);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void setLookup(size_t varIndex, size_t* subIndices, double* points, size_t numPoints) {
|
|
217
|
+
${setLookupBody}
|
|
218
|
+
}
|
|
159
219
|
|
|
160
220
|
const char* getHeader() {
|
|
161
221
|
return "${R.map(varName => varName.replace(/"/g, '\\"'), headerVarNames).join('\\t')}";
|
|
@@ -165,14 +225,8 @@ void storeOutputData() {
|
|
|
165
225
|
${specOutputSection(outputVarIds)}
|
|
166
226
|
}
|
|
167
227
|
|
|
168
|
-
void storeOutput(size_t varIndex, size_t
|
|
169
|
-
|
|
170
|
-
switch (varIndex) {
|
|
171
|
-
${fullOutputSection(Model.varIndexInfo())}
|
|
172
|
-
default:
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
#endif
|
|
228
|
+
void storeOutput(size_t varIndex, size_t* subIndices) {
|
|
229
|
+
${storeOutputBody}
|
|
176
230
|
}
|
|
177
231
|
`
|
|
178
232
|
}
|
|
@@ -183,9 +237,9 @@ ${fullOutputSection(Model.varIndexInfo())}
|
|
|
183
237
|
function chunkedFunctions(name, vars, preStep, postStep) {
|
|
184
238
|
// Emit one function for each chunk
|
|
185
239
|
let func = (chunk, idx) => {
|
|
186
|
-
return
|
|
240
|
+
return `\
|
|
187
241
|
void ${name}${idx}() {
|
|
188
|
-
|
|
242
|
+
${section(chunk)}
|
|
189
243
|
}
|
|
190
244
|
`
|
|
191
245
|
}
|
|
@@ -213,22 +267,25 @@ void ${name}${idx}() {
|
|
|
213
267
|
chunks = [vars]
|
|
214
268
|
}
|
|
215
269
|
|
|
216
|
-
|
|
217
|
-
|
|
270
|
+
let funcsPart = funcs(chunks)
|
|
271
|
+
let callsPart = funcCalls(chunks)
|
|
272
|
+
|
|
273
|
+
let out = ''
|
|
274
|
+
if (funcsPart.length > 0) {
|
|
275
|
+
out += funcsPart + '\n'
|
|
218
276
|
}
|
|
219
|
-
|
|
220
|
-
|
|
277
|
+
out += `void ${name}() {\n`
|
|
278
|
+
if (preStep) {
|
|
279
|
+
out += preStep + '\n'
|
|
221
280
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
`
|
|
281
|
+
if (callsPart.length > 0) {
|
|
282
|
+
out += callsPart + '\n'
|
|
283
|
+
}
|
|
284
|
+
if (postStep) {
|
|
285
|
+
out += postStep + '\n'
|
|
286
|
+
}
|
|
287
|
+
out += '}\n\n'
|
|
288
|
+
return out
|
|
232
289
|
}
|
|
233
290
|
|
|
234
291
|
//
|
|
@@ -276,9 +333,6 @@ ${postStep}
|
|
|
276
333
|
} else {
|
|
277
334
|
decls = `const int numOutputs = ${spec.outputVars.length};`
|
|
278
335
|
}
|
|
279
|
-
decls += `\n#define SDE_USE_OUTPUT_INDICES 0`
|
|
280
|
-
decls += `\n#define SDE_MAX_OUTPUT_INDICES 1000`
|
|
281
|
-
decls += `\nconst int maxOutputIndices = SDE_USE_OUTPUT_INDICES ? SDE_MAX_OUTPUT_INDICES : 0;`
|
|
282
336
|
return decls
|
|
283
337
|
}
|
|
284
338
|
function arrayDimensionsSection() {
|
|
@@ -313,31 +367,44 @@ ${postStep}
|
|
|
313
367
|
// Input/output section helpers
|
|
314
368
|
//
|
|
315
369
|
function specOutputSection(varNames) {
|
|
316
|
-
// Emit
|
|
370
|
+
// Emit `outputVar` calls for all variables listed in the `outputVarNames`
|
|
371
|
+
// array in the spec file using varNames in C format.
|
|
317
372
|
let code = R.map(varName => ` outputVar(${varName});`)
|
|
318
373
|
let section = R.pipe(code, lines)
|
|
319
374
|
return section(varNames)
|
|
320
375
|
}
|
|
321
|
-
function
|
|
322
|
-
// Emit
|
|
376
|
+
function customOutputSection(varIndexInfo, customOutputs) {
|
|
377
|
+
// Emit `outputVar` calls for all variables that can be accessed as an output.
|
|
378
|
+
// This excludes data and lookup variables; at this time, the data for these
|
|
379
|
+
// cannot be output like for other types of variables.
|
|
380
|
+
let includeCase
|
|
381
|
+
if (Array.isArray(customOutputs)) {
|
|
382
|
+
// Only include a case statement if the variable was explicitly included
|
|
383
|
+
// in the `customOutputs` array in the spec file
|
|
384
|
+
const customOutputVarNames = customOutputs.map(varName => {
|
|
385
|
+
// The developer might specify a variable name that includes subscripts,
|
|
386
|
+
// but we will ignore the subscript part and only match on the base name
|
|
387
|
+
return canonicalVensimName(varName.split('[')[0])
|
|
388
|
+
})
|
|
389
|
+
includeCase = varName => customOutputVarNames.includes(varName)
|
|
390
|
+
} else {
|
|
391
|
+
// Include a case statement for all accessible variables
|
|
392
|
+
includeCase = () => true
|
|
393
|
+
}
|
|
394
|
+
const outputVars = R.filter(info => {
|
|
395
|
+
return info.varType !== 'lookup' && info.varType !== 'data' && includeCase(info.varName)
|
|
396
|
+
})
|
|
323
397
|
const code = R.map(info => {
|
|
324
398
|
let varAccess = info.varName
|
|
325
|
-
|
|
326
|
-
varAccess +=
|
|
327
|
-
}
|
|
328
|
-
if (info.subscriptCount > 1) {
|
|
329
|
-
varAccess += '[subIndex1]'
|
|
399
|
+
for (let i = 0; i < info.subscriptCount; i++) {
|
|
400
|
+
varAccess += `[subIndices[${i}]]`
|
|
330
401
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
c += ` case ${info.varIndex}:\n`
|
|
336
|
-
c += ` outputVar(${varAccess});\n`
|
|
337
|
-
c += ` break;`
|
|
338
|
-
return c
|
|
402
|
+
return `\
|
|
403
|
+
case ${info.varIndex}:
|
|
404
|
+
outputVar(${varAccess});
|
|
405
|
+
break;`
|
|
339
406
|
})
|
|
340
|
-
const section = R.pipe(code, lines)
|
|
407
|
+
const section = R.pipe(outputVars, code, lines)
|
|
341
408
|
return section(varIndexInfo)
|
|
342
409
|
}
|
|
343
410
|
function inputsFromStringImpl() {
|
|
@@ -346,7 +413,7 @@ ${postStep}
|
|
|
346
413
|
let inputVars = ''
|
|
347
414
|
if (spec.inputVars && spec.inputVars.length > 0) {
|
|
348
415
|
let inputVarPtrs = R.reduce((a, inputVar) => R.concat(a, ` &${inputVar},\n`), '', spec.inputVars)
|
|
349
|
-
inputVars =
|
|
416
|
+
inputVars = `\
|
|
350
417
|
static double* inputVarPtrs[] = {\n${inputVarPtrs} };
|
|
351
418
|
char* inputs = (char*)inputData;
|
|
352
419
|
char* token = strtok(inputs, " ");
|
|
@@ -359,21 +426,53 @@ ${postStep}
|
|
|
359
426
|
*inputVarPtrs[modelVarIndex] = value;
|
|
360
427
|
}
|
|
361
428
|
token = strtok(NULL, " ");
|
|
362
|
-
}
|
|
363
|
-
`
|
|
429
|
+
}`
|
|
364
430
|
}
|
|
365
431
|
return inputVars
|
|
366
432
|
}
|
|
367
433
|
function inputsFromBufferImpl() {
|
|
368
|
-
let inputVars =
|
|
434
|
+
let inputVars = []
|
|
369
435
|
if (spec.inputVars && spec.inputVars.length > 0) {
|
|
370
|
-
inputVars += '\n'
|
|
371
436
|
for (let i = 0; i < spec.inputVars.length; i++) {
|
|
372
437
|
const inputVar = spec.inputVars[i]
|
|
373
|
-
inputVars
|
|
438
|
+
inputVars.push(` ${inputVar} = inputData[${i}];`)
|
|
374
439
|
}
|
|
375
440
|
}
|
|
376
|
-
return inputVars
|
|
441
|
+
return inputVars.join('\n')
|
|
442
|
+
}
|
|
443
|
+
function setLookupImpl(varIndexInfo, customLookups) {
|
|
444
|
+
// Emit `replaceLookup` calls for all lookups and data variables that can be overridden
|
|
445
|
+
// at runtime
|
|
446
|
+
let includeCase
|
|
447
|
+
if (Array.isArray(customLookups)) {
|
|
448
|
+
// Only include a case statement if the variable was explicitly included
|
|
449
|
+
// in the `customLookups` array in the spec file
|
|
450
|
+
const customLookupVarNames = customLookups.map(varName => {
|
|
451
|
+
// The developer might specify a variable name that includes subscripts,
|
|
452
|
+
// but we will ignore the subscript part and only match on the base name
|
|
453
|
+
return canonicalVensimName(varName.split('[')[0])
|
|
454
|
+
})
|
|
455
|
+
includeCase = varName => customLookupVarNames.includes(varName)
|
|
456
|
+
} else {
|
|
457
|
+
// Include a case statement for all lookup and data variables
|
|
458
|
+
includeCase = () => true
|
|
459
|
+
}
|
|
460
|
+
const lookupAndDataVars = R.filter(info => {
|
|
461
|
+
return (info.varType === 'lookup' || info.varType === 'data') && includeCase(info.varName)
|
|
462
|
+
})
|
|
463
|
+
const code = R.map(info => {
|
|
464
|
+
let lookupVar = info.varName
|
|
465
|
+
for (let i = 0; i < info.subscriptCount; i++) {
|
|
466
|
+
lookupVar += `[subIndices[${i}]]`
|
|
467
|
+
}
|
|
468
|
+
let c = ''
|
|
469
|
+
c += ` case ${info.varIndex}:\n`
|
|
470
|
+
c += ` replaceLookup(&${lookupVar}, points, numPoints);\n`
|
|
471
|
+
c += ` break;`
|
|
472
|
+
return c
|
|
473
|
+
})
|
|
474
|
+
const section = R.pipe(lookupAndDataVars, code, lines)
|
|
475
|
+
return section(varIndexInfo)
|
|
377
476
|
}
|
|
378
477
|
|
|
379
478
|
return {
|