|
| 1 | +import { readFileSync } from 'fs' |
1 | 2 | import { resolve } from 'path' |
2 | 3 | import { fileURLToPath } from 'url' |
3 | 4 |
|
| 5 | +import type { Model } from '@sdeverywhere/parse' |
| 6 | + |
4 | 7 | import type { VensimModelParseTree } from '../parse/parser' |
5 | | -import { parseModel } from '../parse/parser' |
6 | 8 | import { preprocessModel } from '../preprocess/preprocessor' |
7 | 9 | import { canonicalName } from '../_shared/helpers' |
| 10 | +import { parseModel } from '../parse-and-generate' |
| 11 | + |
| 12 | +export interface ParsedVensimModel { |
| 13 | + kind: 'vensim' |
| 14 | + root: Model |
| 15 | +} |
| 16 | + |
| 17 | +export interface LegacyParsedVensimModel { |
| 18 | + kind: 'vensim-legacy' |
| 19 | + parseTree: VensimModelParseTree |
| 20 | +} |
| 21 | + |
| 22 | +export type ParsedModel = ParsedVensimModel | LegacyParsedVensimModel |
8 | 23 |
|
9 | 24 | export type DimModelName = string |
10 | 25 | export type DimCName = string |
@@ -49,6 +64,7 @@ export type VariableType = 'const' | 'aux' | 'level' | 'initial' | 'lookup' | 'd |
49 | 64 | export interface Variable { |
50 | 65 | modelLHS: string // 'Target Capacity' |
51 | 66 | modelFormula: string // 'ACTIVE INITIAL(Capacity*Utilization Adjustment,Initial Target Capacity)' |
| 67 | + origModelFormula?: string // 'IF THEN ELSE(cond, x, y)' |
52 | 68 | varName: string // '_target_capacity' |
53 | 69 | subscripts: string[] // TODO: sub type |
54 | 70 | exceptSubscripts: string[] // TODO: This is only used during parsing, doesn't need to be exposed |
@@ -140,14 +156,31 @@ export function sampleModelDir(modelName: string): string { |
140 | 156 | return resolve(__dirname, '..', '..', '..', '..', 'models', modelName) |
141 | 157 | } |
142 | 158 |
|
143 | | -export function parseVensimModel(modelName: string): VensimModelParseTree { |
144 | | - const modelFile = resolve(sampleModelDir(modelName), `${modelName}.mdl`) |
145 | | - const preprocessed = preprocessModel(modelFile, undefined, 'genc', false) |
146 | | - return parseModel(preprocessed) |
| 159 | +export function parseVensimModel(modelName: string): ParsedModel { |
| 160 | + const modelDir = sampleModelDir(modelName) |
| 161 | + const modelFile = resolve(modelDir, `${modelName}.mdl`) |
| 162 | + let mdlContent: string |
| 163 | + if (process.env.SDE_NONPUBLIC_USE_NEW_PARSE === '1') { |
| 164 | + // Note that the new parser implicitly runs the preprocessor on the input model text, |
| 165 | + // so we don't need to do that here. (We should make it configurable so that we can |
| 166 | + // skip the preprocess step in `parse-and-generate.js` when the input model text has |
| 167 | + // already been run through a preprocessor.) |
| 168 | + mdlContent = readFileSync(modelFile, 'utf8') |
| 169 | + } else { |
| 170 | + mdlContent = preprocessModel(modelFile, undefined, 'genc', false) |
| 171 | + } |
| 172 | + // We currently sort the preprocessed definitions alphabetically for |
| 173 | + // compatibility with the legacy preprocessor. Once we drop the legacy code |
| 174 | + // we could remove this step and update the tests to use the original order. |
| 175 | + return parseModel(mdlContent, modelDir, /*sort=*/ true) |
147 | 176 | } |
148 | 177 |
|
149 | | -export function parseInlineVensimModel(mdl: string): VensimModelParseTree { |
150 | | - return parseModel(mdl) |
| 178 | +export function parseInlineVensimModel(mdlContent: string, modelDir?: string): ParsedModel { |
| 179 | + // For tests that parse inline model text, in the case of the legacy parser, don't run |
| 180 | + // the preprocess step, and in the case of the new parser (which implicitly runs the |
| 181 | + // preprocess step), don't sort the definitions. This makes it easier to do apples |
| 182 | + // to apples comparisons on the outputs from the two parser implementations. |
| 183 | + return parseModel(mdlContent, modelDir, /*sort=*/ false) |
151 | 184 | } |
152 | 185 |
|
153 | 186 | function prettyVar(variable: Variable): string { |
|
0 commit comments