import { isConstantNode, isFunctionNode, isSymbolNode, type MathNode } from 'mathjs'; import type { State } from './state.js'; import { migrateNode } from './node.js'; import { isResult, type Result } from './interface.js'; /** 生成转义序列 */ function escapeString(str: string | number): string { return String(str) .replaceAll('\\', String.raw`\\`) .replaceAll('`', '\\`') .replaceAll('$', String.raw`\$`) .replaceAll('\n', String.raw`\n`) .replaceAll('\r', String.raw`\r`) .replaceAll('\t', String.raw`\t`); } /** 连接字符串 */ export function concat(state: State, args: ReadonlyArray): Result { const parts: string[] = []; for (const arg of args) { if (typeof arg != 'object') { parts.push(escapeString(arg)); } else if (isConstantNode(arg)) { const value = escapeString(String(arg.value)); parts.push(value); } else if (isResult(arg)) { if (arg.literal && typeof arg.literal != 'object') { parts.push(String(arg.literal).replaceAll('`', '\\`')); } else if (arg.type === 'string' && arg.code.startsWith('to_string(') && arg.code.endsWith(')')) { parts.push(`$(${arg.code.slice(10, -1)})`); } else { parts.push(`$(${arg.code})`); } } else if (isFunctionNode(arg) && isSymbolNode(arg.fn) && arg.fn.name === 'string') { parts.push(`$(${migrateNode(state, arg.args[0]!, { format: 'no-paren' }).code})`); } else { parts.push(`$(${migrateNode(state, arg, { format: 'no-paren' }).code})`); } } return { code: '`' + parts.join('') + '`', type: 'string' }; }