@sdeverywhere/compile 0.7.0
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/LICENSE +21 -0
- package/README.md +12 -0
- package/package.json +40 -0
- package/src/_shared/helpers.js +351 -0
- package/src/_shared/read-dat.js +71 -0
- package/src/_shared/subscript.js +373 -0
- package/src/generate/code-gen.js +356 -0
- package/src/generate/equation-gen.js +1188 -0
- package/src/generate/loop-index-vars.js +27 -0
- package/src/generate/model-lhs-reader.js +88 -0
- package/src/index.js +8 -0
- package/src/model/equation-reader.js +708 -0
- package/src/model/expr-reader.js +202 -0
- package/src/model/model.js +1045 -0
- package/src/model/subscript-range-reader.js +123 -0
- package/src/model/toposort.js +100 -0
- package/src/model/var-name-reader.js +40 -0
- package/src/model/variable-reader.js +172 -0
- package/src/model/variable.js +111 -0
- package/src/parse/model-reader.js +141 -0
- package/src/parse/parser.js +32 -0
- package/src/parse-and-generate.js +114 -0
- package/src/preprocess/preprocessor.js +247 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { ModelLexer, ModelVisitor } from 'antlr4-vensim'
|
|
2
|
+
|
|
3
|
+
import { canonicalName } from '../_shared/helpers.js'
|
|
4
|
+
import { createParser } from '../parse/parser.js'
|
|
5
|
+
|
|
6
|
+
import Model from './model.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reads an expression and determines if it resolves to a constant numeric value.
|
|
10
|
+
* This depends on having access to the model variables, so this should be used
|
|
11
|
+
* only after the `readVariables` process has been completed and the spec file
|
|
12
|
+
* has been loaded.
|
|
13
|
+
*/
|
|
14
|
+
export default class ExprReader extends ModelVisitor {
|
|
15
|
+
constructor() {
|
|
16
|
+
super()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
read(exprText) {
|
|
20
|
+
let parser = createParser(exprText)
|
|
21
|
+
let expr = parser.expr()
|
|
22
|
+
expr.accept(this)
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
constantValue: this.constantValue
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Constants
|
|
30
|
+
|
|
31
|
+
visitConst(ctx) {
|
|
32
|
+
const constantValue = parseFloat(ctx.Const().getText())
|
|
33
|
+
if (!Number.isNaN(constantValue)) {
|
|
34
|
+
this.constantValue = constantValue
|
|
35
|
+
} else {
|
|
36
|
+
this.constantValue = undefined
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
visitConstList() {
|
|
40
|
+
this.constantValue = undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Function calls and variables
|
|
44
|
+
|
|
45
|
+
visitCall() {
|
|
46
|
+
// Treat function calls as non-constant (can't easily determine if they
|
|
47
|
+
// resolve to a constant)
|
|
48
|
+
this.constantValue = undefined
|
|
49
|
+
}
|
|
50
|
+
visitExprList() {
|
|
51
|
+
// Treat function calls as non-constant (can't easily determine if they
|
|
52
|
+
// resolve to a constant)
|
|
53
|
+
this.constantValue = undefined
|
|
54
|
+
}
|
|
55
|
+
visitVar(ctx) {
|
|
56
|
+
// Determine whether this variable has a constant value
|
|
57
|
+
const varName = ctx.Id().getText().trim()
|
|
58
|
+
const cName = canonicalName(varName)
|
|
59
|
+
const v = Model.varWithName(cName)
|
|
60
|
+
const modelFormula = v?.modelFormula?.trim() || ''
|
|
61
|
+
const isNumber = modelFormula.match(/^[+-]?\d+(\.\d+)?$/)
|
|
62
|
+
const canBeOverridden = Model.isInputVar(cName)
|
|
63
|
+
if (v && isNumber && !canBeOverridden) {
|
|
64
|
+
const numValue = parseFloat(modelFormula)
|
|
65
|
+
if (!Number.isNaN(numValue)) {
|
|
66
|
+
this.constantValue = numValue
|
|
67
|
+
} else {
|
|
68
|
+
this.constantValue = undefined
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
this.constantValue = undefined
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Lookups
|
|
76
|
+
|
|
77
|
+
visitLookup() {
|
|
78
|
+
this.constantValue = undefined
|
|
79
|
+
}
|
|
80
|
+
visitLookupCall() {
|
|
81
|
+
this.constantValue = undefined
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Unary operators
|
|
85
|
+
|
|
86
|
+
visitNegative(ctx) {
|
|
87
|
+
super.visitNegative(ctx)
|
|
88
|
+
if (this.constantValue !== undefined) {
|
|
89
|
+
this.constantValue = -this.constantValue
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
visitPositive(ctx) {
|
|
93
|
+
super.visitPositive(ctx)
|
|
94
|
+
if (this.constantValue !== undefined) {
|
|
95
|
+
this.constantValue = +this.constantValue
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
visitNot(ctx) {
|
|
99
|
+
super.visitNot(ctx)
|
|
100
|
+
if (this.constantValue !== undefined) {
|
|
101
|
+
this.constantValue = !this.constantValue
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Binary operators
|
|
106
|
+
|
|
107
|
+
visitBinaryArgs(ctx, combine) {
|
|
108
|
+
ctx.expr(0).accept(this)
|
|
109
|
+
const constantValue0 = this.constantValue
|
|
110
|
+
ctx.expr(1).accept(this)
|
|
111
|
+
const constantValue1 = this.constantValue
|
|
112
|
+
if (constantValue0 !== undefined && constantValue1 !== undefined) {
|
|
113
|
+
this.constantValue = combine(constantValue0, constantValue1)
|
|
114
|
+
} else {
|
|
115
|
+
this.constantValue = undefined
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
visitPower(ctx) {
|
|
120
|
+
this.visitBinaryArgs(ctx, (a, b) => Math.pow(a, b))
|
|
121
|
+
}
|
|
122
|
+
visitMulDiv(ctx) {
|
|
123
|
+
this.visitBinaryArgs(ctx, (a, b) => {
|
|
124
|
+
if (ctx.op.type === ModelLexer.Star) {
|
|
125
|
+
return a * b
|
|
126
|
+
} else {
|
|
127
|
+
return a / b
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
visitAddSub(ctx) {
|
|
132
|
+
this.visitBinaryArgs(ctx, (a, b) => {
|
|
133
|
+
if (ctx.op.type === ModelLexer.Plus) {
|
|
134
|
+
return a + b
|
|
135
|
+
} else {
|
|
136
|
+
return a - b
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
visitRelational(ctx) {
|
|
141
|
+
this.visitBinaryArgs(ctx, (a, b) => {
|
|
142
|
+
if (ctx.op.type === ModelLexer.Less) {
|
|
143
|
+
return a < b ? 1 : 0
|
|
144
|
+
} else if (ctx.op.type === ModelLexer.Greater) {
|
|
145
|
+
return a > b ? 1 : 0
|
|
146
|
+
} else if (ctx.op.type === ModelLexer.LessEqual) {
|
|
147
|
+
return a <= b ? 1 : 0
|
|
148
|
+
} else {
|
|
149
|
+
return a >= b ? 1 : 0
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
visitEquality(ctx) {
|
|
154
|
+
this.visitBinaryArgs(ctx, (a, b) => {
|
|
155
|
+
if (ctx.op.type === ModelLexer.Equal) {
|
|
156
|
+
return a === b ? 1 : 0
|
|
157
|
+
} else {
|
|
158
|
+
return a !== b ? 1 : 0
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
visitAnd(ctx) {
|
|
163
|
+
// For AND expressions, we don't need both sides to have a constant value; as
|
|
164
|
+
// long as one side is known to be false, then the expression resolves to false
|
|
165
|
+
ctx.expr(0).accept(this)
|
|
166
|
+
const constantValue0 = this.constantValue
|
|
167
|
+
ctx.expr(1).accept(this)
|
|
168
|
+
const constantValue1 = this.constantValue
|
|
169
|
+
if (constantValue0 !== undefined && constantValue1 !== undefined) {
|
|
170
|
+
this.constantValue = constantValue0 && constantValue1
|
|
171
|
+
} else if (constantValue0 !== undefined && !constantValue0) {
|
|
172
|
+
this.constantValue = 0
|
|
173
|
+
} else if (constantValue1 !== undefined && !constantValue1) {
|
|
174
|
+
this.constantValue = 0
|
|
175
|
+
} else {
|
|
176
|
+
this.constantValue = undefined
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
visitOr(ctx) {
|
|
180
|
+
// For OR expressions, we don't need both sides to have a constant value; as
|
|
181
|
+
// long as one side is known to be true, then the expression resolves to true
|
|
182
|
+
ctx.expr(0).accept(this)
|
|
183
|
+
const constantValue0 = this.constantValue
|
|
184
|
+
ctx.expr(1).accept(this)
|
|
185
|
+
const constantValue1 = this.constantValue
|
|
186
|
+
if (constantValue0 !== undefined && constantValue1 !== undefined) {
|
|
187
|
+
this.constantValue = constantValue0 || constantValue1
|
|
188
|
+
} else if (constantValue0 !== undefined && constantValue0) {
|
|
189
|
+
this.constantValue = 1
|
|
190
|
+
} else if (constantValue1 !== undefined && constantValue1) {
|
|
191
|
+
this.constantValue = 1
|
|
192
|
+
} else {
|
|
193
|
+
this.constantValue = undefined
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Tokens
|
|
198
|
+
|
|
199
|
+
visitParens(ctx) {
|
|
200
|
+
super.visitParens(ctx)
|
|
201
|
+
}
|
|
202
|
+
}
|