initial release
[squeep-web-linking] / lib / rfc8288-web-linking.peggy
diff --git a/lib/rfc8288-web-linking.peggy b/lib/rfc8288-web-linking.peggy
new file mode 100644 (file)
index 0000000..95b4792
--- /dev/null
@@ -0,0 +1,122 @@
+{{
+/* Generated code is not pretty, ignore a lot of this horrorstyle. */
+/* eslint-disable quotes */
+/* eslint-disable vars-on-top */
+/* eslint-disable no-control-regex */
+/* eslint-disable security/detect-object-injection */
+/* eslint-disable vars-on-top */
+/* eslint-disable brace-style */
+/* eslint-disable comma-dangle */
+/* eslint-disable no-unused-vars */
+
+function makeString(o) {
+  return o.join('');
+}
+
+const onlyFirstAttributes = [
+  'media',
+  'rel',
+  'title',
+  'title*',
+  'type',
+];
+
+}}
+
+{
+// Per-parsing tracking of attributes which should be ignored after first occurrence.
+let seenAttributes = [];
+}
+
+links 'links'
+  = links:linkValue+ {
+    return links;
+  }
+
+linkValue 'link-value'
+  = uriReference:uriReference OWS attributes:attributes ','? OWS {
+    seenAttributes = [];
+    return {
+      target: uriReference,
+      attributes,
+    };
+  }
+
+uriReference 'uri-reference'
+  = '<' uri:uri '>' ';' { 
+    return uri;
+  }
+
+uri 'uri'
+  = uri:[^>]+ {
+    return makeString(uri);
+  }
+
+attributes 'attributes'
+  = attrs:linkParam+ {
+    return attrs.filter((a) => a);
+  }
+
+linkParam 'link-param'
+  = name:name BWS '=' BWS value:value ';'? OWS {
+    if (onlyFirstAttributes.includes(name.name)) {
+      if (seenAttributes.includes(name.name)) {
+        // Repeat of singleton attribute, ignore it.
+        return;
+      }
+      seenAttributes.push(name.name);
+    }
+    return {
+      ...name,
+      ...value,
+    };
+  }
+  / name:name BWS ';'? OWS {
+    return {
+      ...name,
+      value: null,
+    }
+  }
+
+name 'name'
+  = name:[a-zA-Z]+ extended:'*'? {
+    return {
+      name: makeString(name.concat(extended)).toLowerCase(),
+      extended: !!extended,
+    };
+  }
+
+value 'value'
+  = ["] value:[^"]+ ["] {
+    return {
+      value: makeString(value),
+    };
+  }
+  / value:[^";,]+ {
+    return {
+      value: makeString(value),
+    };
+  }
+
+OWS 'whitespace'
+  = spaces:[ ]*
+
+BWS 'bad whitespace'
+  = OWS
+
+// This is also an alternate startRule.
+extendedValue 'extended-value'
+  = encoding:[^']* ['] language:[^']* ['] value:.* {
+    return {
+      encoding: encoding.length ? makeString(encoding) : 'UTF-8',
+      language: language.length ? makeString(language) : null,
+      value: decodeURIComponent(makeString(value)),
+    };
+  }
+  / value:.* {
+    return {
+      encoding: null,
+      language: null,
+      value: makeString(value),
+    };
+  }