Skip to content

Commit 7ff1380

Browse files
fix: handle case where subscript/index appears in expression position (#398)
Fixes #397
1 parent 76f890c commit 7ff1380

7 files changed

Lines changed: 94 additions & 6 deletions

File tree

models/subscript/subscript.dat

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,12 @@ u[C4]
176176
0 4
177177
u[C5]
178178
0 5
179+
v[A1]
180+
0 0
181+
1 0
182+
v[A2]
183+
0 1
184+
1 1
185+
v[A3]
186+
0 0
187+
1 0

models/subscript/subscript.mdl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ u[C3] = 3 ~~~:SUPPLEMENTARY|
5353
u[C4] = 4 ~~~:SUPPLEMENTARY|
5454
u[C5] = 5 ~~~:SUPPLEMENTARY|
5555

56+
v[DimA] = IF THEN ELSE (DimA = A2, 1, 0)
57+
~
58+
~ dimension name and subscript/index name both referenced in an expression
59+
~:SUPPLEMENTARY
60+
|
61+
5662
********************************************************
5763
.Control
5864
********************************************************~

packages/compile/src/generate/equation-gen.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -997,13 +997,18 @@ export default class EquationGen extends ModelReader {
997997
// Emit the size of the dimension in place of the dimension name.
998998
this.emit(`${sub(varName).size}`)
999999
} else {
1000-
// A subscript masquerading as a variable takes the value of the loop index var plus one
1001-
// (since Vensim indices are one-based).
1000+
// A dimension masquerading as a variable (i.e., in expression position) takes the
1001+
// value of the loop index var plus one (since Vensim indices are one-based).
10021002
let s = this.rhsSubscriptGen([varName])
10031003
// Remove the brackets around the C subscript expression.
10041004
s = s.slice(1, s.length - 1)
10051005
this.emit(`(${s} + 1)`)
10061006
}
1007+
} else if (isIndex(varName)) {
1008+
// A subscript masquerading as a variable (i.e., in expression position) takes the
1009+
// numeric index value plus one (since Vensim indices are one-based).
1010+
const index = sub(varName).value
1011+
this.emit(`${index + 1}`)
10071012
} else {
10081013
this.varNames.push(varName)
10091014
if (functionName === '_VECTOR_SELECT') {

packages/compile/src/generate/gen-equation-c.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,34 @@ describe('generateEquation (Vensim -> C)', () => {
228228
expect(genC(vars.get('_y'))).toEqual(['_y = _IF_THEN_ELSE(!_z, 1.0, 0.0);'])
229229
})
230230

231+
it('should work for conditional expression with reference to dimension', () => {
232+
const vars = readInlineModel(`
233+
DimA: A1, A2 ~~|
234+
x = 1 ~~|
235+
y[DimA] = IF THEN ELSE(DimA = x, 1, 0) ~~|
236+
`)
237+
expect(vars.size).toBe(2)
238+
expect(genC(vars.get('_x'))).toEqual(['_x = 1.0;'])
239+
expect(genC(vars.get('_y'))).toEqual([
240+
'for (size_t i = 0; i < 2; i++) {',
241+
'_y[i] = _IF_THEN_ELSE((i + 1) == _x, 1.0, 0.0);',
242+
'}'
243+
])
244+
})
245+
246+
it('should work for conditional expression with reference to dimension and subscript/index', () => {
247+
const vars = readInlineModel(`
248+
DimA: A1, A2 ~~|
249+
y[DimA] = IF THEN ELSE(DimA = A2, 1, 0) ~~|
250+
`)
251+
expect(vars.size).toBe(1)
252+
expect(genC(vars.get('_y'))).toEqual([
253+
'for (size_t i = 0; i < 2; i++) {',
254+
'_y[i] = _IF_THEN_ELSE((i + 1) == 2, 1.0, 0.0);',
255+
'}'
256+
])
257+
})
258+
231259
it('should work for data variable definition', () => {
232260
const extData: ExtData = new Map([
233261
[

packages/compile/src/model/equation-reader.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import {
2222
import {
2323
extractMarkedDims,
2424
indexNamesForSubscript,
25+
isDimension,
26+
isIndex,
2527
normalizeSubscripts,
2628
separatedVariableIndex,
27-
sub,
28-
isDimension
29+
sub
2930
} from '../_shared/subscript.js'
3031
import ModelReader from '../parse/model-reader.js'
3132
import { createParser } from '../parse/parser.js'
@@ -238,8 +239,8 @@ export default class EquationReader extends ModelReader {
238239
// Get the var name of a variable in a call and save it as a reference.
239240
let id = ctx.Id().getText()
240241
let varName = canonicalName(id)
241-
// Do not add a dimension name as a reference.
242-
if (!isDimension(varName)) {
242+
// Do not add a dimension or index name as a reference.
243+
if (!isDimension(varName) && !isIndex(varName)) {
243244
let fn = this.currentFunctionName()
244245
this.refId = varName
245246
this.expandedRefIds = []

packages/compile/src/model/read-equations.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,38 @@ describe('readEquations', () => {
119119
])
120120
})
121121

122+
it('should work for conditional expression with reference to dimension', () => {
123+
const vars = readInlineModel(`
124+
DimA: A1, A2 ~~|
125+
x = 1 ~~|
126+
y[DimA] = IF THEN ELSE(DimA = x, 1, 0) ~~|
127+
`)
128+
expect(vars).toEqual([
129+
v('x', '1', {
130+
refId: '_x',
131+
varType: 'const'
132+
}),
133+
v('y[DimA]', 'IF THEN ELSE(DimA=x,1,0)', {
134+
refId: '_y',
135+
subscripts: ['_dima'],
136+
references: ['_x']
137+
})
138+
])
139+
})
140+
141+
it('should work for conditional expression with reference to dimension and subscript/index', () => {
142+
const vars = readInlineModel(`
143+
DimA: A1, A2 ~~|
144+
y[DimA] = IF THEN ELSE(DimA = A2, 1, 0) ~~|
145+
`)
146+
expect(vars).toEqual([
147+
v('y[DimA]', 'IF THEN ELSE(DimA=A2,1,0)', {
148+
refId: '_y',
149+
subscripts: ['_dima']
150+
})
151+
])
152+
})
153+
122154
it('should work for data variable definition', () => {
123155
const vars = readInlineModel(
124156
`
@@ -7693,6 +7725,10 @@ describe('readEquations', () => {
76937725
subscripts: ['_c5'],
76947726
varType: 'const'
76957727
}),
7728+
v('v[DimA]', 'IF THEN ELSE(DimA=A2,1,0)', {
7729+
refId: '_v',
7730+
subscripts: ['_dima']
7731+
}),
76967732
v('Time', '', {
76977733
refId: '_time',
76987734
varType: 'const'

packages/compile/src/model/read-variables.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,9 @@ describe('readVariables', () => {
13501350
v('u[C5]', '5', {
13511351
subscripts: ['_c5']
13521352
}),
1353+
v('v[DimA]', 'IF THEN ELSE(DimA=A2,1,0)', {
1354+
subscripts: ['_dima']
1355+
}),
13531356
v('Time', '')
13541357
])
13551358
})

0 commit comments

Comments
 (0)