tinyrulelang/index.js

189 lines
5.0 KiB
JavaScript

/**
* 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<string>} 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"