112 lines
2.9 KiB
JavaScript
112 lines
2.9 KiB
JavaScript
import { NameTerm, NumberTerm } from "./terms.js";
|
|
|
|
class ParseCommandError extends SyntaxError { }
|
|
|
|
class Parameter {
|
|
name;
|
|
value;
|
|
|
|
constructor(name, value) {
|
|
if (!name instanceof NameTerm) {
|
|
throw new TypeError("Parameter name must be a NameTerm");
|
|
}
|
|
|
|
this.name = name;
|
|
this.value = value;
|
|
}
|
|
|
|
toString() {
|
|
return this.value ? `${this.name}=${this.value}` : `${this.name}`;
|
|
}
|
|
|
|
valueOf() {
|
|
if (this.value !== undefined) {
|
|
return this.value.valueOf();
|
|
} else {
|
|
return this.name;
|
|
}
|
|
}
|
|
}
|
|
|
|
export class Command {
|
|
static stringRegex = /'[^'"]*'|"[^'"]*"/;
|
|
static whitespaceRegex = /\s+/;
|
|
|
|
static parse(commandString) {
|
|
const terms = commandString.split(Command.whitespaceRegex).filter(s => !!s);
|
|
|
|
if (terms.length < 1) {
|
|
return null;
|
|
}
|
|
|
|
let verb;
|
|
try {
|
|
verb = new NameTerm(terms[0]);
|
|
} catch {
|
|
throw new ParseCommandError(`Invalid verb: ${verb}`);
|
|
}
|
|
|
|
const parameters = terms.slice(1).map(t => {
|
|
const [nameString, valueString, ...rest] = t.split("=");
|
|
|
|
if (rest.length > 0) {
|
|
throw new ParseCommandError(`Invalid parameter: ${t}`);
|
|
}
|
|
|
|
let name;
|
|
try {
|
|
name = new NameTerm(nameString);
|
|
} catch {
|
|
throw new ParseCommandError(`Invalid parameter name: ${t}`);
|
|
}
|
|
|
|
let value;
|
|
if (valueString) {
|
|
for (const termClass of [NameTerm, NumberTerm]) {
|
|
try {
|
|
value = new termClass(valueString);
|
|
break;
|
|
} catch {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!value) {
|
|
throw new ParseCommandError(`Invalid parameter value: ${t}`);
|
|
}
|
|
}
|
|
|
|
return new Parameter(name, value);
|
|
});
|
|
|
|
return new Command(commandString, verb, parameters);
|
|
}
|
|
|
|
originalString;
|
|
verb;
|
|
parameters = []
|
|
|
|
constructor(originalString, verb, parameters) {
|
|
if (!verb instanceof String) {
|
|
throw new TypeError("Command verb must be a string");
|
|
}
|
|
|
|
this.originalString = originalString;
|
|
this.verb = verb;
|
|
this.parameters = parameters;
|
|
}
|
|
|
|
parameter(nameOrIndex) {
|
|
if (typeof nameOrIndex === "string" || nameOrIndex instanceof String) {
|
|
return this.parameters.find(p => p.name == nameOrIndex);
|
|
} else if (nameOrIndex instanceof Number) {
|
|
return this.parameters[nameOrIndex].valueOf();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
toString() {
|
|
return `Command: {verb: "${this.verb.toString()}", parameters: [${this.parameters.join(", ")}] }`;
|
|
}
|
|
}
|