blob: bec24a004bd56a38c1c9b55a2e6b539508569291 [file] [log] [blame]
import { Base } from "./base.js";
import {
return_type,
argument_list,
unescape,
autoParenter,
} from "./helpers.js";
import { validationError } from "../error.js";
export class Operation extends Base {
/**
* @param {import("../tokeniser.js").Tokeniser} tokeniser
* @param {object} [options]
* @param {import("../tokeniser.js").Token} [options.special]
* @param {import("../tokeniser.js").Token} [options.regular]
*/
static parse(tokeniser, { special, regular } = {}) {
const tokens = { special };
const ret = autoParenter(
new Operation({ source: tokeniser.source, tokens }),
);
if (special && special.value === "stringifier") {
tokens.termination = tokeniser.consume(";");
if (tokens.termination) {
ret.arguments = [];
return ret;
}
}
if (!special && !regular) {
tokens.special = tokeniser.consume("getter", "setter", "deleter");
}
ret.idlType =
return_type(tokeniser) || tokeniser.error("Missing return type");
tokens.name =
tokeniser.consumeKind("identifier") || tokeniser.consume("includes");
tokens.open =
tokeniser.consume("(") || tokeniser.error("Invalid operation");
ret.arguments = argument_list(tokeniser);
tokens.close =
tokeniser.consume(")") || tokeniser.error("Unterminated operation");
tokens.termination =
tokeniser.consume(";") ||
tokeniser.error("Unterminated operation, expected `;`");
return ret.this;
}
get type() {
return "operation";
}
get name() {
const { name } = this.tokens;
if (!name) {
return "";
}
return unescape(name.value);
}
get special() {
if (!this.tokens.special) {
return "";
}
return this.tokens.special.value;
}
*validate(defs) {
yield* this.extAttrs.validate(defs);
if (!this.name && ["", "static"].includes(this.special)) {
const message = `Regular or static operations must have both a return type and an identifier.`;
yield validationError(this.tokens.open, this, "incomplete-op", message);
}
if (this.idlType) {
if (this.idlType.generic === "async_sequence") {
const message = `async_sequence types cannot be returned by an operation.`;
yield validationError(
this.idlType.tokens.base,
this,
"async-sequence-idl-to-js",
message,
);
}
yield* this.idlType.validate(defs);
}
for (const argument of this.arguments) {
yield* argument.validate(defs);
}
}
/** @param {import("../writer.js").Writer} w */
write(w) {
const { parent } = this;
const body = this.idlType
? [
w.ts.type(this.idlType.write(w)),
w.name_token(this.tokens.name, { data: this, parent }),
w.token(this.tokens.open),
w.ts.wrap(this.arguments.map((arg) => arg.write(w))),
w.token(this.tokens.close),
]
: [];
return w.ts.definition(
w.ts.wrap([
this.extAttrs.write(w),
this.tokens.name
? w.token(this.tokens.special)
: w.token(this.tokens.special, w.ts.nameless, { data: this, parent }),
...body,
w.token(this.tokens.termination),
]),
{ data: this, parent },
);
}
}