bitburner-src/src/Terminal/Parser.ts
2021-09-16 18:10:28 -04:00

128 lines
3.8 KiB
TypeScript

import { substituteAliases } from "../Alias";
// Helper function that checks if an argument (which is a string) is a valid number
function isNumber(str: string): boolean {
if (typeof str != "string") {
return false;
} // Only process strings
return !isNaN(parseFloat(str));
}
export function ParseCommands(commands: string): string[] {
// Sanitize input
commands = commands.trim();
// Replace all extra whitespace in command with a single space
commands = commands.replace(/\s\s+/g, " ");
const match = commands.match(/(?:'[^']*'|"[^"]*"|[^;"])*/g);
if (!match) return [];
// Split commands and execute sequentially
const allCommands = match
.map(substituteAliases)
.map((c) => c.match(/(?:'[^']*'|"[^"]*"|[^;"])*/g))
.flat();
const out: string[] = [];
for (const c of allCommands) {
if (c === null) continue;
if (c.match(/^\s*$/)) {
continue;
} // Don't run commands that only have whitespace
out.push(c.trim());
}
return out;
}
export function ParseCommand(command: string): (string | number)[] {
// This will be used to keep track of whether we're in a quote. This is for situations
// like the alias command:
// alias run="run NUKE.exe"
// We want the run="run NUKE.exe" to be parsed as a single command, so this flag
// will keep track of whether we have a quote in
let inQuote = ``;
// Returns an array with the command and its arguments in each index
// Properly handles quotation marks (e.g. `run foo.script "the sun"` will return [run, foo.script, the sun])
const args = [];
let start = 0,
i = 0;
let prevChar = ""; // Previous character
while (i < command.length) {
let escaped = false; // Check for escaped quotation marks
if (i >= 1) {
prevChar = command.charAt(i - 1);
if (prevChar === "\\") {
escaped = true;
}
}
const c = command.charAt(i);
if (c === '"') {
// Double quotes
if (!escaped && prevChar === " ") {
const endQuote = command.indexOf('"', i + 1);
if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === " ")) {
args.push(command.substr(i + 1, endQuote - i - 1));
if (endQuote === command.length - 1) {
start = i = endQuote + 1;
} else {
start = i = endQuote + 2; // Skip the space
}
continue;
}
} else {
if (inQuote === ``) {
inQuote = `"`;
} else if (inQuote === `"`) {
inQuote = ``;
}
}
} else if (c === "'") {
// Single quotes, same thing as above
if (!escaped && prevChar === " ") {
const endQuote = command.indexOf("'", i + 1);
if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === " ")) {
args.push(command.substr(i + 1, endQuote - i - 1));
if (endQuote === command.length - 1) {
start = i = endQuote + 1;
} else {
start = i = endQuote + 2; // Skip the space
}
continue;
}
} else {
if (inQuote === ``) {
inQuote = `'`;
} else if (inQuote === `'`) {
inQuote = ``;
}
}
} else if (c === " " && inQuote === ``) {
const arg = command.substr(start, i - start);
// If this is a number, convert it from a string to number
if (isNumber(arg)) {
args.push(parseFloat(arg));
} else {
args.push(arg);
}
start = i + 1;
}
++i;
}
// Add the last argument
if (start !== i) {
const arg = command.substr(start, i - start);
// If this is a number, convert it from string to number
if (isNumber(arg)) {
args.push(parseFloat(arg));
} else {
args.push(arg);
}
}
return args;
}