import { isFunctionNode, isOperatorNode, type ConditionalNode } from 'mathjs'; import type { State } from './state.js'; import type { Options, Result } from './interface.js'; import { toBoolean } from './to-type.js'; import { migrateAtomic } from './node.js'; import { constantValue } from './utils.js'; /** 三目运算 */ export function migrateConditional(state: State, node: ConditionalNode, options: Options): Result { const { condition, trueExpr, falseExpr } = node; if (constantValue(trueExpr) === true && constantValue(falseExpr) === false) { // x ? true : false const condRet = toBoolean(state, condition); let { code } = condRet; code += ' ?? false'; if (options.format === 'paren') { code = `(${code})`; } return { type: 'boolean', code, }; } if (constantValue(trueExpr) === false && constantValue(falseExpr) === true) { // x ? false : true const condRet = toBoolean(state, condition); let { code } = condRet; code = `!(${code} ?? false)`; return { type: 'boolean', code, }; } if ( // is(x, 'string') isFunctionNode(condition) && condition.fn.isSymbolNode && condition.fn.name === 'is' && condition.args.length === 2 && constantValue(condition.args[1]!) === 'string' && // ? count(x) != 0 isOperatorNode(trueExpr) && trueExpr.op === '!=' && isFunctionNode(trueExpr.args[0]!) && trueExpr.args[0].fn.isSymbolNode && trueExpr.args[0].fn.name === 'count' && trueExpr.args[0].args.length === 1 && constantValue(trueExpr.args[1]!) === 0 && // : false constantValue(falseExpr) === false && // same arg condition.args[0]!.equals(trueExpr.args[0].args[0]!) ) { // is(x, 'string') ? count(x) != 0 : false const x = migrateAtomic(state, condition.args[0]!); let code = `type(${x.code}) == 'string' && ${x.code} != ''`; if (options.format === 'paren') { code = `(${code})`; } return { type: 'boolean', code, }; } if ( // is(x, 'string') isFunctionNode(condition) && condition.fn.isSymbolNode && condition.fn.name === 'is' && condition.args.length === 2 && constantValue(condition.args[1]!) === 'string' && // ? equalText(x, "xxx") isFunctionNode(trueExpr) && trueExpr.fn.isSymbolNode && trueExpr.fn.name === 'equalText' && trueExpr.args.length === 2 && typeof constantValue(trueExpr.args[1]!) === 'string' && // : false constantValue(falseExpr) === false && // same arg condition.args[0]!.equals(trueExpr.args[0]!) ) { // is(x, 'string') ? equalText(x, "xxx") : false const x = migrateAtomic(state, condition.args[0]!); const xxx = migrateAtomic(state, trueExpr.args[1]!); let code = `${x.code} == ${xxx.code}`; if (options.format === 'paren') { code = `(${code})`; } return { type: 'boolean', code, }; } const condRet = toBoolean(state, condition); let trueRet = migrateAtomic(state, trueExpr); let falseRet = migrateAtomic(state, falseExpr); if (!(trueRet.code.startsWith('{') && trueRet.code.endsWith('}'))) { trueRet = { ...trueRet, code: `{ ${trueRet.code} }` }; } if (!((falseRet.code.startsWith('{') || falseRet.code.startsWith('if ')) && falseRet.code.endsWith('}'))) { falseRet = { ...falseRet, code: `{ ${falseRet.code} }` }; } let code = `if ${condRet.code}${condRet.code.startsWith('@@to_boolean(') ? ' ?? false' : ''} ${trueRet.code} else ${falseRet.code}`; if (options.format === 'paren') { code = `(${code})`; } return { type: trueRet.type === falseRet.type ? trueRet.type : undefined, code, }; }