@sdeverywhere/compile 0.7.26 → 0.7.27
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 -1
- package/src/generate/gen-code-c.js +86 -36
- package/src/generate/gen-code-js.js +59 -0
package/package.json
CHANGED
|
@@ -146,6 +146,24 @@ ${chunkedFunctions('evalLevels', Model.levelVars(), ' // Evaluate levels.')}`
|
|
|
146
146
|
// Input/output section
|
|
147
147
|
//
|
|
148
148
|
function emitIOCode() {
|
|
149
|
+
// Configure the body of the `setConstant` function depending on the value
|
|
150
|
+
// of the `customConstants` property in the spec file
|
|
151
|
+
let setConstantBody
|
|
152
|
+
if (spec.customConstants === true || Array.isArray(spec.customConstants)) {
|
|
153
|
+
setConstantBody = `\
|
|
154
|
+
switch (varIndex) {
|
|
155
|
+
${setConstantImpl(Model.varIndexInfo(), spec.customConstants)}
|
|
156
|
+
default:
|
|
157
|
+
fprintf(stderr, "No constant found for var index %zu in setConstant\\n", varIndex);
|
|
158
|
+
break;
|
|
159
|
+
}`
|
|
160
|
+
} else {
|
|
161
|
+
let msg = 'The setConstant function was not enabled for the generated model. '
|
|
162
|
+
msg += 'Set the customConstants property in the spec/config file to allow for overriding constants at runtime.'
|
|
163
|
+
setConstantBody = `\
|
|
164
|
+
fprintf(stderr, "${msg}\\n");`
|
|
165
|
+
}
|
|
166
|
+
|
|
149
167
|
// Configure the body of the `setLookup` function depending on the value
|
|
150
168
|
// of the `customLookups` property in the spec file
|
|
151
169
|
// TODO: The fprintf calls should be replaced with a mechanism that throws
|
|
@@ -200,12 +218,12 @@ ${customOutputSection(Model.varIndexInfo(), spec.customOutputs)}
|
|
|
200
218
|
|
|
201
219
|
mode = 'io'
|
|
202
220
|
return `\
|
|
203
|
-
void setInputs(
|
|
204
|
-
${
|
|
221
|
+
void setInputs(double* inputValues, int32_t* inputIndices) {
|
|
222
|
+
${setInputsImpl()}
|
|
205
223
|
}
|
|
206
224
|
|
|
207
|
-
void
|
|
208
|
-
${
|
|
225
|
+
void setConstant(size_t varIndex, size_t* subIndices, double value) {
|
|
226
|
+
${setConstantBody}
|
|
209
227
|
}
|
|
210
228
|
|
|
211
229
|
void setLookup(size_t varIndex, size_t* subIndices, double* points, size_t numPoints) {
|
|
@@ -322,13 +340,19 @@ ${section(chunk)}
|
|
|
322
340
|
}
|
|
323
341
|
function internalVarsSection() {
|
|
324
342
|
// Declare internal variables to run the model.
|
|
325
|
-
let
|
|
343
|
+
let numInputsDecl
|
|
344
|
+
if (spec.inputVars && spec.inputVars.length > 0) {
|
|
345
|
+
numInputsDecl = `const int numInputs = ${spec.inputVars.length};`
|
|
346
|
+
} else {
|
|
347
|
+
numInputsDecl = `const int numInputs = 0;`
|
|
348
|
+
}
|
|
349
|
+
let numOutputsDecl
|
|
326
350
|
if (outputAllVars) {
|
|
327
|
-
|
|
351
|
+
numOutputsDecl = `const int numOutputs = ${expandedVarNames().length};`
|
|
328
352
|
} else {
|
|
329
|
-
|
|
353
|
+
numOutputsDecl = `const int numOutputs = ${spec.outputVars.length};`
|
|
330
354
|
}
|
|
331
|
-
return
|
|
355
|
+
return `${numInputsDecl}\n${numOutputsDecl}`
|
|
332
356
|
}
|
|
333
357
|
function arrayDimensionsSection() {
|
|
334
358
|
// Emit a declaration for each array dimension's index numbers.
|
|
@@ -402,38 +426,64 @@ ${section(chunk)}
|
|
|
402
426
|
const section = R.pipe(outputVars, code, lines)
|
|
403
427
|
return section(varIndexInfo)
|
|
404
428
|
}
|
|
405
|
-
function
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
let inputVars = ''
|
|
409
|
-
if (spec.inputVars && spec.inputVars.length > 0) {
|
|
410
|
-
let inputVarPtrs = R.reduce((a, inputVar) => R.concat(a, ` &${inputVar},\n`), '', spec.inputVars)
|
|
411
|
-
inputVars = `\
|
|
412
|
-
static double* inputVarPtrs[] = {\n${inputVarPtrs} };
|
|
413
|
-
char* inputs = (char*)inputData;
|
|
414
|
-
char* token = strtok(inputs, " ");
|
|
415
|
-
while (token) {
|
|
416
|
-
char* p = strchr(token, ':');
|
|
417
|
-
if (p) {
|
|
418
|
-
*p = '\\0';
|
|
419
|
-
int modelVarIndex = atoi(token);
|
|
420
|
-
double value = atof(p+1);
|
|
421
|
-
*inputVarPtrs[modelVarIndex] = value;
|
|
429
|
+
function setInputsImpl() {
|
|
430
|
+
if (!spec.inputVars || spec.inputVars.length === 0) {
|
|
431
|
+
return ''
|
|
422
432
|
}
|
|
423
|
-
|
|
424
|
-
|
|
433
|
+
// Build the pointer table for input variables
|
|
434
|
+
let inputVarPtrs = R.reduce((a, inputVar) => R.concat(a, ` &${inputVar},\n`), '', spec.inputVars)
|
|
435
|
+
return `\
|
|
436
|
+
static double* inputVarPtrs[] = {
|
|
437
|
+
${inputVarPtrs} };
|
|
438
|
+
if (inputIndices == NULL) {
|
|
439
|
+
// When inputIndices is NULL, assume that inputValues contains all input values
|
|
440
|
+
// in the same order that the variables are defined in the model spec
|
|
441
|
+
for (size_t i = 0; i < numInputs; i++) {
|
|
442
|
+
*inputVarPtrs[i] = inputValues[i];
|
|
425
443
|
}
|
|
426
|
-
|
|
444
|
+
} else {
|
|
445
|
+
// When inputIndices is non-NULL, set the input values according to the indices
|
|
446
|
+
// in the inputIndices array, where each index corresponds to the index of the
|
|
447
|
+
// variable in the model spec
|
|
448
|
+
size_t numInputsToSet = (size_t)inputIndices[0];
|
|
449
|
+
for (size_t i = 0; i < numInputsToSet; i++) {
|
|
450
|
+
size_t inputVarIndex = (size_t)inputIndices[i + 1];
|
|
451
|
+
*inputVarPtrs[inputVarIndex] = inputValues[i];
|
|
452
|
+
}
|
|
453
|
+
}`
|
|
427
454
|
}
|
|
428
|
-
function
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
455
|
+
function setConstantImpl(varIndexInfo, customConstants) {
|
|
456
|
+
// Emit case statements for all const variables that can be overridden at runtime
|
|
457
|
+
let includeCase
|
|
458
|
+
if (Array.isArray(customConstants)) {
|
|
459
|
+
// Only include a case statement if the variable was explicitly included
|
|
460
|
+
// in the `customConstants` array in the spec file
|
|
461
|
+
const customConstantVarNames = customConstants.map(varName => {
|
|
462
|
+
// The developer might specify a variable name that includes subscripts,
|
|
463
|
+
// but we will ignore the subscript part and only match on the base name
|
|
464
|
+
return canonicalVensimName(varName.split('[')[0])
|
|
465
|
+
})
|
|
466
|
+
includeCase = varName => customConstantVarNames.includes(varName)
|
|
467
|
+
} else {
|
|
468
|
+
// Include a case statement for all constant variables
|
|
469
|
+
includeCase = () => true
|
|
435
470
|
}
|
|
436
|
-
|
|
471
|
+
const constVars = R.filter(info => {
|
|
472
|
+
return info.varType === 'const' && includeCase(info.varName)
|
|
473
|
+
})
|
|
474
|
+
const code = R.map(info => {
|
|
475
|
+
let constVar = info.varName
|
|
476
|
+
for (let i = 0; i < info.subscriptCount; i++) {
|
|
477
|
+
constVar += `[subIndices[${i}]]`
|
|
478
|
+
}
|
|
479
|
+
let c = ''
|
|
480
|
+
c += ` case ${info.varIndex}:\n`
|
|
481
|
+
c += ` ${constVar} = value;\n`
|
|
482
|
+
c += ` break;`
|
|
483
|
+
return c
|
|
484
|
+
})
|
|
485
|
+
const section = R.pipe(constVars, code, lines)
|
|
486
|
+
return section(varIndexInfo)
|
|
437
487
|
}
|
|
438
488
|
function setLookupImpl(varIndexInfo, customLookups) {
|
|
439
489
|
// Emit case statements for all lookups and data variables that can be overridden
|
|
@@ -241,6 +241,27 @@ ${chunkedFunctions('evalLevels', true, Model.levelVars(), ' // Evaluate levels'
|
|
|
241
241
|
function emitIOCode() {
|
|
242
242
|
mode = 'io'
|
|
243
243
|
|
|
244
|
+
// Configure the body of the `setConstant` function depending on the value
|
|
245
|
+
// of the `customConstants` property in the spec file
|
|
246
|
+
let setConstantBody
|
|
247
|
+
if (spec.customConstants === true || Array.isArray(spec.customConstants)) {
|
|
248
|
+
setConstantBody = `\
|
|
249
|
+
if (!varSpec) {
|
|
250
|
+
throw new Error('Got undefined varSpec in setConstant');
|
|
251
|
+
}
|
|
252
|
+
const varIndex = varSpec.varIndex;
|
|
253
|
+
const subs = varSpec.subscriptIndices;
|
|
254
|
+
switch (varIndex) {
|
|
255
|
+
${setConstantImpl(Model.varIndexInfo(), spec.customConstants)}
|
|
256
|
+
default:
|
|
257
|
+
throw new Error(\`No constant found for var index \${varIndex} in setConstant\`);
|
|
258
|
+
}`
|
|
259
|
+
} else {
|
|
260
|
+
let msg = 'The setConstant function was not enabled for the generated model. '
|
|
261
|
+
msg += 'Set the customConstants property in the spec/config file to allow for overriding constants at runtime.'
|
|
262
|
+
setConstantBody = ` throw new Error('${msg}');`
|
|
263
|
+
}
|
|
264
|
+
|
|
244
265
|
// Configure the body of the `setLookup` function depending on the value
|
|
245
266
|
// of the `customLookups` property in the spec file
|
|
246
267
|
let setLookupBody
|
|
@@ -312,6 +333,10 @@ ${customOutputSection(Model.varIndexInfo(), spec.customOutputs)}
|
|
|
312
333
|
return `\
|
|
313
334
|
/*export*/ function setInputs(valueAtIndex /*: (index: number) => number*/) {${inputsFromBufferImpl()}}
|
|
314
335
|
|
|
336
|
+
/*export*/ function setConstant(varSpec /*: VarSpec*/, value /*: number*/) {
|
|
337
|
+
${setConstantBody}
|
|
338
|
+
}
|
|
339
|
+
|
|
315
340
|
/*export*/ function setLookup(varSpec /*: VarSpec*/, points /*: Float64Array | undefined*/) {
|
|
316
341
|
${setLookupBody}
|
|
317
342
|
}
|
|
@@ -521,6 +546,39 @@ ${section(chunk)}
|
|
|
521
546
|
}
|
|
522
547
|
return inputVars
|
|
523
548
|
}
|
|
549
|
+
function setConstantImpl(varIndexInfo, customConstants) {
|
|
550
|
+
// Emit case statements for all const variables that can be overridden at runtime
|
|
551
|
+
let overrideAllowed
|
|
552
|
+
if (Array.isArray(customConstants)) {
|
|
553
|
+
// Only include a case statement if the variable was explicitly included
|
|
554
|
+
// in the `customConstants` array in the spec file
|
|
555
|
+
const customConstantVarNames = customConstants.map(varName => {
|
|
556
|
+
// The developer might specify a variable name that includes subscripts,
|
|
557
|
+
// but we will ignore the subscript part and only match on the base name
|
|
558
|
+
return canonicalVensimName(varName.split('[')[0])
|
|
559
|
+
})
|
|
560
|
+
overrideAllowed = varName => customConstantVarNames.includes(varName)
|
|
561
|
+
} else {
|
|
562
|
+
// Include a case statement for all constant variables
|
|
563
|
+
overrideAllowed = () => true
|
|
564
|
+
}
|
|
565
|
+
const constVars = R.filter(info => {
|
|
566
|
+
return info.varType === 'const' && overrideAllowed(info.varName)
|
|
567
|
+
})
|
|
568
|
+
const code = R.map(info => {
|
|
569
|
+
let constVar = info.varName
|
|
570
|
+
for (let i = 0; i < info.subscriptCount; i++) {
|
|
571
|
+
constVar += `[subs[${i}]]`
|
|
572
|
+
}
|
|
573
|
+
let c = ''
|
|
574
|
+
c += ` case ${info.varIndex}:\n`
|
|
575
|
+
c += ` ${constVar} = value;\n`
|
|
576
|
+
c += ` break;`
|
|
577
|
+
return c
|
|
578
|
+
})
|
|
579
|
+
const section = R.pipe(constVars, code, lines)
|
|
580
|
+
return section(varIndexInfo)
|
|
581
|
+
}
|
|
524
582
|
function setLookupImpl(varIndexInfo, customLookups) {
|
|
525
583
|
// Emit case statements for all lookups and data variables that can be overridden
|
|
526
584
|
// at runtime
|
|
@@ -605,6 +663,7 @@ export default async function () {
|
|
|
605
663
|
|
|
606
664
|
setTime,
|
|
607
665
|
setInputs,
|
|
666
|
+
setConstant,
|
|
608
667
|
setLookup,
|
|
609
668
|
|
|
610
669
|
storeOutputs,
|