Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/productions/callback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Base } from "./base";
import { return_type, argument_list, unescape } from "./helpers";

export class CallbackFunction extends Base {
/**
* @param {import("../tokeniser.js").Tokeniser} tokeniser
*/
static parse(tokeniser, base) {
const tokens = { base };
const ret = new CallbackFunction({ source: tokeniser.source, tokens });
tokens.name = tokeniser.consume("identifier") || tokeniser.error("Callback lacks a name");
tokeniser.current = ret;
tokens.assign = tokeniser.consume("=") || tokeniser.error("Callback lacks an assignment");
ret.idlType = return_type(tokeniser) || tokeniser.error("Callback lacks a return type");
tokens.open = tokeniser.consume("(") || tokeniser.error("Callback lacks parentheses for arguments");
ret.arguments = argument_list(tokeniser);
tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated callback");
tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated callback, expected `;`");
return ret;
}

get type() {
return "callback";
}
get name() {
return unescape(this.tokens.name.value);
}
}
48 changes: 48 additions & 0 deletions lib/productions/iterable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Base } from "./base";
import { type_with_extended_attributes } from "./helpers";

export class IterableLike extends Base {
/**
* @param {import("../tokeniser.js").Tokeniser} tokeniser
*/
static parse(tokeniser) {
const start_position = tokeniser.position;
const tokens = {};
const ret = new IterableLike({ source: tokeniser.source, tokens });
tokens.readonly = tokeniser.consume("readonly");
tokens.base = tokens.readonly ?
tokeniser.consume("maplike", "setlike") :
tokeniser.consume("iterable", "maplike", "setlike");
if (!tokens.base) {
tokeniser.unconsume(start_position);
return;
}

const { type } = ret;
const secondTypeRequired = type === "maplike";
const secondTypeAllowed = secondTypeRequired || type === "iterable";

tokens.open = tokeniser.consume("<") || tokeniser.error(`Missing less-than sign \`<\` in ${type} declaration`);
const first = type_with_extended_attributes(tokeniser) || tokeniser.error(`Missing a type argument in ${type} declaration`);
ret.idlType = [first];
if (secondTypeAllowed) {
first.tokens.separator = tokeniser.consume(",");
if (first.tokens.separator) {
ret.idlType.push(type_with_extended_attributes(tokeniser));
}
else if (secondTypeRequired)
tokeniser.error(`Missing second type argument in ${type} declaration`);
}
tokens.close = tokeniser.consume(">") || tokeniser.error(`Missing greater-than sign \`>\` in ${type} declaration`);
tokens.termination = tokeniser.consume(";") || tokeniser.error(`Missing semicolon after ${type} declaration`);

return ret;
}

get type() {
return this.tokens.base.value;
}
get readonly() {
return !!this.tokens.readonly;
}
}
76 changes: 4 additions & 72 deletions lib/webidl2.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

import { unescape, argument_list, type_with_extended_attributes, return_type } from "./productions/helpers.js";
import { unescape } from "./productions/helpers.js";
import { Tokeniser } from "./tokeniser.js";
import { Base } from "./productions/base.js";
import { Enum } from "./productions/enum.js";
Expand All @@ -11,6 +11,8 @@ import { Operation } from "./productions/operation.js";
import { Constant } from "./productions/constant.js";
import { Typedef } from "./productions/typedef.js";
import { Field } from "./productions/field.js";
import { CallbackFunction } from "./productions/callback.js";
import { IterableLike } from "./productions/iterable.js";

