Repository: w3c/webidl2.js
Pull request #274 (2019-03-06 13:58:44)

refactor: iterable as a class
Also adding missing object arguments (and tests for them) on the writer.


Patch:
diff --git a/lib/webidl2.js b/lib/webidl2.js
index 41d29c4..5635aaf 100644
--- a/lib/webidl2.js
+++ b/lib/webidl2.js
@@ -890,53 +890,47 @@
       return arr;
     }
 
-    function iterable_type() {
-      return consume("iterable", "maplike", "setlike");
-    }
+    class IterableLike extends Definition {
+      static parse() {
+        const start_position = consume_position;
+        const tokens = {};
+        const ret = new IterableLike({ tokens });
+        tokens.readonly = optional_consume("readonly");
+        tokens.base = tokens.readonly ?
+          consume("maplike", "setlike") :
+          consume("iterable", "maplike", "setlike");
+        if (!tokens.base) {
+          unconsume(start_position);
+          return;
+        }
 
-    function readonly_iterable_type() {
-      return consume("maplike", "setlike");
-    }
+        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() || error(`Error parsing ${type} declaration`);
+        ret.idlType = [first];
+        if (secondTypeAllowed) {
+          first.separator = untyped_consume(",") || null;
+          if (first.separator) {
+            ret.idlType.push(type_with_extended_attributes());
+          }
+          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`);
 
-    function iterable() {
-      const start_position = consume_position;
-      const ret = { type: null, idlType: null, readonly: null, trivia: {} };
-      const readonly = consume("readonly");
-      if (readonly) {
-        ret.readonly = { trivia: readonly.trivia };
+        return ret;
       }
-      const consumeItType = ret.readonly ? readonly_iterable_type : iterable_type;
 
-      const ittype = consumeItType();
-      if (!ittype) {
-        unconsume(start_position);
-        return;
+      get type() {
+        return this.tokens.base.value;
       }
-      ret.trivia.base = ittype.trivia;
-
-      const secondTypeRequired = ittype.value === "maplike";
-      const secondTypeAllowed = secondTypeRequired || ittype.value === "iterable";
-      ret.type = ittype.value;
-      if (ret.type !== 'maplike' && ret.type !== 'setlike')
-        delete ret.readonly;
-      const open = consume("<") || error(`Error parsing ${ittype.value} declaration`);
-      ret.trivia.open = open.trivia;
-      const first = type_with_extended_attributes() || error(`Error parsing ${ittype.value} declaration`);
-      ret.idlType = [first];
-      if (secondTypeAllowed) {
-        first.separator = untyped_consume(",") || null;
-        if (first.separator) {
-          ret.idlType.push(type_with_extended_attributes());
-        }
-        else if (secondTypeRequired)
-          error(`Missing second type argument in ${ittype.value} declaration`);
+      get readonly() {
+        return unvalue_token(this.tokens.readonly);
       }
-      const close = consume(">") || error(`Unterminated ${ittype.value} declaration`);
-      ret.trivia.close = close.trivia;
-      const termination = consume(";") || error(`Missing semicolon after ${ittype.value} declaration`);
-      ret.trivia.termination = termination.trivia;
-
-      return ret;
     }
 
     class Inheritance extends Definition {
@@ -1012,7 +1006,7 @@
             [const_],
             [static_member],
             [stringifier],
-            [iterable],
+            [IterableLike.parse],
             [attribute],
             [operation]
           ]
diff --git a/lib/writer.js b/lib/writer.js
index 2a50090..76421dc 100644
--- a/lib/writer.js
+++ b/lib/writer.js
@@ -220,7 +220,7 @@
         name_token(it.tokens.name, { data: it, parent }),
         default_(it.default, it),
         token(it.tokens.termination)
-      ]));
+      ]), { data: it, parent });
     }
     function const_(it, parent) {
       const trivia = extract_trivia(it);
@@ -240,7 +240,7 @@
         ts.type(type(it.idlType)),
         name_token(it.tokens.name, { data: it }),
         token(it.tokens.termination)
-      ]));
+      ]), { data: it });
     }
     function includes(it) {
       return ts.definition(ts.wrap([
@@ -249,7 +249,7 @@
         token(it.tokens.includes),
         reference_token(it.tokens.mixin, it.includes),
         token(it.tokens.termination)
-      ]));
+      ]), { data: it });
     }
     function callback(it) {
       const trivia = extract_trivia(it);
@@ -272,7 +272,7 @@
         iterate(it.values, it),
         token(it.tokens.close),
         token(it.tokens.termination)
-      ]));
+      ]), { data: it });
     }
     function enum_value(v, parent) {
       return wrap`${ts.trivia(v.trivia)}${
@@ -280,13 +280,15 @@
       }${token(v.separator)}`;
     }
     function iterable_like(it, parent) {
-      const trivia = extract_trivia(it);
-      const readonly = token(it.readonly, "readonly");
-      const bracket = wrap`${trivia.open}<${ts.wrap(it.idlType.map(type))}${trivia.close}>`;
-      return ts.definition(
-        wrap`${readonly}${trivia.base}${it.type}${bracket}${trivia.termination};`,
-        { data: it, parent }
-      );
+      return ts.definition(ts.wrap([
+        extended_attributes(it.extAttrs),
+        token(it.tokens.readonly),
+        token(it.tokens.base),
+        token(it.tokens.open),
+        ts.wrap(it.idlType.map(type)),
+        token(it.tokens.close),
+        token(it.tokens.termination)
+      ]), { data: it, parent });
     }
     function callbackInterface(it) {
       return ts.definition(


Test patch:
diff --git a/test/syntax/baseline/iterable.json b/test/syntax/baseline/iterable.json
index 6decadf..6c80a74 100644
--- a/test/syntax/baseline/iterable.json
+++ b/test/syntax/baseline/iterable.json
@@ -25,13 +25,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
+                "readonly": null,
                 "trivia": {
                     "base": "\n  ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
@@ -91,13 +92,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
+                "readonly": null,
                 "trivia": {
                     "base": "\n  ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
@@ -153,13 +155,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
+                "readonly": null,
                 "trivia": {
                     "base": "\n  ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
diff --git a/test/syntax/baseline/maplike.json b/test/syntax/baseline/maplike.json
index 030da7d..473f4c4 100644
--- a/test/syntax/baseline/maplike.json
+++ b/test/syntax/baseline/maplike.json
@@ -44,14 +44,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
                 "readonly": null,
                 "trivia": {
                     "base": "\n  ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
@@ -109,6 +109,7 @@
                         }
                     }
                 ],
+                "extAttrs": null,
                 "readonly": {
                     "trivia": "\n  "
                 },
@@ -117,8 +118,7 @@
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
@@ -210,14 +210,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
                 "readonly": null,
                 "trivia": {
                     "base": "\n    ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
diff --git a/test/syntax/baseline/setlike.json b/test/syntax/baseline/setlike.json
index 3adbb71..978fcc3 100644
--- a/test/syntax/baseline/setlike.json
+++ b/test/syntax/baseline/setlike.json
@@ -25,14 +25,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
                 "readonly": null,
                 "trivia": {
                     "base": "\n  ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
@@ -71,6 +71,7 @@
                         }
                     }
                 ],
+                "extAttrs": null,
                 "readonly": {
                     "trivia": "\n  "
                 },
@@ -79,8 +80,7 @@
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
@@ -136,14 +136,14 @@
                         }
                     }
                 ],
+                "extAttrs": null,
                 "readonly": null,
                 "trivia": {
                     "base": "\n  ",
                     "open": "",
                     "close": "",
                     "termination": ""
-                },
-                "extAttrs": null
+                }
             }
         ],
         "extAttrs": null,
diff --git a/test/writer.js b/test/writer.js
index a9baf5f..96113db 100644
--- a/test/writer.js
+++ b/test/writer.js
@@ -124,4 +124,30 @@ describe("Writer template functions", () => {
     });
     expect(result).toBe("[<Exposed>=Window, <Constructor>] interface EagleJump { void aoba([<Clamp>] long work); };");
   });
+
+  it("gives definition object", () => {
+    function rewriteDefinition(text) {
+      return rewrite(text, {
+        definition: (def, { data, parent }) => {
+          const parentType = parent ? `${parent.type}:` : "";
+          return `${parentType}${data.type}[${def}]`;
+        }
+      });
+    }
+
+    const includes = rewriteDefinition("A includes B;");
+    expect(includes).toBe("includes[A includes B;]");
+
+    const typedef = rewriteDefinition("typedef A B;");
+    expect(typedef).toBe("typedef[typedef A B;]");
+
+    const enumeration = rewriteDefinition('enum A { "b", "c" };');
+    expect(enumeration).toBe('enum[enum A { enum:enum-value["b"], enum:enum-value["c"] };]');
+
+    const dictionary = rewriteDefinition("dictionary X { required DOMString str; };");
+    expect(dictionary).toBe("dictionary[dictionary X {dictionary:field[ required DOMString str;] };]");
+
+    const interface_ = rewriteDefinition("interface X { iterable<DOMString>; };");
+    expect(interface_).toBe("interface[interface X {interface:iterable[ iterable<DOMString>;] };]");
+  });
 });