@sdeverywhere/compile 0.7.20 → 0.7.22
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 +3 -4
- package/src/_shared/helpers.js +8 -39
- package/src/_shared/subscript.js +1 -23
- package/src/generate/gen-code-c.js +10 -15
- package/src/generate/gen-code-js.js +8 -3
- package/src/generate/gen-const-list.js +9 -19
- package/src/generate/gen-equation.js +114 -79
- package/src/generate/gen-expr.js +67 -24
- package/src/generate/gen-lookup-from-points.js +17 -4
- package/src/generate/loop-index-vars.js +12 -0
- package/src/index.js +5 -1
- package/src/model/model.js +2 -6
- package/src/model/read-equation-fn-delay.js +55 -29
- package/src/model/read-equation-fn-game.js +2 -2
- package/src/model/read-equation-fn-npv.js +10 -3
- package/src/model/read-equation-fn-smooth.js +15 -8
- package/src/model/read-equation-fn-trend.js +6 -2
- package/src/model/read-equation-fn-with-lookup.js +1 -1
- package/src/model/read-equations.js +273 -119
- package/src/model/read-variables.js +13 -21
- package/src/model/toposort.js +1 -1
- package/src/parse-and-generate.js +10 -15
- package/src/preprocess/preprocessor.js +0 -247
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import B from 'bufx'
|
|
3
|
-
import * as R from 'ramda'
|
|
4
|
-
import { splitEquations, replaceDelimitedStrings } from '../_shared/helpers.js'
|
|
5
|
-
|
|
6
|
-
export let preprocessModel = (mdlFilename, spec, profile = 'runnable', writeFiles = false, outDecls = []) => {
|
|
7
|
-
const MACROS_FILENAME = 'macros.txt'
|
|
8
|
-
const REMOVALS_FILENAME = 'removals.txt'
|
|
9
|
-
const INSERTIONS_FILENAME = 'mdl-edits.txt'
|
|
10
|
-
const ENCODING = '{UTF-8}'
|
|
11
|
-
let profiles = {
|
|
12
|
-
// simplified but still runnable model
|
|
13
|
-
runnable: {
|
|
14
|
-
emitEncoding: true,
|
|
15
|
-
emitCommentMarkers: true,
|
|
16
|
-
joinFormulaLines: false
|
|
17
|
-
},
|
|
18
|
-
// even simpler model that does not run
|
|
19
|
-
analysis: {
|
|
20
|
-
emitEncoding: false,
|
|
21
|
-
emitCommentMarkers: false,
|
|
22
|
-
joinFormulaLines: true
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
let opts = profiles[profile]
|
|
26
|
-
let mdl, eqns
|
|
27
|
-
// Equations that contain a string in the removalKeys list in the spec file will be removed.
|
|
28
|
-
let removalKeys = (spec && spec.removalKeys) || []
|
|
29
|
-
// Optional insertions can be used to add expanded macros back into the model.
|
|
30
|
-
let insertions = ''
|
|
31
|
-
let getMdlFromPPBuf = () => {
|
|
32
|
-
// Reset the mdl string from the preprocessor buffer.
|
|
33
|
-
mdl = B.getBuf('pp')
|
|
34
|
-
B.clearBuf('pp')
|
|
35
|
-
}
|
|
36
|
-
let emitPP = str => {
|
|
37
|
-
if (str) {
|
|
38
|
-
B.emit(str, 'pp')
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Open output channels.
|
|
42
|
-
B.open('rm')
|
|
43
|
-
B.open('macros')
|
|
44
|
-
B.open('pp')
|
|
45
|
-
// Read the optional insertions file into the model unless we are doing a pass that writes removals.
|
|
46
|
-
try {
|
|
47
|
-
if (!writeFiles) {
|
|
48
|
-
let insPathname = path.join(path.dirname(mdlFilename), INSERTIONS_FILENAME)
|
|
49
|
-
insertions = B.read(insPathname)
|
|
50
|
-
}
|
|
51
|
-
} catch (error) {
|
|
52
|
-
// TODO: Handle error
|
|
53
|
-
}
|
|
54
|
-
// Read the model file.
|
|
55
|
-
try {
|
|
56
|
-
mdl = B.read(mdlFilename)
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error(error.message)
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
// Remove the macro section.
|
|
62
|
-
let inMacroSection = false
|
|
63
|
-
for (let line of B.lines(mdl)) {
|
|
64
|
-
if (!inMacroSection && R.contains(':MACRO:', line)) {
|
|
65
|
-
B.emitLine(line, 'macros')
|
|
66
|
-
inMacroSection = true
|
|
67
|
-
} else if (inMacroSection) {
|
|
68
|
-
B.emitLine(line, 'macros')
|
|
69
|
-
if (R.contains(':END OF MACRO:', line)) {
|
|
70
|
-
B.emit('\n', 'macros')
|
|
71
|
-
inMacroSection = false
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
B.emitLine(line, 'pp')
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
getMdlFromPPBuf()
|
|
78
|
-
|
|
79
|
-
// Split the model into an array of equations and groups.
|
|
80
|
-
eqns = splitEquations(mdl)
|
|
81
|
-
// Remove some equations into the removals channel.
|
|
82
|
-
for (let eqn of eqns) {
|
|
83
|
-
if (R.contains('\\---/// Sketch', eqn)) {
|
|
84
|
-
// Skip everything starting with the first sketch section.
|
|
85
|
-
break
|
|
86
|
-
} else if (R.contains('********************************************************', eqn)) {
|
|
87
|
-
// Skip groups
|
|
88
|
-
} else if (R.contains('TABBED ARRAY', eqn) || R.any(x => R.contains(x, eqn), removalKeys)) {
|
|
89
|
-
// Remove tabbed arrays and equations containing removal key strings from the spec.
|
|
90
|
-
B.emit(eqn, 'rm')
|
|
91
|
-
B.emit('|', 'rm')
|
|
92
|
-
} else if (!R.isEmpty(eqn)) {
|
|
93
|
-
// Emit the equation.
|
|
94
|
-
emitPP(eqn)
|
|
95
|
-
emitPP('|')
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
getMdlFromPPBuf()
|
|
99
|
-
|
|
100
|
-
// Join lines continued with trailing backslash characters.
|
|
101
|
-
let prevLine = ''
|
|
102
|
-
for (let line of B.lines(mdl)) {
|
|
103
|
-
// Join a previous line with a backslash ending to the current line.
|
|
104
|
-
if (!R.isEmpty(prevLine)) {
|
|
105
|
-
line = prevLine + line.trim()
|
|
106
|
-
prevLine = ''
|
|
107
|
-
}
|
|
108
|
-
let continuation = line.match(/\\\s*$/)
|
|
109
|
-
if (continuation) {
|
|
110
|
-
// If there is a backslash ending on this line, save it without the backslash.
|
|
111
|
-
prevLine = line.substr(0, continuation.index).replace(/\s+$/, ' ')
|
|
112
|
-
} else {
|
|
113
|
-
// With no continuation on this line, go ahead and emit it.
|
|
114
|
-
B.emitLine(line, 'pp')
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
getMdlFromPPBuf()
|
|
118
|
-
|
|
119
|
-
// Emit the encoding line and optional insertions.
|
|
120
|
-
if (opts.emitEncoding) {
|
|
121
|
-
B.emitLine(ENCODING, 'pp')
|
|
122
|
-
B.emitLine('', 'pp')
|
|
123
|
-
}
|
|
124
|
-
if (insertions) {
|
|
125
|
-
B.emitLine(insertions, 'pp')
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Split into separate equations
|
|
129
|
-
eqns = splitEquations(mdl)
|
|
130
|
-
|
|
131
|
-
// Extract the LHS variable name for each equation, which we will use to sort
|
|
132
|
-
// the equations alphabetically
|
|
133
|
-
const unsorted = outDecls
|
|
134
|
-
for (let eqn of eqns) {
|
|
135
|
-
// Ignore the encoding
|
|
136
|
-
eqn = eqn.replace('{UTF-8}', '')
|
|
137
|
-
// Remove ":RAW:" flag; it is not needed by SDE and causes problems if left in
|
|
138
|
-
eqn = eqn.replace(/:RAW:/g, '')
|
|
139
|
-
// Remove inline comments
|
|
140
|
-
eqn = replaceDelimitedStrings(eqn, '{', '}', '')
|
|
141
|
-
// Remove whitespace
|
|
142
|
-
eqn = eqn.trim()
|
|
143
|
-
if (eqn.length > 0) {
|
|
144
|
-
// Remove newlines so that we look at the full equation as a single line
|
|
145
|
-
let line = eqn.replace(/\n/g, ' ').trim()
|
|
146
|
-
let kind
|
|
147
|
-
let key = line
|
|
148
|
-
// Remove everything after the comment delimiters
|
|
149
|
-
key = key.split('~')[0]
|
|
150
|
-
// Strip the ":INTERPOLATE:"; it should not be included in the key
|
|
151
|
-
key = key.replace(/:INTERPOLATE:/g, '')
|
|
152
|
-
if (key.includes('=')) {
|
|
153
|
-
// The line contains an '='; treat this as an equation
|
|
154
|
-
kind = 'eqn'
|
|
155
|
-
key = key.split('=')[0].trim()
|
|
156
|
-
} else if (key.includes(':')) {
|
|
157
|
-
// The line contains a ':'; treat this as an subscript declaration
|
|
158
|
-
kind = 'sub'
|
|
159
|
-
key = key.split(':')[0].trim()
|
|
160
|
-
} else {
|
|
161
|
-
// Treat this as a general declaration
|
|
162
|
-
kind = 'decl'
|
|
163
|
-
}
|
|
164
|
-
// Ignore double quotes
|
|
165
|
-
key = key.replace(/"/g, '')
|
|
166
|
-
// Ignore the lookup data if it starts on the first line
|
|
167
|
-
key = key.split('(')[0]
|
|
168
|
-
// Ignore any whitespace that remains
|
|
169
|
-
key = key.trim()
|
|
170
|
-
// Remove whitespace on the inside of the brackets
|
|
171
|
-
key = key.replace(/\[\s*/g, '[')
|
|
172
|
-
key = key.replace(/\s*\]/g, ']')
|
|
173
|
-
// Ignore case
|
|
174
|
-
key = key.toLowerCase()
|
|
175
|
-
unsorted.push({
|
|
176
|
-
key,
|
|
177
|
-
kind,
|
|
178
|
-
originalDecl: eqn
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Sort the equations alphabetically by LHS variable name
|
|
184
|
-
const sorted = unsorted.sort((a, b) => {
|
|
185
|
-
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
// Emit formula lines without comment contents.
|
|
189
|
-
for (const elem of sorted) {
|
|
190
|
-
const eqn = elem.originalDecl
|
|
191
|
-
let processedDecl = eqn
|
|
192
|
-
let iComment = eqn.indexOf('~')
|
|
193
|
-
if (iComment >= 0) {
|
|
194
|
-
processedDecl = ''
|
|
195
|
-
let formula = B.lines(eqn.substr(0, iComment))
|
|
196
|
-
for (let i = 0; i < formula.length; i++) {
|
|
197
|
-
let line = formula[i]
|
|
198
|
-
// Remove trailing whitespace
|
|
199
|
-
line = line.replace(/\s+$/, '')
|
|
200
|
-
if (i === 0) {
|
|
201
|
-
if (line !== ENCODING) {
|
|
202
|
-
emitPP(line)
|
|
203
|
-
processedDecl += line
|
|
204
|
-
}
|
|
205
|
-
} else {
|
|
206
|
-
if (opts.joinFormulaLines) {
|
|
207
|
-
// Remove any leading tabs
|
|
208
|
-
const lineWithoutLeadingTabs = line.replace(/^\t+/, '')
|
|
209
|
-
emitPP(lineWithoutLeadingTabs)
|
|
210
|
-
processedDecl += lineWithoutLeadingTabs
|
|
211
|
-
} else {
|
|
212
|
-
// Only emit the line if it has non-whitespace characters
|
|
213
|
-
if (line.length > 0) {
|
|
214
|
-
emitPP(`\n${line}`)
|
|
215
|
-
processedDecl += `\n${line}`
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
// Emit the last line
|
|
221
|
-
if (opts.emitCommentMarkers) {
|
|
222
|
-
const declEnd = '\n\t~~|'
|
|
223
|
-
B.emitLine(`${declEnd}\n`, 'pp')
|
|
224
|
-
processedDecl += declEnd
|
|
225
|
-
} else {
|
|
226
|
-
B.emitLine('', 'pp')
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
elem.processedDecl = processedDecl
|
|
230
|
-
}
|
|
231
|
-
getMdlFromPPBuf()
|
|
232
|
-
|
|
233
|
-
// Write removals to a file in the model directory.
|
|
234
|
-
if (writeFiles) {
|
|
235
|
-
if (B.getBuf('macros')) {
|
|
236
|
-
let macrosPathname = path.join(path.dirname(mdlFilename), MACROS_FILENAME)
|
|
237
|
-
B.writeBuf(macrosPathname, 'macros')
|
|
238
|
-
}
|
|
239
|
-
if (B.getBuf('rm')) {
|
|
240
|
-
let rmPathname = path.join(path.dirname(mdlFilename), REMOVALS_FILENAME)
|
|
241
|
-
B.writeBuf(rmPathname, 'rm')
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Return the preprocessed model as a string.
|
|
246
|
-
return mdl
|
|
247
|
-
}
|