/**
* @param {Tokeniser} tokeniser
Expand All @@ -30,41 +32,14 @@ function parseByTokens(tokeniser, options) {
return tokeniser.consume(...candidates);
}

function unconsume(position) {
return tokeniser.unconsume(position);
}

class CallbackFunction extends Base {
static parse(base) {
const tokens = { base };
const ret = new CallbackFunction({ source, tokens });
tokens.name = consume(ID) || error("No name for callback");
tokeniser.current = ret;
tokens.assign = consume("=") || error("No assignment in callback");
ret.idlType = return_type(tokeniser) || error("Missing return type");
tokens.open = consume("(") || error("No arguments in callback");
ret.arguments = argument_list(tokeniser);
tokens.close = consume(")") || error("Unterminated callback");
tokens.termination = consume(";") || error("Unterminated callback");
return ret;
}

get type() {
return "callback";
}
get name() {
return unescape(this.tokens.name.value);
}
}

function callback() {
const callback = consume("callback");
if (!callback) return;
const tok = consume("interface");
if (tok) {
return Interface.parse(tok, { callback });
}
return CallbackFunction.parse(callback);
return CallbackFunction.parse(tokeniser, callback);
}

function static_member() {
Expand All @@ -85,49 +60,6 @@ function parseByTokens(tokeniser, options) {
return member;
}

class IterableLike extends Base {
static parse() {
const start_position = tokeniser.position;
const tokens = {};
const ret = new IterableLike({ source, tokens });
tokens.readonly = consume("readonly");
tokens.base = tokens.readonly ?
consume("maplike", "setlike") :
consume("iterable", "maplike", "setlike");
if (!tokens.base) {
unconsume(start_position);
return;
}

const { type } = ret;
const secondTypeRequired = type === "maplike";
const secondTypeAllowed = secondTypeRequired || type === "iterable";

tokens.open = consume("<") || error(`Error parsing ${type} declaration`);
const first = type_with_extended_attributes(tokeniser) || error(`Error parsing ${type} declaration`);
ret.idlType = [first];
if (secondTypeAllowed) {
first.tokens.separator = consume(",");
if (first.tokens.separator) {
ret.idlType.push(type_with_extended_attributes(tokeniser));
}
else if (secondTypeRequired)
error(`Missing second type argument in ${type} declaration`);
}
tokens.close = consume(">") || error(`Unterminated ${type} declaration`);
tokens.termination = consume(";") || error(`Missing semicolon after ${type} declaration`);

return ret;
}

get type() {
return this.tokens.base.value;
}
get readonly() {
return !!this.tokens.readonly;
}
}

function inheritance() {
const colon = consume(":");
if (!colon) {
Expand Down
3 changes: 3 additions & 0 deletions test/invalid/baseline/callback-noassign.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 1, since `callback MyCallback`:
callback MyCallback;
^ Callback lacks an assignment
3 changes: 3 additions & 0 deletions test/invalid/baseline/callback-noparen.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 1, since `callback YourCall`:
callback YourCall = void;
^ Callback lacks parentheses for arguments
3 changes: 3 additions & 0 deletions test/invalid/baseline/callback-noreturn.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 1, since `callback MyCall`:
callback MyCall = ;
^ Callback lacks a return type
3 changes: 3 additions & 0 deletions test/invalid/baseline/callback-semicolon.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 1, since `callback TotheFuture`:
TotheFuture = void ()
^ Unterminated callback, expected `;`
2 changes: 1 addition & 1 deletion test/invalid/baseline/iterable-empty.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Syntax error at line 2, since `interface X`:
iterable<>;
^ Error parsing iterable declaration
^ Missing a type argument in iterable declaration
3 changes: 3 additions & 0 deletions test/invalid/baseline/iterable-notype.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 2, since `interface X`:
iterable;
^ Missing less-than sign `<` in iterable declaration
2 changes: 1 addition & 1 deletion test/invalid/baseline/setlike-2types.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Syntax error at line 2, since `interface SetLikeTwoTypes`:
setlike<long, long>;
^ Unterminated setlike declaration
^ Missing greater-than sign `>` in setlike declaration
1 change: 1 addition & 0 deletions test/invalid/idl/callback-noassign.widl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
callback MyCallback;
1 change: 1 addition & 0 deletions test/invalid/idl/callback-noparen.widl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
callback YourCall = void;
1 change: 1 addition & 0 deletions test/invalid/idl/callback-noreturn.widl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
callback MyCall = ;
1 change: 1 addition & 0 deletions test/invalid/idl/callback-semicolon.widl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
callback TotheFuture = void ()
3 changes: 3 additions & 0 deletions test/invalid/idl/iterable-notype.widl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface X {
iterable;
};