@sdeverywhere/compile 0.7.20 → 0.7.21

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.
@@ -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
- }