@sdeverywhere/compile 0.7.29 → 0.7.30

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdeverywhere/compile",
3
- "version": "0.7.29",
3
+ "version": "0.7.30",
4
4
  "description": "The core Vensim to C compiler for the SDEverywhere tool suite.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -387,6 +387,12 @@ function generateFunctionCall(callExpr, ctx) {
387
387
  }
388
388
  return generateAllocateAvailableCall(callExpr, ctx)
389
389
 
390
+ case '_ALLOCATE_BY_PRIORITY':
391
+ if (ctx.outFormat === 'js') {
392
+ throw new Error(`${callExpr.fnName} function not yet implemented for JS code gen`)
393
+ }
394
+ return generateAllocateByPriorityCall(callExpr, ctx)
395
+
390
396
  case '_ELMCOUNT':
391
397
  case '_SIZE': {
392
398
  // Emit the size of the dimension in place of the dimension name. Note that Vensim uses
@@ -944,6 +950,88 @@ function generateAllocateAvailableCall(callExpr, ctx) {
944
950
  return `${tmpVarId}[${allocDimId}[${allocLoopIndexVar}]]`
945
951
  }
946
952
 
953
+ /**
954
+ * Generate C/JS code for an `ALLOCATE BY PRIORITY` function call.
955
+ *
956
+ * @param {*} callExpr The function call expression from the parsed model.
957
+ * @param {GenExprContext} ctx The context used when generating code for the expression.
958
+ * @return {string} The generated C/JS code.
959
+ */
960
+ function generateAllocateByPriorityCall(callExpr, ctx) {
961
+ function validateArg(index, name) {
962
+ const arg = callExpr.args[index]
963
+ if (arg.kind === 'variable-ref') {
964
+ return arg
965
+ } else {
966
+ throw new Error(`ALLOCATE BY PRIORITY argument '${name}' must be a variable reference`)
967
+ }
968
+ }
969
+
970
+ // Given a C/JS variable reference string (e.g., '_var[i][j]'), return that
971
+ // string without the last N array index parts
972
+ function cVarRefWithoutLastIndices(arg, count) {
973
+ const varRef = ctx.cVarRef(arg)
974
+ const origIndexParts = Model.splitRefId(varRef).subscripts
975
+ if (origIndexParts < count) {
976
+ throw new Error(`ALLOCATE BY PRIORITY argument '${arg}' should have at least ${count} subscripts`)
977
+ }
978
+ const newIndexParts = origIndexParts.slice(0, -count)
979
+ if (newIndexParts.length > 0) {
980
+ return `${arg.varId}${newIndexParts.map(x => `[${x}]`).join('')}`
981
+ } else {
982
+ return arg.varId
983
+ }
984
+ }
985
+
986
+ // Process the request argument. Only include subscripts up until the last one;
987
+ // the implementation function will iterate over the requesters array.
988
+ const reqArg = validateArg(0, 'req')
989
+ const reqRef = cVarRefWithoutLastIndices(reqArg, 1)
990
+
991
+ // Process the priority argument. Only include subscripts up until the
992
+ // last one; the implementation function will iterate over the priorities
993
+ // array.
994
+ const priorityArg = validateArg(1, 'priority')
995
+ const priorityRef = cVarRefWithoutLastIndices(priorityArg, 1)
996
+
997
+ // Process the size argument
998
+ const sizeArg = generateExpr(callExpr.args[2], ctx)
999
+
1000
+ // Process the width argument
1001
+ const widthArg = generateExpr(callExpr.args[3], ctx)
1002
+
1003
+ // Process the supply argument
1004
+ const supplyArg = generateExpr(callExpr.args[4], ctx)
1005
+
1006
+ // The `ALLOCATE BY PRIORITY` function iterates over the last subscript in its first
1007
+ // argument, allocating the available quantity according to the priority values given
1008
+ // in the second argument. The `readEquation` process will have already verified that
1009
+ // the last dimension of both arguments matches the last dimension of the LHS.
1010
+ const allocDimId = reqArg.subscriptRefs[reqArg.subscriptRefs.length - 1].subId
1011
+ const allocLoopIndexVar = ctx.loopIndexVars.index(allocDimId)
1012
+
1013
+ // Generate the code that is emitted before the entire block (before any loops are opened)
1014
+ const tmpVarId = newTmpVarName()
1015
+ const numRequesters = sub(allocDimId).size
1016
+ switch (ctx.outFormat) {
1017
+ case 'c':
1018
+ ctx.emitPreInnerLoop(
1019
+ ` double* ${tmpVarId} = _ALLOCATE_BY_PRIORITY(${reqRef}, ${priorityRef}, ${sizeArg}, ${widthArg}, ${supplyArg}, ${numRequesters});`
1020
+ )
1021
+ break
1022
+ case 'js':
1023
+ ctx.emitPreInnerLoop(
1024
+ ` let ${tmpVarId} = fns.ALLOCATE_BY_PRIORITY(${reqRef}, ${priorityRef}, ${sizeArg}, ${widthArg}, ${supplyArg}, ${numRequesters});`
1025
+ )
1026
+ break
1027
+ default:
1028
+ throw new Error(`Unhandled output format '${ctx.outFormat}'`)
1029
+ }
1030
+
1031
+ // Generate the RHS expression used in the inner loop
1032
+ return `${tmpVarId}[${allocDimId}[${allocLoopIndexVar}]]`
1033
+ }
1034
+
947
1035
  /**
948
1036
  * Recursively traverse the given expression and call the function when visiting a variable ref.
949
1037
  *
@@ -481,6 +481,11 @@ function visitFunctionCall(v, callExpr, context) {
481
481
  validateCallArgs(callExpr, 3)
482
482
  break
483
483
 
484
+ case '_ALLOCATE_BY_PRIORITY':
485
+ validateCallDepth(callExpr, context)
486
+ validateCallArgs(callExpr, 5)
487
+ break
488
+
484
489
  case '_DELAY1':
485
490
  case '_DELAY1I':
486
491
  case '_DELAY3':
@@ -880,6 +885,9 @@ function visitFunctionCall(v, callExpr, context) {
880
885
  }
881
886
  }
882
887
  continue
888
+ } else if (callExpr.fnId === '_ALLOCATE_BY_PRIORITY') {
889
+ // TODO: Throw an error if the last dimension of arg0 does not match last dimension of LHS
890
+ // TODO: Throw an error if the last dimension of arg1 does not match last dimension of LHS
883
891
  }
884
892
 
885
893
  context.setArgIndex(index, argModes[index])