/** * Adds a prefix to a string. * @param {string} str - The original string. * @param {string} prefix - The prefix to add. * @returns {string} The string with the prefix added. */ function addPrefix(str, prefix) { return prefix + str; } /** * Removes a prefix from a string. * @param {string} str - The original string. * @param {string} prefix - The prefix to remove. * @returns {string} The string with the prefix removed. */ function removePrefix(str, prefix) { if (str.startsWith(prefix)) { return str.slice(prefix.length); } return str; } /** * Adds a suffix to a string. * @param {string} str - The original string. * @param {string} suffix - The suffix to add. * @returns {string} The string with the suffix added. */ function addSuffix(str, suffix) { return str + suffix; } /** * Removes a suffix from a string. * @param {string} str - The original string. * @param {string} suffix - The suffix to remove. * @returns {string} The string with the suffix removed. */ function removeSuffix(str, suffix) { if (str.endsWith(suffix)) { return str.slice(0, -suffix.length); } return str; } /** * Tokenizes a string into tokens * @param {string} str * @returns {Array} tokens */ function tokenize(str) { const tokens = []; let currentToken = ""; let inQuotes = false; let escapeNext = false; for (let i = 0; i < str.length; i++) { const char = str[i]; if (escapeNext) { currentToken += char; escapeNext = false; } else if (char === "\\") { escapeNext = true; } else if (char === '"') { inQuotes = !inQuotes; } else if ((char === " " || char === "\n" || char === "\r") && !inQuotes) { if (currentToken !== "") { tokens.push(currentToken); currentToken = ""; } } else { currentToken += char; } } if (currentToken !== "") { tokens.push(currentToken); } return tokens; } /** * Manipulates the filename based on rules * @param {string} string * @param {string} filename * @returns */ function manipulateFilename(string, filename) { /** * @type {{dimension: string, action: string, target:string, value: string}} cmd */ let cmd; /** * @type {Array<{dimension: string, action: string, target:string, value: string}>} commands */ let commands = []; let state = "next"; for (const token of tokenize(string)) { switch (state) { case "next": if (token === "for") { cmd = {}; state = "dimension"; } else { throw new Error("Syntax Error, command must start with 'for'"); } break; case "dimension": if (token === "2D" || token === "3D") { cmd = { ...cmd, dimension: token }; state = "action"; } else { throw new Error("Syntax Error, command must be '2D' or '3D'"); } break; case "action": if (token === "add" || token === "remove") { cmd = { ...cmd, action: token }; state = "target"; } else { throw new Error("Syntax Error, command must be 'add' or 'remove'"); } break; case "target": if (token === "all" && cmd.action === "add") { throw new Error( "Syntax Error, you cannot use an 'all' target with an 'add' action, only 'remove' is allowed" ); } else if (token === "all" && cmd.action === "remove") { cmd = { ...cmd, target: token }; state = "value"; } else if (token === "prefix" || token === "suffix") { cmd = { ...cmd, target: token }; state = "value"; } else { throw new Error( "Syntax Error, command must be 'prefix', 'suffix', or 'all'" ); } break; case "value": cmd = { ...cmd, value: token }; commands.push(cmd); state = "more"; case "more": if (token === "and") { state = "next"; } break; default: break; } } for (const command of commands) { if (command.target === "prefix" && command.action === "add") { filename = addPrefix(filename, command.value); } if (command.target === "prefix" && command.action === "remove") { filename = removePrefix(filename, command.value); } if (command.target === "suffix" && command.action === "add") { filename = addSuffix(filename, command.value); } if (command.target === "suffix" && command.action === "remove") { filename = removeSuffix(filename, command.value); } if (command.target === "all" && command.action === "remove") { filename = filename.replace(command.value, ""); } } return filename; } const cmdStr = `for 2D add prefix "hello-" and for 2D add suffix "-3" and for 2D add suffix "-3" and for 2D add suffix "-4" and for 2D add suffix "-5" and for 2D remove all "-3"`; const filename = "example"; const result = manipulateFilename(cmdStr, filename); console.log(result); // Output: "hello-example-3"