| 56 | } |
| 57 | |
| 58 | class TcbExprTranslator implements AstVisitor { |
| 59 | constructor( |
| 60 | private maybeResolve: (ast: AST) => TcbExpr | null, |
| 61 | private config: TypeCheckingConfig, |
| 62 | ) {} |
| 63 | |
| 64 | translate(ast: AST): TcbExpr { |
| 65 | if (ast instanceof ASTWithSource) { |
| 66 | ast = ast.ast; |
| 67 | } |
| 68 | |
| 69 | // First attempt to let any custom resolution logic provide a translation for the given node. |
| 70 | const resolved = this.maybeResolve(ast); |
| 71 | if (resolved !== null) { |
| 72 | return resolved; |
| 73 | } |
| 74 | |
| 75 | return ast.visit(this); |
| 76 | } |
| 77 | |
| 78 | visitUnary(ast: Unary): TcbExpr { |
| 79 | const expr = this.translate(ast.expr); |
| 80 | const node = new TcbExpr(`${ast.operator}${expr.print()}`); |
| 81 | return node.wrapForTypeChecker().addParseSpanInfo(ast.sourceSpan); |
| 82 | } |
| 83 | |
| 84 | visitBinary(ast: Binary): TcbExpr { |
| 85 | const lhs = this.translate(ast.left); |
| 86 | const rhs = this.translate(ast.right); |
| 87 | lhs.wrapForTypeChecker(); |
| 88 | rhs.wrapForTypeChecker(); |
| 89 | const expression = `${lhs.print()} ${ast.operation} ${rhs.print()}`; |
| 90 | const node = new TcbExpr( |
| 91 | ast.operation === '??' || ast.operation === '**' ? `(${expression})` : expression, |
| 92 | ); |
| 93 | node.addParseSpanInfo(ast.sourceSpan); |
| 94 | return node; |
| 95 | } |
| 96 | |
| 97 | visitChain(ast: Chain): TcbExpr { |
| 98 | const elements = ast.expressions.map((expr) => this.translate(expr).print()); |
| 99 | const node = new TcbExpr(elements.join(', ')); |
| 100 | node.wrapForTypeChecker(); |
| 101 | node.addParseSpanInfo(ast.sourceSpan); |
| 102 | return node; |
| 103 | } |
| 104 | |
| 105 | visitConditional(ast: Conditional): TcbExpr { |
| 106 | const condExpr = this.translate(ast.condition); |
| 107 | const trueExpr = this.translate(ast.trueExp); |
| 108 | // Wrap `falseExpr` in parens so that the trailing parse span info is not attributed to the |
| 109 | // whole conditional. |
| 110 | // In the following example, the last source span comment (5,6) could be seen as the |
| 111 | // trailing comment for _either_ the whole conditional expression _or_ just the `falseExpr` that |
| 112 | // is immediately before it: |
| 113 | // `conditional /*1,2*/ ? trueExpr /*3,4*/ : falseExpr /*5,6*/` |
| 114 | // This should be instead be `conditional /*1,2*/ ? trueExpr /*3,4*/ : (falseExpr /*5,6*/)` |
| 115 | const falseExpr = this.translate(ast.falseExp).wrapForTypeChecker(); |
nothing calls this directly
no outgoing calls
no test coverage detected