1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 13:00:36 -05:00
This commit is contained in:
Marvin Hagemeister 2024-12-16 18:16:29 +01:00
parent 86fc6e64cb
commit 2f18594ca8
3 changed files with 182 additions and 88 deletions

View file

@ -136,7 +136,6 @@ export class Lexer {
*/
expect(token) {
if (this.token !== token) {
console.log(this, JSON.stringify(String.fromCharCode(this.ch)));
throw new Error(
`Expected token '${token}', but got '${this.token}'.\n\n${this.input}\n${
" ".repeat(this.i)
@ -180,17 +179,14 @@ export class Lexer {
return;
}
console.log(
"NEXT",
this.input,
this.i,
JSON.stringify(String.fromCharCode(this.ch)),
);
if (this.i > 30) throw new Error("infininte #2");
// console.log(
// "NEXT",
// this.input,
// this.i,
// JSON.stringify(String.fromCharCode(this.ch)),
// );
let j = 0;
while (true) {
if (j++ > 50) throw new Error("infininte");
switch (this.ch) {
case Char.Space:
while (this.isWhiteSpace()) {
@ -376,7 +372,7 @@ export const ATTR_EXISTS_NODE = 3;
export const ATTR_BIN_NODE = 4;
export const PSEUDO_NTH_CHILD = 5;
export const PSEUDO_HAS = 6;
export const PSEUDO_NODE_NOT = 7;
export const PSEUDO_NOT = 7;
export const PSEUDO_FIRST_CHILD = 8;
export const PSEUDO_LAST_CHILD = 9;
@ -390,17 +386,20 @@ export function parseSelector(input, toElem, toAttr) {
/** @type {Selector[]} */
const result = [];
/** @type {Selector} */
let current = [];
/** @type {Selector[]} */
const stack = [[]];
const lex = new Lexer(input);
// Some subselectors like `:nth-child(.. of <selector>)` must have
// a single selector instead of selector list.
let throwOnComma = false;
while (lex.token !== Token.EOF) {
console.log(
lex.token,
result,
current,
);
const current = /** @type {Selector} */ (stack.at(-1));
console.log(input.slice(lex.i));
console.log(stack);
if (lex.token === Token.Word) {
const value = lex.value;
const wildcard = value === "*";
@ -449,7 +448,6 @@ export function parseSelector(input, toElem, toAttr) {
lex.next();
lex.expect(Token.Word);
console.log("PSEUDO", lex);
switch (lex.value) {
case "first-child":
current.push({
@ -484,12 +482,22 @@ export function parseSelector(input, toElem, toAttr) {
step = +value;
}
let stepOffset = 0;
lex.next();
/** @type {PseudoNthChild} */
const node = {
type: PSEUDO_NTH_CHILD,
backwards,
of: null,
step,
stepOffset: 0,
repeat,
};
current.push(node);
if (lex.token !== Token.BraceClose) {
lex.expect(Token.Op);
if (lex.value !== "+") {
if (/** @type {string} */ (lex.value) !== "+") {
throw new Error(
`Unknown operator in ':nth-child' selector: '${lex.value}'`,
);
@ -497,48 +505,69 @@ export function parseSelector(input, toElem, toAttr) {
lex.next();
lex.expect(Token.Word);
stepOffset = +lex.value;
node.stepOffset = +lex.value;
lex.next();
lex.next(); // Space
if (lex.token !== Token.BraceClose) {
lex.next(); // Space
if (lex.token === Token.Word) {
if (lex.value !== "of") {
throw new Error(
`Expected 'of' keyword in ':nth-child' but got: ${lex.value}`,
);
if (lex.token === Token.Word) {
if (lex.value !== "of") {
throw new Error(
`Expected 'of' keyword in ':nth-child' but got: ${lex.value}`,
);
}
lex.next();
lex.expect(Token.Space);
lex.next();
throwOnComma = true;
stack.push([]);
}
lex.next();
lex.expect(Token.Space);
lex.next();
continue;
}
lex.expect(Token.BraceClose);
//
console.log("CLOSE", Token.Op, lex.token);
}
console.log("nth", lex);
console.log(lex.getSlice());
lex.next();
current.push({
type: PSEUDO_NTH_CHILD,
backwards,
of: null,
step,
stepOffset,
repeat,
});
continue;
}
break;
case "has":
case "where":
case "is": {
lex.next();
lex.expect(Token.BraceOpen);
lex.next();
console.log("================================");
/** @type {PseudoHas} */
const node = {
type: PSEUDO_HAS,
selectors: [],
};
current.push(node);
stack.push([]);
continue;
}
default:
throw new Error(`Unknown pseudo selector: '${lex.value}'`);
}
} else if (lex.ch === Char.Comma) {
result.push(current);
current = [];
} else if (lex.token === Token.Comma) {
if (throwOnComma) {
throw new Error(`Multiple selector arguments not supported here`);
}
popSelector(result, stack);
stack.push([]);
} else if (lex.token === Token.BraceClose) {
throwOnComma = false;
popSelector(result, stack);
} else if (lex.token === Token.Op) {
current.push({
type: RELATION_NODE,
@ -549,16 +578,44 @@ export function parseSelector(input, toElem, toAttr) {
lex.next();
}
if (current.length > 0) {
result.push(current);
if (stack.length > 0) {
result.push(stack[0]);
}
console.log(lex);
console.log("--> SELECTORS", result);
return result;
}
/**
* @param {Selector[]} result
* @param {Selector[]} stack
*/
function popSelector(result, stack) {
const sel = /** @type {Selector} */ (stack.pop());
if (stack.length === 0) {
result.push(sel);
stack.push([]);
} else {
const prev = /** @type {Selector} */ (stack.at(-1));
if (prev.length === 0) {
throw new Error(`Empty selector`);
}
const node = prev.at(-1);
if (node === undefined) {
throw new Error(`Empty node`);
}
if (node.type === PSEUDO_NTH_CHILD) {
node.of = sel;
} else if (node.type === PSEUDO_HAS || node.type === PSEUDO_NOT) {
node.selectors.push(prev);
} else {
throw new Error(`Multiple selectors not allowed here`);
}
}
}
const TRUE_FN = () => true;
/**
@ -613,8 +670,8 @@ export function compileSelector(selector) {
// FIXME
// fn = matchIs(part, fn);
throw new Error("TODO: :has");
case PSEUDO_NODE_NOT:
fn = matchNot(node.selector, fn);
case PSEUDO_NOT:
fn = matchNot(node.selectors, fn);
break;
}
}
@ -657,7 +714,7 @@ function matchNthChild(node, next) {
return (ctx, id) => {
const siblings = ctx.getSiblings(id);
if (node.backward) {
if (node.backwards) {
for (
let i = siblings.length - 1 - node.stepOffset;
i < siblings.length;

View file

@ -33,10 +33,12 @@ function testLexer(input: string): LexState[] {
}
const Tags: Record<string, number> = { Foo: 1, Bar: 2, FooBar: 3 };
const Attrs: Record<string, number> = { foo: 1, bar: 2, foobar: 3 };
const Attrs: Record<string, number> = { foo: 1, bar: 2, foobar: 3, attr: 4 };
const toTag = (name: string): number => Tags[name];
const toAttr = (name: string): number => Attrs[name];
const testParse = (input: string) => parseSelector(input, toTag, toAttr);
Deno.test("Lexer - Elem", () => {
expect(testLexer("Foo")).toEqual([
{ token: Token.Word, value: "Foo" },
@ -229,14 +231,14 @@ Deno.test("Lexer - Pseudo", () => {
});
Deno.test("Parser", () => {
expect(parseSelector("Foo", toTag, toAttr)).toEqual([[
expect(testParse("Foo")).toEqual([[
{
type: ELEM_NODE,
elem: 1,
wildcard: false,
},
]]);
expect(parseSelector("Foo Bar", toTag, toAttr)).toEqual([[
expect(testParse("Foo Bar")).toEqual([[
{
type: ELEM_NODE,
elem: 1,
@ -251,7 +253,7 @@ Deno.test("Parser", () => {
});
Deno.test("Parser - Relation", () => {
expect(parseSelector("Foo > Bar", toTag, toAttr)).toEqual([[
expect(testParse("Foo > Bar")).toEqual([[
{
type: ELEM_NODE,
elem: 1,
@ -268,7 +270,7 @@ Deno.test("Parser - Relation", () => {
},
]]);
expect(parseSelector("Foo ~ Bar", toTag, toAttr)).toEqual([[
expect(testParse("Foo ~ Bar")).toEqual([[
{
type: ELEM_NODE,
elem: 1,
@ -285,7 +287,7 @@ Deno.test("Parser - Relation", () => {
},
]]);
expect(parseSelector("Foo + Bar", toTag, toAttr)).toEqual([[
expect(testParse("Foo + Bar")).toEqual([[
{
type: ELEM_NODE,
elem: 1,
@ -304,14 +306,14 @@ Deno.test("Parser - Relation", () => {
});
Deno.test("Parser - Attr", () => {
expect(parseSelector("[foo]", toTag, toAttr)).toEqual([[
expect(testParse("[foo]")).toEqual([[
{
type: ATTR_EXISTS_NODE,
prop: [1],
},
]]);
expect(parseSelector("[foo][bar]", toTag, toAttr)).toEqual([[
expect(testParse("[foo][bar]")).toEqual([[
{
type: ATTR_EXISTS_NODE,
prop: [1],
@ -322,7 +324,7 @@ Deno.test("Parser - Attr", () => {
},
]]);
expect(parseSelector("[foo=1]", toTag, toAttr)).toEqual([[
expect(testParse("[foo=1]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -330,7 +332,7 @@ Deno.test("Parser - Attr", () => {
value: 1,
},
]]);
expect(parseSelector("[foo=true]", toTag, toAttr)).toEqual([[
expect(testParse("[foo=true]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -338,7 +340,7 @@ Deno.test("Parser - Attr", () => {
value: true,
},
]]);
expect(parseSelector("[foo=false]", toTag, toAttr)).toEqual([[
expect(testParse("[foo=false]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -346,7 +348,7 @@ Deno.test("Parser - Attr", () => {
value: false,
},
]]);
expect(parseSelector("[foo=null]", toTag, toAttr)).toEqual([[
expect(testParse("[foo=null]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -354,7 +356,7 @@ Deno.test("Parser - Attr", () => {
value: null,
},
]]);
expect(parseSelector("[foo='str']", toTag, toAttr)).toEqual([[
expect(testParse("[foo='str']")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -362,7 +364,7 @@ Deno.test("Parser - Attr", () => {
value: "str",
},
]]);
expect(parseSelector('[foo="str"]', toTag, toAttr)).toEqual([[
expect(testParse('[foo="str"]')).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -370,7 +372,7 @@ Deno.test("Parser - Attr", () => {
value: "str",
},
]]);
expect(parseSelector("[foo=/str/]", toTag, toAttr)).toEqual([[
expect(testParse("[foo=/str/]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -378,7 +380,7 @@ Deno.test("Parser - Attr", () => {
value: /str/,
},
]]);
expect(parseSelector("[foo=/str/g]", toTag, toAttr)).toEqual([[
expect(testParse("[foo=/str/g]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -389,14 +391,14 @@ Deno.test("Parser - Attr", () => {
});
Deno.test("Parser - Attr nested", () => {
expect(parseSelector("[foo.bar]", toTag, toAttr)).toEqual([[
expect(testParse("[foo.bar]")).toEqual([[
{
type: ATTR_EXISTS_NODE,
prop: [1, 2],
},
]]);
expect(parseSelector("[foo.bar = 2]", toTag, toAttr)).toEqual([[
expect(testParse("[foo.bar = 2]")).toEqual([[
{
type: ATTR_BIN_NODE,
op: 1,
@ -407,20 +409,20 @@ Deno.test("Parser - Attr nested", () => {
});
Deno.test("Parser - Pseudo no value", () => {
expect(parseSelector(":first-child", toTag, toAttr)).toEqual([[
expect(testParse(":first-child")).toEqual([[
{
type: PSEUDO_FIRST_CHILD,
},
]]);
expect(parseSelector(":last-child", toTag, toAttr)).toEqual([[
expect(testParse(":last-child")).toEqual([[
{
type: PSEUDO_LAST_CHILD,
},
]]);
});
Deno.test.only("Parser - Pseudo nth-child", () => {
expect(parseSelector(":nth-child(2)", toTag, toAttr)).toEqual([[
Deno.test("Parser - Pseudo nth-child", () => {
expect(testParse(":nth-child(2)")).toEqual([[
{
type: PSEUDO_NTH_CHILD,
of: null,
@ -430,7 +432,7 @@ Deno.test.only("Parser - Pseudo nth-child", () => {
repeat: false,
},
]]);
expect(parseSelector(":nth-child(2n)", toTag, toAttr)).toEqual([[
expect(testParse(":nth-child(2n)")).toEqual([[
{
type: PSEUDO_NTH_CHILD,
of: null,
@ -440,7 +442,7 @@ Deno.test.only("Parser - Pseudo nth-child", () => {
repeat: true,
},
]]);
expect(parseSelector(":nth-child(-2n)", toTag, toAttr)).toEqual([[
expect(testParse(":nth-child(-2n)")).toEqual([[
{
type: PSEUDO_NTH_CHILD,
of: null,
@ -450,7 +452,7 @@ Deno.test.only("Parser - Pseudo nth-child", () => {
repeat: true,
},
]]);
expect(parseSelector(":nth-child(2n + 1)", toTag, toAttr)).toEqual([[
expect(testParse(":nth-child(2n + 1)")).toEqual([[
{
type: PSEUDO_NTH_CHILD,
of: null,
@ -460,25 +462,60 @@ Deno.test.only("Parser - Pseudo nth-child", () => {
repeat: true,
},
]]);
expect(parseSelector(":nth-child(2n + 1 of Foo[attr])", toTag, toAttr))
expect(testParse(":nth-child(2n + 1 of Foo[attr])"))
.toEqual([[
{
type: PSEUDO_NTH_CHILD,
of: [],
of: [
{ type: ELEM_NODE, elem: 1, wildcard: false },
{ type: ATTR_EXISTS_NODE, prop: [4] },
],
backwards: false,
step: 2,
stepOffset: 1,
repeat: true,
},
]]);
// Invalid selectors
expect(() => testParse(":nth-child(2n + 1 of Foo[attr], Bar)"))
.toThrow();
expect(() => testParse(":nth-child(2n - 1)")).toThrow();
expect(() => testParse(":nth-child(2n - 1 foo)")).toThrow();
});
Deno.test("Parser - Pseudo has/is/where", () => {
expect(parseSelector(":has(Foo:has(Foo), Bar)", toTag, toAttr)).toEqual([[
Deno.test.only("Parser - Pseudo has/is/where", () => {
expect(testParse(":has(Foo, Bar)")).toEqual([[
{
type: PSEUDO_HAS,
op: 1,
selectors: [{}],
selectors: [
[
{ type: ELEM_NODE, elem: 1, wildcard: false },
{
type: PSEUDO_HAS,
selectors: [
{ type: ELEM_NODE, elem: 1, wildcard: false },
],
},
],
[
{ type: ELEM_NODE, elem: 1, wildcard: false },
],
],
},
]]);
// expect(testParse(":where(Foo:where(Foo), Bar)")).toEqual([[
// {
// type: PSEUDO_HAS,
// op: 1,
// selectors: [{}],
// },
// ]]);
// expect(testParse(":is(Foo:is(Foo), Bar)")).toEqual([[
// {
// type: PSEUDO_HAS,
// op: 1,
// selectors: [{}],
// },
// ]]);
});

View file

@ -44,11 +44,11 @@ export interface PseudoNthChild {
export interface PseudoHas {
type: 6;
selector: Selector[];
selectors: Selector[];
}
export interface PseudoNot {
type: 7;
selector: Selector[];
selectors: Selector[];
}
export interface PseudoFirstChild {
type: 8;