(
program: PreprocessorProgram,
options: PreprocessorOptions = {}
)
| 475 | }; |
| 476 | |
| 477 | const preprocessAst = ( |
| 478 | program: PreprocessorProgram, |
| 479 | options: PreprocessorOptions = {} |
| 480 | ) => { |
| 481 | const macros: Macros = Object.entries(options.defines || {}).reduce( |
| 482 | (defines, [name, body]) => ({ ...defines, [name]: { body } }), |
| 483 | {} |
| 484 | ); |
| 485 | |
| 486 | const { preserve } = options; |
| 487 | const preserveNode = shouldPreserve(preserve); |
| 488 | |
| 489 | visitPreprocessedAst(program, { |
| 490 | conditional: { |
| 491 | enter: (initialPath) => { |
| 492 | const path = convertPath(initialPath); |
| 493 | const { node } = path; |
| 494 | // TODO: Determining if we need to handle edge case conditionals here |
| 495 | if (preserveNode(path)) { |
| 496 | return; |
| 497 | } |
| 498 | |
| 499 | // Expand macros in if/else *expressions* only. Macros are expanded in: |
| 500 | // #if X + 1 |
| 501 | // #elif Y + 2 |
| 502 | // But *not* in |
| 503 | // # ifdef X |
| 504 | // Because X should not be expanded in the ifdef. Note that |
| 505 | // # if defined(X) |
| 506 | // does have an expression, but the skip() in unary_defined prevents |
| 507 | // macro expansion in there. Checking for .expression and filtering out |
| 508 | // any conditionals without expressions is how ifdef is avoided. |
| 509 | // It's not great that ifdef is skipped differentaly than defined(). |
| 510 | expandInExpressions( |
| 511 | macros, |
| 512 | ...[ |
| 513 | (node.ifPart as PreprocessorIfNode).expression, |
| 514 | ...node.elseIfParts.map( |
| 515 | (elif: PreprocessorElseIfNode) => elif.expression |
| 516 | ), |
| 517 | ].filter(isTruthy) |
| 518 | ); |
| 519 | |
| 520 | if (evaluateIfPart(macros, node.ifPart)) { |
| 521 | path.replaceWith(node.ifPart.body); |
| 522 | } else { |
| 523 | const elseBranchHit = node.elseIfParts.reduce( |
| 524 | (res: boolean, elif: PreprocessorElseIfNode) => |
| 525 | res || |
| 526 | (evaluteExpression(elif.expression, macros) && |
| 527 | // path/visit hack to remove type error |
| 528 | (path.replaceWith(elif.body as PreprocessorAstNode), true)), |
| 529 | false |
| 530 | ); |
| 531 | if (!elseBranchHit) { |
| 532 | if (node.elsePart) { |
| 533 | path.replaceWith(node.elsePart.body as PreprocessorAstNode); |
| 534 | } else { |
no test coverage detected