1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
Description: Replace regex with hand-rolled parser
Author: Felix Böhm <188768+fb55@users.noreply.github.com>
Origin: upstream, https://patch-diff.githubusercontent.com/raw/fb55/nth-check/pull/9.patch
Bug: https://github.com/advisories/GHSA-rp65-9cf3-cjxr
Forwarded: not-needed
Reviewed-By: Yadd <yadd@debian.org>
Last-Update: 2022-02-05
--- a/src/parse.ts
+++ b/src/parse.ts
@@ -1,7 +1,9 @@
// Following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
-// [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]?
-const RE_NTH_ELEMENT = /^([+-]?\d*n)?\s*(?:([+-]?)\s*(\d+))?$/;
+// Whitespace as per https://www.w3.org/TR/selectors-3/#lex is " \t\r\n\f"
+const whitespace = new Set([9, 10, 12, 13, 32]);
+const ZERO = "0".charCodeAt(0);
+const NINE = "9".charCodeAt(0);
/**
* Parses an expression.
@@ -19,24 +21,72 @@
return [2, 1];
}
- const parsed = formula.match(RE_NTH_ELEMENT);
+ // Parse [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]?
- if (!parsed) {
+ let idx = 0;
+
+ let a = 0;
+ let sign = readSign();
+ let number = readNumber();
+
+ if (idx < formula.length && formula.charAt(idx) === "n") {
+ idx++;
+ a = sign * (number ?? 1);
+
+ skipWhitespace();
+
+ if (idx < formula.length) {
+ sign = readSign();
+ skipWhitespace();
+ number = readNumber();
+ } else {
+ sign = number = 0;
+ }
+ }
+
+ // Throw if there is anything else
+ if (number === null || idx < formula.length) {
throw new Error(`n-th rule couldn't be parsed ('${formula}')`);
}
- let a;
+ return [a, sign * number];
- if (parsed[1]) {
- a = parseInt(parsed[1], 10);
- if (isNaN(a)) {
- a = parsed[1].startsWith("-") ? -1 : 1;
+ function readSign() {
+ if (formula.charAt(idx) === "-") {
+ idx++;
+ return -1;
}
- } else a = 0;
- const b =
- (parsed[2] === "-" ? -1 : 1) *
- (parsed[3] ? parseInt(parsed[3], 10) : 0);
+ if (formula.charAt(idx) === "+") {
+ idx++;
+ }
+
+ return 1;
+ }
- return [a, b];
+ function readNumber() {
+ const start = idx;
+ let value = 0;
+
+ while (
+ idx < formula.length &&
+ formula.charCodeAt(idx) >= ZERO &&
+ formula.charCodeAt(idx) <= NINE
+ ) {
+ value = value * 10 + (formula.charCodeAt(idx) - ZERO);
+ idx++;
+ }
+
+ // Return `null` if we didn't read anything.
+ return idx === start ? null : value;
+ }
+
+ function skipWhitespace() {
+ while (
+ idx < formula.length &&
+ whitespace.has(formula.charCodeAt(idx))
+ ) {
+ idx++;
+ }
+ }
}
|