@sdeverywhere/compile 0.7.17 → 0.7.19
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 +169 -170
- 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
|
@@ -30,10 +30,11 @@ import LoopIndexVars from './loop-index-vars.js'
|
|
|
30
30
|
* @param {Map<string, any>} directData The mapping of dataset name used in a `GET DIRECT DATA` call (e.g.,
|
|
31
31
|
* `?data`) to the tabular data contained in the loaded data file.
|
|
32
32
|
* @param {string} modelDir The path to the directory containing the model (used for resolving data files).
|
|
33
|
+
* @param {'c' | 'js'} outFormat The output format.
|
|
33
34
|
* @return {string[]} An array of strings containing the generated C code for the variable,
|
|
34
35
|
* one string per line of code.
|
|
35
36
|
*/
|
|
36
|
-
export function generateEquation(variable, mode, extData, directData, modelDir) {
|
|
37
|
+
export function generateEquation(variable, mode, extData, directData, modelDir, outFormat) {
|
|
37
38
|
// Maps of LHS subscript families to loop index vars for lookup on the RHS
|
|
38
39
|
const loopIndexVars = new LoopIndexVars(['i', 'j', 'k', 'l', 'm'])
|
|
39
40
|
const arrayIndexVars = new LoopIndexVars(['u', 'v', 'w', 's', 't', 'f', 'g', 'h', 'o', 'p', 'q', 'r'])
|
|
@@ -68,12 +69,13 @@ export function generateEquation(variable, mode, extData, directData, modelDir)
|
|
|
68
69
|
|
|
69
70
|
// Turn each dimension ID into a loop with a loop index variable.
|
|
70
71
|
// If the variable has no subscripts, nothing will be emitted here.
|
|
72
|
+
const indexDecl = outFormat === 'js' ? 'let' : 'size_t'
|
|
71
73
|
const openLoops = []
|
|
72
74
|
const closeLoops = []
|
|
73
75
|
for (const dimId of dimIds) {
|
|
74
76
|
const indexName = loopIndexVars.index(dimId)
|
|
75
77
|
const dimLength = sub(dimId).size
|
|
76
|
-
openLoops.push(` for (
|
|
78
|
+
openLoops.push(` for (${indexDecl} ${indexName} = 0; ${indexName} < ${dimLength}; ${indexName}++) {`)
|
|
77
79
|
closeLoops.push(' }')
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -86,7 +88,7 @@ export function generateEquation(variable, mode, extData, directData, modelDir)
|
|
|
86
88
|
// The variable already has data points defined, so generate a new lookup using that data.
|
|
87
89
|
// Note that unlike the other lookup cases, this one needs to include loop open/close code
|
|
88
90
|
// if the variable is subscripted.
|
|
89
|
-
const lookupDef = generateLookupFromPoints(variable, mode, /*copy=*/ true, cLhs, loopIndexVars)
|
|
91
|
+
const lookupDef = generateLookupFromPoints(variable, mode, /*copy=*/ true, cLhs, loopIndexVars, outFormat)
|
|
90
92
|
if (lookupDef.length > 0) {
|
|
91
93
|
return [comment, ...openLoops, ...lookupDef, ...closeLoops]
|
|
92
94
|
} else {
|
|
@@ -95,18 +97,25 @@ export function generateEquation(variable, mode, extData, directData, modelDir)
|
|
|
95
97
|
} else if (variable.directDataArgs) {
|
|
96
98
|
// The data is referenced using a `GET DIRECT DATA` call; generate one or more lookups
|
|
97
99
|
// using the data defined in external files
|
|
98
|
-
return generateLookupsFromDirectData(variable, mode, directData, modelDir, cLhs)
|
|
100
|
+
return generateLookupsFromDirectData(variable, mode, directData, modelDir, cLhs, outFormat)
|
|
99
101
|
} else {
|
|
100
102
|
// This is a "normal" data variable; generate one or more lookups using the data defined
|
|
101
103
|
// in external files
|
|
102
|
-
return generateLookupsFromExternalData(variable, mode, extData, cLhs)
|
|
104
|
+
return generateLookupsFromExternalData(variable, mode, extData, cLhs, outFormat)
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
// Apply special handling for lookup variables. The data for lookup variables is already
|
|
107
109
|
// defined as a set of explicit data points (stored in the `Variable` instance).
|
|
108
110
|
if (variable.isLookup()) {
|
|
109
|
-
|
|
111
|
+
if (variable.varSubtype === 'gameInputs') {
|
|
112
|
+
// For a synthesized game inputs lookup, there is no data array (the data is expected
|
|
113
|
+
// to be supplied at runtime), so don't emit decl or init code for these
|
|
114
|
+
return []
|
|
115
|
+
} else {
|
|
116
|
+
// For all other lookups, emit decl/init code for the lookup
|
|
117
|
+
return generateLookupFromPoints(variable, mode, /*copy=*/ false, cLhs, loopIndexVars, outFormat)
|
|
118
|
+
}
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
// Keep a buffer of code that will be included before the innermost loop
|
|
@@ -125,6 +134,7 @@ export function generateEquation(variable, mode, extData, directData, modelDir)
|
|
|
125
134
|
const genExprCtx = {
|
|
126
135
|
variable,
|
|
127
136
|
mode,
|
|
137
|
+
outFormat,
|
|
128
138
|
cLhs,
|
|
129
139
|
loopIndexVars,
|
|
130
140
|
arrayIndexVars,
|
package/src/generate/gen-expr.js
CHANGED
|
@@ -8,7 +8,8 @@ import Model from '../model/model.js'
|
|
|
8
8
|
*
|
|
9
9
|
* @param {*} variable The `Variable` instance to process.
|
|
10
10
|
* @param {'decl' | 'init-constants' | 'init-lookups' | 'init-levels' | 'eval'} mode The code generation mode.
|
|
11
|
-
* @param {
|
|
11
|
+
* @param {'c' | 'js'} outFormat The output format.
|
|
12
|
+
* @param {string} cLhs The C/JS code for the LHS variable reference.
|
|
12
13
|
* @param {LoopIndexVars} loopIndexVars The loop index state used for LHS dimensions.
|
|
13
14
|
* @param {LoopIndexVars} arrayIndexVars The loop index state used for array functions (that use marked dimensions).
|
|
14
15
|
* @param {() => void} resetMarkedDims Function that resets the marked dimension state.
|
|
@@ -23,7 +24,7 @@ import Model from '../model/model.js'
|
|
|
23
24
|
* referenced in a RHS expression.
|
|
24
25
|
* @param {(baseVarId: string) => string} cVarRefWithLhsSubscripts Function that returns a C variable reference that
|
|
25
26
|
* takes into account the relevant LHS subscripts.
|
|
26
|
-
* @param {(subOrDimId: string) => string} cVarIndex Function that returns C code for indexing into a subscripted variable.
|
|
27
|
+
* @param {(subOrDimId: string) => string} cVarIndex Function that returns C/JS code for indexing into a subscripted variable.
|
|
27
28
|
*/
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -33,7 +34,7 @@ import Model from '../model/model.js'
|
|
|
33
34
|
*
|
|
34
35
|
* @param {*} expr The expression from the parsed model.
|
|
35
36
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
36
|
-
* @return {string} The generated C code.
|
|
37
|
+
* @return {string} The generated C/JS code.
|
|
37
38
|
*/
|
|
38
39
|
export function generateExpr(expr, ctx) {
|
|
39
40
|
switch (expr.kind) {
|
|
@@ -57,7 +58,7 @@ export function generateExpr(expr, ctx) {
|
|
|
57
58
|
// This is a reference to a known variable
|
|
58
59
|
if (v.isData()) {
|
|
59
60
|
// It's a data variable; transform to a `_LOOKUP` function call
|
|
60
|
-
return
|
|
61
|
+
return `${fnRef('_LOOKUP', ctx)}(${ctx.cVarRef(expr)}, _time)`
|
|
61
62
|
} else {
|
|
62
63
|
// It's not a data variable; generate a normal variable reference
|
|
63
64
|
return ctx.cVarRef(expr)
|
|
@@ -102,15 +103,15 @@ export function generateExpr(expr, ctx) {
|
|
|
102
103
|
const lhs = generateExpr(expr.lhs, ctx)
|
|
103
104
|
const rhs = generateExpr(expr.rhs, ctx)
|
|
104
105
|
if (expr.op === '^') {
|
|
105
|
-
return
|
|
106
|
+
return `${ctx.outFormat === 'js' ? 'fns.POW' : 'pow'}(${lhs}, ${rhs})`
|
|
106
107
|
} else {
|
|
107
108
|
let op
|
|
108
109
|
switch (expr.op) {
|
|
109
110
|
case '=':
|
|
110
|
-
op = '=='
|
|
111
|
+
op = ctx.outFormat === 'js' ? '===' : '=='
|
|
111
112
|
break
|
|
112
113
|
case '<>':
|
|
113
|
-
op = '!='
|
|
114
|
+
op = ctx.outFormat === 'js' ? '!==' : '!='
|
|
114
115
|
break
|
|
115
116
|
case ':AND:':
|
|
116
117
|
op = '&&'
|
|
@@ -154,13 +155,13 @@ export function generateExpr(expr, ctx) {
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
/**
|
|
157
|
-
* Generate C code for the given function call.
|
|
158
|
+
* Generate C/JS code for the given function call.
|
|
158
159
|
*
|
|
159
160
|
* TODO: Types
|
|
160
161
|
*
|
|
161
162
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
162
163
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
163
|
-
* @return {string} The generated C code.
|
|
164
|
+
* @return {string} The generated C/JS code.
|
|
164
165
|
*/
|
|
165
166
|
function generateFunctionCall(callExpr, ctx) {
|
|
166
167
|
const fnId = callExpr.fnId
|
|
@@ -170,8 +171,8 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
170
171
|
//
|
|
171
172
|
// Simple functions
|
|
172
173
|
//
|
|
173
|
-
// Each of these functions is implemented with a C function or macro, so no further processing
|
|
174
|
-
// is required other than to emit the
|
|
174
|
+
// Each of these functions is implemented with a C/JS function or C macro, so no further processing
|
|
175
|
+
// is required other than to emit the function/macro call.
|
|
175
176
|
//
|
|
176
177
|
//
|
|
177
178
|
|
|
@@ -181,7 +182,6 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
181
182
|
case '_ARCTAN':
|
|
182
183
|
case '_COS':
|
|
183
184
|
case '_EXP':
|
|
184
|
-
case '_GAME':
|
|
185
185
|
case '_GAMMA_LN':
|
|
186
186
|
case '_IF_THEN_ELSE':
|
|
187
187
|
case '_INTEGER':
|
|
@@ -202,16 +202,26 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
202
202
|
case '_WITH_LOOKUP':
|
|
203
203
|
case '_XIDZ':
|
|
204
204
|
case '_ZIDZ': {
|
|
205
|
-
// For simple functions, emit a C function call with a generated C expression for each argument
|
|
206
205
|
const args = callExpr.args.map(argExpr => generateExpr(argExpr, ctx))
|
|
207
|
-
|
|
206
|
+
if (ctx.outFormat === 'js' && fnId === '_GAMMA_LN') {
|
|
207
|
+
throw new Error(`${callExpr.fnName} function not yet implemented for JS code gen`)
|
|
208
|
+
}
|
|
209
|
+
if (ctx.outFormat === 'js' && fnId === '_IF_THEN_ELSE') {
|
|
210
|
+
// When generating conditional expressions for JS target, since we can't rely on macros like we do for C,
|
|
211
|
+
// it is better to translate it into a ternary instead of relying on a built-in function (since the latter
|
|
212
|
+
// would require always evaluating both branches, while the former can be more optimized by the interpreter)
|
|
213
|
+
return `((${args[0]}) ? (${args[1]}) : (${args[2]}))`
|
|
214
|
+
} else {
|
|
215
|
+
// For simple functions, emit a C/JS function call with a generated C/JS expression for each argument
|
|
216
|
+
return `${fnRef(fnId, ctx)}(${args.join(', ')})`
|
|
217
|
+
}
|
|
208
218
|
}
|
|
209
219
|
|
|
210
220
|
//
|
|
211
221
|
//
|
|
212
222
|
// Lookup functions
|
|
213
223
|
//
|
|
214
|
-
// Each of these functions is implemented with a C function (like the simple functions above),
|
|
224
|
+
// Each of these functions is implemented with a C/JS function (like the simple functions above),
|
|
215
225
|
// but we need to handle the first argument specially, otherwise we would get the default handling
|
|
216
226
|
// for data variables, which generates a lookup call (see 'variable-ref' case in `generateExpr`).
|
|
217
227
|
//
|
|
@@ -222,10 +232,18 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
222
232
|
case '_LOOKUP_FORWARD':
|
|
223
233
|
case '_LOOKUP_INVERT': {
|
|
224
234
|
// For LOOKUP* functions, the first argument must be a reference to the lookup variable. Emit
|
|
225
|
-
// a C function call with a generated C expression for each remaining argument.
|
|
235
|
+
// a C/JS function call with a generated C/JS expression for each remaining argument.
|
|
226
236
|
const cVarRef = ctx.cVarRef(callExpr.args[0])
|
|
227
237
|
const cArgs = callExpr.args.slice(1).map(arg => generateExpr(arg, ctx))
|
|
228
|
-
return `${fnId}(${cVarRef}, ${cArgs.join(', ')})`
|
|
238
|
+
return `${fnRef(fnId, ctx)}(${cVarRef}, ${cArgs.join(', ')})`
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
case '_GAME': {
|
|
242
|
+
// For the GAME function, emit a C/JS function call that has the synthesized game inputs lookup
|
|
243
|
+
// as the first argument, followed by the default value argument from the function call
|
|
244
|
+
const cLookupArg = ctx.cVarRefWithLhsSubscripts(ctx.variable.gameLookupVarName)
|
|
245
|
+
const cDefaultArg = generateExpr(callExpr.args[0], ctx)
|
|
246
|
+
return `${fnRef(fnId, ctx)}(${cLookupArg}, ${cDefaultArg})`
|
|
229
247
|
}
|
|
230
248
|
|
|
231
249
|
//
|
|
@@ -240,6 +258,9 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
240
258
|
case '_SAMPLE_IF_TRUE':
|
|
241
259
|
case '_INTEG':
|
|
242
260
|
// Split level functions into init and eval expressions
|
|
261
|
+
if (ctx.outFormat === 'js' && (fnId === '_DELAY_FIXED' || fnId === '_DEPRECIATE_STRAIGHTLINE')) {
|
|
262
|
+
throw new Error(`${callExpr.fnName} function not yet implemented for JS code gen`)
|
|
263
|
+
}
|
|
243
264
|
if (ctx.mode.startsWith('init')) {
|
|
244
265
|
return generateLevelInit(callExpr, ctx)
|
|
245
266
|
} else if (ctx.mode === 'eval') {
|
|
@@ -321,6 +342,9 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
321
342
|
//
|
|
322
343
|
|
|
323
344
|
case '_ALLOCATE_AVAILABLE':
|
|
345
|
+
if (ctx.outFormat === 'js') {
|
|
346
|
+
throw new Error(`${callExpr.fnName} function not yet implemented for JS code gen`)
|
|
347
|
+
}
|
|
324
348
|
return generateAllocateAvailableCall(callExpr, ctx)
|
|
325
349
|
|
|
326
350
|
case '_ELMCOUNT': {
|
|
@@ -383,13 +407,13 @@ function generateFunctionCall(callExpr, ctx) {
|
|
|
383
407
|
}
|
|
384
408
|
|
|
385
409
|
/**
|
|
386
|
-
* Generate C code for the given level variable at init time.
|
|
410
|
+
* Generate C/JS code for the given level variable at init time.
|
|
387
411
|
*
|
|
388
412
|
* TODO: Types
|
|
389
413
|
*
|
|
390
414
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
391
415
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
392
|
-
* @return {string} The generated C code.
|
|
416
|
+
* @return {string} The generated C/JS code.
|
|
393
417
|
*/
|
|
394
418
|
function generateLevelInit(callExpr, ctx) {
|
|
395
419
|
const fnId = callExpr.fnId
|
|
@@ -432,19 +456,19 @@ function generateLevelInit(callExpr, ctx) {
|
|
|
432
456
|
}
|
|
433
457
|
|
|
434
458
|
/**
|
|
435
|
-
* Generate C code for the given level variable at eval time.
|
|
459
|
+
* Generate C/JS code for the given level variable at eval time.
|
|
436
460
|
*
|
|
437
461
|
* TODO: Types
|
|
438
462
|
*
|
|
439
463
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
440
464
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
441
|
-
* @return {string} The generated C code.
|
|
465
|
+
* @return {string} The generated C/JS code.
|
|
442
466
|
*/
|
|
443
467
|
function generateLevelEval(callExpr, ctx) {
|
|
444
468
|
const fnId = callExpr.fnId
|
|
445
469
|
|
|
446
470
|
function generateCall(args) {
|
|
447
|
-
return `${fnId}(${args.join(', ')})`
|
|
471
|
+
return `${fnRef(fnId, ctx)}(${args.join(', ')})`
|
|
448
472
|
}
|
|
449
473
|
|
|
450
474
|
switch (fnId) {
|
|
@@ -478,7 +502,15 @@ function generateLevelEval(callExpr, ctx) {
|
|
|
478
502
|
if (fnId === '_SAMPLE_IF_TRUE') {
|
|
479
503
|
args.push(generateExpr(callExpr.args[1], ctx))
|
|
480
504
|
}
|
|
481
|
-
|
|
505
|
+
if (ctx.outFormat === 'js' && fnId === '_SAMPLE_IF_TRUE') {
|
|
506
|
+
// When generating conditional expressions for JS target, since we can't rely on macros like we do for C,
|
|
507
|
+
// it is better to translate it into a ternary instead of relying on a built-in function (since the latter
|
|
508
|
+
// would require always evaluating both branches, while the former can be more optimized by the interpreter)
|
|
509
|
+
return `((${args[1]}) ? (${args[2]}) : (${args[0]}))`
|
|
510
|
+
} else {
|
|
511
|
+
// In all other cases, generate a normal call
|
|
512
|
+
return generateCall(args)
|
|
513
|
+
}
|
|
482
514
|
}
|
|
483
515
|
|
|
484
516
|
default:
|
|
@@ -487,27 +519,27 @@ function generateLevelEval(callExpr, ctx) {
|
|
|
487
519
|
}
|
|
488
520
|
|
|
489
521
|
/**
|
|
490
|
-
* Generate C code for a lookup call.
|
|
522
|
+
* Generate C/JS code for a lookup call.
|
|
491
523
|
*
|
|
492
524
|
* TODO: Types
|
|
493
525
|
*
|
|
494
526
|
* @param {*} lookupVarRef The lookup `VariableRef`.
|
|
495
527
|
* @param {*} argExpr The parsed `Expr` for the single argument for the lookup.
|
|
496
528
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
497
|
-
* @return {string} The generated C code.
|
|
529
|
+
* @return {string} The generated C/JS code.
|
|
498
530
|
*/
|
|
499
531
|
function generateLookupCall(lookupVarRef, argExpr, ctx) {
|
|
500
532
|
const cVarRef = ctx.cVarRef(lookupVarRef)
|
|
501
533
|
const cArg = generateExpr(argExpr, ctx)
|
|
502
|
-
return
|
|
534
|
+
return `${fnRef('_LOOKUP', ctx)}(${cVarRef}, ${cArg})`
|
|
503
535
|
}
|
|
504
536
|
|
|
505
537
|
/**
|
|
506
|
-
* Generate C code for an array function call (e.g., `SUM`).
|
|
538
|
+
* Generate C/JS code for an array function call (e.g., `SUM`).
|
|
507
539
|
*
|
|
508
540
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
509
541
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
510
|
-
* @return {string} The generated C code.
|
|
542
|
+
* @return {string} The generated C/JS code.
|
|
511
543
|
*/
|
|
512
544
|
function generateArrayFunctionCall(callExpr, ctx) {
|
|
513
545
|
// Determine the initial value and loop body depending on the function
|
|
@@ -523,12 +555,12 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
523
555
|
break
|
|
524
556
|
|
|
525
557
|
case '_VMIN':
|
|
526
|
-
initValue =
|
|
558
|
+
initValue = maxNumber(ctx)
|
|
527
559
|
loopBodyOp = 'min'
|
|
528
560
|
break
|
|
529
561
|
|
|
530
562
|
case '_VMAX':
|
|
531
|
-
initValue =
|
|
563
|
+
initValue = `-${maxNumber(ctx)}`
|
|
532
564
|
loopBodyOp = 'max'
|
|
533
565
|
break
|
|
534
566
|
|
|
@@ -573,7 +605,7 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
573
605
|
loopBodyOp = 'sum'
|
|
574
606
|
break
|
|
575
607
|
case 3:
|
|
576
|
-
initValue =
|
|
608
|
+
initValue = `-${maxNumber(ctx)}`
|
|
577
609
|
loopBodyOp = 'max'
|
|
578
610
|
break
|
|
579
611
|
default:
|
|
@@ -582,7 +614,7 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
582
614
|
|
|
583
615
|
// Emit the temporary condition variable declaration
|
|
584
616
|
vsCondVar = newTmpVarName()
|
|
585
|
-
ctx.emitPreFormula(` bool
|
|
617
|
+
ctx.emitPreFormula(` ${varDecl('bool', vsCondVar, 'false', ctx)}`)
|
|
586
618
|
|
|
587
619
|
// Define the code that will be emitted in place of the `VECTOR SELECT` call
|
|
588
620
|
tmpVar = newTmpVarName()
|
|
@@ -598,7 +630,7 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
598
630
|
if (!tmpVar) {
|
|
599
631
|
tmpVar = newTmpVarName()
|
|
600
632
|
}
|
|
601
|
-
ctx.emitPreFormula(` double
|
|
633
|
+
ctx.emitPreFormula(` ${varDecl('double', tmpVar, initValue, ctx)}`)
|
|
602
634
|
|
|
603
635
|
// Find all marked dimensions used in the array function arguments
|
|
604
636
|
const markedDimIds = new Set()
|
|
@@ -612,11 +644,12 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
612
644
|
}
|
|
613
645
|
|
|
614
646
|
// Open the array function loop(s)
|
|
647
|
+
const indexDecl = ctx.outFormat === 'js' ? 'let' : 'size_t'
|
|
615
648
|
for (const markedDimId of markedDimIds) {
|
|
616
649
|
ctx.addMarkedDim(markedDimId)
|
|
617
650
|
const n = sub(markedDimId).size
|
|
618
651
|
const i = ctx.arrayIndexVars.index(markedDimId)
|
|
619
|
-
ctx.emitPreFormula(` for (
|
|
652
|
+
ctx.emitPreFormula(` for (${indexDecl} ${i} = 0; ${i} < ${n}; ${i}++) {`)
|
|
620
653
|
}
|
|
621
654
|
|
|
622
655
|
// Emit the body of the array function loop. Note that we generate the expression code here
|
|
@@ -627,9 +660,9 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
627
660
|
case 'sum':
|
|
628
661
|
return `${tmpVar} += ${argCode};`
|
|
629
662
|
case 'min':
|
|
630
|
-
return `${tmpVar} =
|
|
663
|
+
return `${tmpVar} = ${minFunc(ctx)}(${tmpVar}, ${argCode});`
|
|
631
664
|
case 'max':
|
|
632
|
-
return `${tmpVar} =
|
|
665
|
+
return `${tmpVar} = ${maxFunc(ctx)}(${tmpVar}, ${argCode});`
|
|
633
666
|
default:
|
|
634
667
|
throw new Error(`Unexpected loop body op ${loopBodyOp} for VECTOR SELECT`)
|
|
635
668
|
}
|
|
@@ -639,7 +672,11 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
639
672
|
// For `VECTOR SELECT`, the inner loop includes a conditional
|
|
640
673
|
const selArrayCode = generateExpr(callExpr.args[0], ctx)
|
|
641
674
|
const exprArrayCode = generateExpr(callExpr.args[1], ctx)
|
|
642
|
-
ctx.
|
|
675
|
+
if (ctx.outFormat === 'c') {
|
|
676
|
+
ctx.emitPreFormula(` if (bool_cond(${selArrayCode})) {`)
|
|
677
|
+
} else {
|
|
678
|
+
ctx.emitPreFormula(` if (${selArrayCode}) {`)
|
|
679
|
+
}
|
|
643
680
|
ctx.emitPreFormula(` ${innerStmt(exprArrayCode)}`)
|
|
644
681
|
ctx.emitPreFormula(` ${vsCondVar} = true;`)
|
|
645
682
|
ctx.emitPreFormula(' }')
|
|
@@ -667,11 +704,11 @@ function generateArrayFunctionCall(callExpr, ctx) {
|
|
|
667
704
|
}
|
|
668
705
|
|
|
669
706
|
/**
|
|
670
|
-
* Generate C code for a `VECTOR ELM MAP` function call.
|
|
707
|
+
* Generate C/JS code for a `VECTOR ELM MAP` function call.
|
|
671
708
|
*
|
|
672
709
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
673
710
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
674
|
-
* @return {string} The generated C code.
|
|
711
|
+
* @return {string} The generated C/JS code.
|
|
675
712
|
*/
|
|
676
713
|
function generateVectorElmMapCall(callExpr, ctx) {
|
|
677
714
|
function validateArg(index, name) {
|
|
@@ -713,7 +750,18 @@ function generateVectorElmMapCall(callExpr, ctx) {
|
|
|
713
750
|
const rhsSubIds = normalizeSubscripts(vecSubIds)
|
|
714
751
|
const cSubscripts = rhsSubIds.map(rhsSubId => {
|
|
715
752
|
if (isIndex(rhsSubId)) {
|
|
716
|
-
|
|
753
|
+
let indexDecl
|
|
754
|
+
switch (ctx.outFormat) {
|
|
755
|
+
case 'c':
|
|
756
|
+
indexDecl = `(size_t)(${subBase} + ${offsetArgCode})`
|
|
757
|
+
break
|
|
758
|
+
case 'js':
|
|
759
|
+
indexDecl = `${subBase} + ${offsetArgCode}`
|
|
760
|
+
break
|
|
761
|
+
default:
|
|
762
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
763
|
+
}
|
|
764
|
+
return `[${subFamily}[${indexDecl}]]`
|
|
717
765
|
} else {
|
|
718
766
|
const subIndex = ctx.loopIndexVars.index(rhsSubId)
|
|
719
767
|
return `[${rhsSubId}[${subIndex}]]`
|
|
@@ -725,11 +773,11 @@ function generateVectorElmMapCall(callExpr, ctx) {
|
|
|
725
773
|
}
|
|
726
774
|
|
|
727
775
|
/**
|
|
728
|
-
* Generate C code for a `VECTOR SORT ORDER` function call.
|
|
776
|
+
* Generate C/JS code for a `VECTOR SORT ORDER` function call.
|
|
729
777
|
*
|
|
730
778
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
731
779
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
732
|
-
* @return {string} The generated C code.
|
|
780
|
+
* @return {string} The generated C/JS code.
|
|
733
781
|
*/
|
|
734
782
|
function generateVectorSortOrderCall(callExpr, ctx) {
|
|
735
783
|
// Process the vector argument
|
|
@@ -758,18 +806,27 @@ function generateVectorSortOrderCall(callExpr, ctx) {
|
|
|
758
806
|
// Generate the code that is emitted before the entire block (before any loops are opened)
|
|
759
807
|
const tmpVarId = newTmpVarName()
|
|
760
808
|
const dimSize = sub(dimId).size
|
|
761
|
-
ctx.
|
|
809
|
+
switch (ctx.outFormat) {
|
|
810
|
+
case 'c':
|
|
811
|
+
ctx.emitPreInnerLoop(` double* ${tmpVarId} = _VECTOR_SORT_ORDER(${vecVarRefId}, ${dimSize}, ${dirArg});`)
|
|
812
|
+
break
|
|
813
|
+
case 'js':
|
|
814
|
+
ctx.emitPreInnerLoop(` let ${tmpVarId} = fns.VECTOR_SORT_ORDER(${vecVarRefId}, ${dimSize}, ${dirArg});`)
|
|
815
|
+
break
|
|
816
|
+
default:
|
|
817
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
818
|
+
}
|
|
762
819
|
|
|
763
820
|
// Generate the RHS expression used in the inner loop
|
|
764
821
|
return `${tmpVarId}[${dimId}[${subIndex}]]`
|
|
765
822
|
}
|
|
766
823
|
|
|
767
824
|
/**
|
|
768
|
-
* Generate C code for an `ALLOCATE AVAILABLE` function call.
|
|
825
|
+
* Generate C/JS code for an `ALLOCATE AVAILABLE` function call.
|
|
769
826
|
*
|
|
770
827
|
* @param {*} callExpr The function call expression from the parsed model.
|
|
771
828
|
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
772
|
-
* @return {string} The generated C code.
|
|
829
|
+
* @return {string} The generated C/JS code.
|
|
773
830
|
*/
|
|
774
831
|
function generateAllocateAvailableCall(callExpr, ctx) {
|
|
775
832
|
function validateArg(index, name) {
|
|
@@ -801,9 +858,20 @@ function generateAllocateAvailableCall(callExpr, ctx) {
|
|
|
801
858
|
// Generate the code that is emitted before the entire block (before any loops are opened)
|
|
802
859
|
const tmpVarId = newTmpVarName()
|
|
803
860
|
const dimSize = sub(dimId).size
|
|
804
|
-
ctx.
|
|
805
|
-
|
|
806
|
-
|
|
861
|
+
switch (ctx.outFormat) {
|
|
862
|
+
case 'c':
|
|
863
|
+
ctx.emitPreInnerLoop(
|
|
864
|
+
` double* ${tmpVarId} = _ALLOCATE_AVAILABLE(${reqRefId}, (double*)${priorityRefId}, ${availRefId}, ${dimSize});`
|
|
865
|
+
)
|
|
866
|
+
break
|
|
867
|
+
case 'js':
|
|
868
|
+
ctx.emitPreInnerLoop(
|
|
869
|
+
` let ${tmpVarId} = fns.ALLOCATE_AVAILABLE(${reqRefId}, ${priorityRefId}, ${availRefId}, ${dimSize});`
|
|
870
|
+
)
|
|
871
|
+
break
|
|
872
|
+
default:
|
|
873
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
874
|
+
}
|
|
807
875
|
|
|
808
876
|
// Generate the RHS expression used in the inner loop
|
|
809
877
|
return `${tmpVarId}[${dimId}[${subIndex}]]`
|
|
@@ -849,3 +917,92 @@ function visitVariableRefs(expr, onVarRef) {
|
|
|
849
917
|
throw new Error(`Unhandled expression kind '${expr.kind}' in visitVariableRefs`)
|
|
850
918
|
}
|
|
851
919
|
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Return a C or JS function reference for the given function ID and context.
|
|
923
|
+
*
|
|
924
|
+
* @param {string} fnId The function ID.
|
|
925
|
+
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
926
|
+
* @return {string} The generated C/JS code.
|
|
927
|
+
*/
|
|
928
|
+
function fnRef(fnId, ctx) {
|
|
929
|
+
switch (ctx.outFormat) {
|
|
930
|
+
case 'c':
|
|
931
|
+
return fnId
|
|
932
|
+
case 'js':
|
|
933
|
+
return `fns.${fnId.slice(1)}`
|
|
934
|
+
default:
|
|
935
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Return a C or JS variable declaration.
|
|
941
|
+
*
|
|
942
|
+
* @param {string} cVarType The variable type (only used for C code generation).
|
|
943
|
+
* @param {string} varName The variable name.
|
|
944
|
+
* @param {string} rhs The RHS for the declaration.
|
|
945
|
+
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
946
|
+
* @return {string} The generated C/JS code.
|
|
947
|
+
*/
|
|
948
|
+
function varDecl(cVarType, varName, rhs, ctx) {
|
|
949
|
+
switch (ctx.outFormat) {
|
|
950
|
+
case 'c':
|
|
951
|
+
return `${cVarType} ${varName} = ${rhs};`
|
|
952
|
+
case 'js':
|
|
953
|
+
return `let ${varName} = ${rhs};`
|
|
954
|
+
default:
|
|
955
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Return the "max number" constant for C or JS.
|
|
961
|
+
*
|
|
962
|
+
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
963
|
+
* @return {string} The generated C/JS code.
|
|
964
|
+
*/
|
|
965
|
+
function maxNumber(ctx) {
|
|
966
|
+
switch (ctx.outFormat) {
|
|
967
|
+
case 'c':
|
|
968
|
+
return 'DBL_MAX'
|
|
969
|
+
case 'js':
|
|
970
|
+
return 'Number.MAX_VALUE'
|
|
971
|
+
default:
|
|
972
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Return the "max" function for C or JS.
|
|
978
|
+
*
|
|
979
|
+
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
980
|
+
* @return {string} The generated C/JS code.
|
|
981
|
+
*/
|
|
982
|
+
function maxFunc(ctx) {
|
|
983
|
+
switch (ctx.outFormat) {
|
|
984
|
+
case 'c':
|
|
985
|
+
return 'fmax'
|
|
986
|
+
case 'js':
|
|
987
|
+
return 'Math.max'
|
|
988
|
+
default:
|
|
989
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Return the "min" function for C or JS.
|
|
995
|
+
*
|
|
996
|
+
* @param {GenExprContext} ctx The context used when generating code for the expression.
|
|
997
|
+
* @return {string} The generated C/JS code.
|
|
998
|
+
*/
|
|
999
|
+
function minFunc(ctx) {
|
|
1000
|
+
switch (ctx.outFormat) {
|
|
1001
|
+
case 'c':
|
|
1002
|
+
return 'fmin'
|
|
1003
|
+
case 'js':
|
|
1004
|
+
return 'Math.min'
|
|
1005
|
+
default:
|
|
1006
|
+
throw new Error(`Unhandled output format '${ctx.outFormat}'`)
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
@@ -15,11 +15,12 @@ import { handleExcelOrCsvFile } from './direct-data-helpers.js'
|
|
|
15
15
|
* @param {Map<string, any>} directData The mapping of dataset name used in a `GET DIRECT DATA` call (e.g.,
|
|
16
16
|
* `?data`) to the tabular data contained in the loaded data file.
|
|
17
17
|
* @param {string} modelDir The path to the directory containing the model (used for resolving data files).
|
|
18
|
-
* @param {string} varLhs The C code for the LHS variable reference.
|
|
19
|
-
* @
|
|
18
|
+
* @param {string} varLhs The C/JS code for the LHS variable reference.
|
|
19
|
+
* @param {'c' | 'js'} outFormat The output format.
|
|
20
|
+
* @return {string[]} An array of strings containing the generated C/JS code for the variable,
|
|
20
21
|
* one string per line of code.
|
|
21
22
|
*/
|
|
22
|
-
export function generateLookupsFromDirectData(variable, mode, directData, modelDir, varLhs) {
|
|
23
|
+
export function generateLookupsFromDirectData(variable, mode, directData, modelDir, varLhs, outFormat) {
|
|
23
24
|
if (mode === 'decl') {
|
|
24
25
|
// Nothing to emit in decl mode
|
|
25
26
|
return []
|
|
@@ -52,10 +53,10 @@ export function generateLookupsFromDirectData(variable, mode, directData, modelD
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
return [generateDirectDataLookup(varLhs, getCellValue, timeRowOrCol, startCell, indexNum)]
|
|
56
|
+
return [generateDirectDataLookup(varLhs, getCellValue, timeRowOrCol, startCell, indexNum, outFormat)]
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
function generateDirectDataLookup(varLhs, getCellValue, timeRowOrCol, startCell, indexNum) {
|
|
59
|
+
function generateDirectDataLookup(varLhs, getCellValue, timeRowOrCol, startCell, indexNum, outFormat) {
|
|
59
60
|
// Read a row or column of data as (time, value) pairs from the worksheet.
|
|
60
61
|
// The cell(c,r) function wraps data access by column and row.
|
|
61
62
|
let lookupData = ''
|
|
@@ -101,5 +102,12 @@ function generateDirectDataLookup(varLhs, getCellValue, timeRowOrCol, startCell,
|
|
|
101
102
|
throw new Error(`Empty lookup data array for ${varLhs}`)
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
switch (outFormat) {
|
|
106
|
+
case 'c':
|
|
107
|
+
return ` ${varLhs} = __new_lookup(${lookupSize}, /*copy=*/true, (double[]){ ${lookupData} });`
|
|
108
|
+
case 'js':
|
|
109
|
+
return ` ${varLhs} = fns.createLookup(${lookupSize}, [${lookupData}]);`
|
|
110
|
+
default:
|
|
111
|
+
throw new Error(`Unhandled output format '${outFormat}'`)
|
|
112
|
+
}
|
|
105
113
|
}
|