import type { ParsedCliCommand, CliCommandArgs } from './CliCommand'; /** * 命令解析错误 */ export class CommandParseError extends Error { constructor(message: string) { super(message); this.name = 'CommandParseError'; } } /** * CLI 命令解析器 * 解析 CLI 风格的命令字符串 */ export class CommandParser { /** * 解析命令字符串 * @param input 命令字符串,如 "move card-1 discard --faceup=true" */ parse(input: string): ParsedCliCommand { const trimmed = input.trim(); if (!trimmed) { throw new CommandParseError('Empty command'); } const tokens = this.tokenize(trimmed); if (tokens.length === 0) { throw new CommandParseError('No command found'); } const commandName = tokens[0]; const args = this.parseArgs(tokens.slice(1)); return { commandName, args, raw: input, }; } /** * 将命令字符串分词 */ private tokenize(input: string): string[] { const tokens: string[] = []; let current = ''; let inQuotes = false; let quoteChar = ''; for (let i = 0; i < input.length; i++) { const char = input[i]; if (inQuotes) { if (char === quoteChar) { tokens.push(current); current = ''; inQuotes = false; } else { current += char; } } else if (char === '"' || char === "'") { inQuotes = true; quoteChar = char; } else if (char === ' ' || char === '\t') { if (current) { tokens.push(current); current = ''; } } else { current += char; } } if (current) { tokens.push(current); } if (inQuotes) { throw new CommandParseError('Unclosed quote in command'); } return tokens; } /** * 解析参数 */ private parseArgs(tokens: string[]): CliCommandArgs { const positional: string[] = []; const flags: Record = {}; for (const token of tokens) { if (token.startsWith('--')) { // 长标志 --key=value 或 --flag const flagMatch = token.match(/^--([^=]+)(?:=(.+))?$/); if (flagMatch) { const [, key, value] = flagMatch; flags[key] = value !== undefined ? this.parseFlagValue(value) : true; } } else if (token.startsWith('-') && token.length === 2) { // 短标志 -f 或 -k=v const flagMatch = token.match(/^-([^=]+)(?:=(.+))?$/); if (flagMatch) { const [, key, value] = flagMatch; flags[key] = value !== undefined ? this.parseFlagValue(value) : true; } } else { // 位置参数 positional.push(token); } } return { positional, flags }; } /** * 解析标志值 */ private parseFlagValue(value: string): string | boolean { // 布尔值 if (value.toLowerCase() === 'true') return true; if (value.toLowerCase() === 'false') return false; // 数字转换为字符串 return value; } /** * 格式化命令用于显示 */ static formatCommand(name: string, args?: CliCommandArgs): string { if (!args) { return name; } const parts = [name]; // 添加位置参数 parts.push(...args.positional); // 添加标志参数 for (const [key, value] of Object.entries(args.flags)) { if (value === true) { parts.push(`--${key}`); } else { parts.push(`--${key}=${value}`); } } return parts.join(' '); } } /** * 创建命令解析器 */ export function createCommandParser(): CommandParser { return new CommandParser(); }