mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-03-21 01:32:30 +01:00
Upgrade terminal ParseCommand function
Adds ability to parse arbitrarily-nested strings into single arguments (see comments on lines 48, 64, 76) Adds ability to parse additional number formats, e.g.: hexadecimal (0x0A), octal (018), and binary (0b1101) Combines arguments separated with escaped spaces into single string arguments, e.g.: "echo Hello\ World!" -> ["echo", "Hello World!"]
This commit is contained in:
committed by
GitHub
parent
9c805dabb8
commit
36db849ff1
@ -1,12 +1,21 @@
|
|||||||
import { KEY } from "../utils/helpers/keyCodes";
|
import { KEY } from "../utils/helpers/keyCodes";
|
||||||
import { substituteAliases } from "../Alias";
|
import { substituteAliases } from "../Alias";
|
||||||
// Helper function that checks if an argument (which is a string) is a valid number
|
// Helper function to parse individual arguments into number/boolean/string as appropriate
|
||||||
function isNumber(str: string): boolean {
|
function parseArg(arg) {
|
||||||
if (typeof str != "string") {
|
// Handles all numbers including hexadecimal, octal, and binary representations, returning NaN on an unparseable string
|
||||||
return false;
|
const asNumber = Number(arg);
|
||||||
} // Only process strings
|
if (!isNaN(asNumber)) {
|
||||||
return !isNaN(str as unknown as number) && !isNaN(parseFloat(str));
|
return asNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === 'true' || arg === 'false') {
|
||||||
|
return arg === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip quotation marks from strings that begin/end with the same mark
|
||||||
|
return arg.replace(/^"(.*?)"$/g, '$1').replace(/^'(.*?)'$/g, '$1');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ParseCommands(commands: string): string[] {
|
export function ParseCommands(commands: string): string[] {
|
||||||
// Sanitize input
|
// Sanitize input
|
||||||
commands = commands.trim();
|
commands = commands.trim();
|
||||||
@ -33,99 +42,67 @@ export function ParseCommands(commands: string): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ParseCommand(command: string): (string | number | boolean)[] {
|
export function ParseCommand(command: string): (string | number | boolean)[] {
|
||||||
// This will be used to keep track of whether we're in a quote. This is for situations
|
let idx = 0;
|
||||||
// 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 = [];
|
const args = [];
|
||||||
let start = 0,
|
|
||||||
i = 0;
|
// Track depth of quoted strings, e.g.: "the're 'going away' rather 'quickly \"and awkwardly\"'" should be parsed as a single string
|
||||||
let prevChar = ""; // Previous character
|
const quotes = [];
|
||||||
while (i < command.length) {
|
|
||||||
let escaped = false; // Check for escaped quotation marks
|
let arg = '';
|
||||||
if (i >= 1) {
|
while (idx < command.length) {
|
||||||
prevChar = command.charAt(i - 1);
|
const c = command.charAt(idx);
|
||||||
if (prevChar === "\\") {
|
|
||||||
escaped = true;
|
// If the current character is a backslash, add the next character verbatim to the argument
|
||||||
|
if (c === '\\') {
|
||||||
|
arg += command.charAt(++idx);
|
||||||
|
// If the current character is a single- or double-quote mark, add it to the current argument.
|
||||||
|
} else if (c === KEY.DOUBLE_QUOTE || c === KEY.QUOTE) {
|
||||||
|
arg += c;
|
||||||
|
const quote = quotes[quotes.length - 1];
|
||||||
|
const prev = command.charAt(idx - 1);
|
||||||
|
const next = command.charAt(idx + 1);
|
||||||
|
// If the previous character is a space or an equal sign this is a valid start to a new string.
|
||||||
|
// If we're already in a quoted string, push onto the stack of string starts to track depth.
|
||||||
|
if (
|
||||||
|
c !== quote &&
|
||||||
|
(
|
||||||
|
prev === KEY.SPACE ||
|
||||||
|
prev === KEY.EQUAL ||
|
||||||
|
(c === KEY.DOUBLE_QUOTE && prev === KEY.QUOTE) ||
|
||||||
|
(c === KEY.QUOTE && prev === KEY.DOUBLE_QUOTE)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
quotes.push(c);
|
||||||
|
// If the next character is a space and the current character is the same as the previously used
|
||||||
|
// quotation mark, this is a valid end to a string. Pop off the depth tracker.
|
||||||
|
} else if (
|
||||||
|
c === quote &&
|
||||||
|
(
|
||||||
|
next === KEY.SPACE ||
|
||||||
|
(c === KEY.DOUBLE_QUOTE && next === KEY.QUOTE) ||
|
||||||
|
(c === KEY.QUOTE && next === KEY.DOUBLE_QUOTE)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
quotes.pop();
|
||||||
}
|
}
|
||||||
}
|
// If the current character is a space and we are not inside a string, parse the current argument
|
||||||
|
// and start a new one
|
||||||
const c = command.charAt(i);
|
} else if (c === KEY.SPACE && quotes.length === 0) {
|
||||||
if (c === KEY.DOUBLE_QUOTE) {
|
args.push(parseArg(arg));
|
||||||
// Double quotes
|
|
||||||
if (!escaped && prevChar === KEY.SPACE) {
|
arg = '';
|
||||||
const endQuote = command.indexOf(KEY.DOUBLE_QUOTE, i + 1);
|
|
||||||
if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === KEY.SPACE)) {
|
|
||||||
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 = KEY.DOUBLE_QUOTE;
|
|
||||||
} else if (inQuote === KEY.DOUBLE_QUOTE) {
|
|
||||||
inQuote = ``;
|
|
||||||
}
|
|
||||||
} else if (c === KEY.QUOTE) {
|
|
||||||
// Single quotes, same thing as above
|
|
||||||
if (!escaped && prevChar === KEY.SPACE) {
|
|
||||||
const endQuote = command.indexOf(KEY.QUOTE, i + 1);
|
|
||||||
if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === KEY.SPACE)) {
|
|
||||||
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 = KEY.QUOTE;
|
|
||||||
} else if (inQuote === KEY.QUOTE) {
|
|
||||||
inQuote = ``;
|
|
||||||
}
|
|
||||||
} else if (c === KEY.SPACE && 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 if (arg === "true") {
|
|
||||||
args.push(true);
|
|
||||||
} else if (arg === "false") {
|
|
||||||
args.push(false);
|
|
||||||
} 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 if (arg === "true") {
|
|
||||||
args.push(true);
|
|
||||||
} else if (arg === "false") {
|
|
||||||
args.push(false);
|
|
||||||
} else {
|
} else {
|
||||||
args.push(arg);
|
// Add the current character to the current argument
|
||||||
|
arg += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the last arg (if any)
|
||||||
|
if (arg !== '') {
|
||||||
|
args.push(parseArg(arg));
|
||||||
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user