mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 13:00:36 -05:00
WIP
This commit is contained in:
parent
86fc6e64cb
commit
2f18594ca8
3 changed files with 182 additions and 88 deletions
|
@ -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;
|
||||
|
|
|
@ -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: [{}],
|
||||
// },
|
||||
// ]]);
|
||||
});
|
||||
|
|
4
cli/js/40_lint_types.d.ts
vendored
4
cli/js/40_lint_types.d.ts
vendored
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue