Skip to content

Instantly share code, notes, and snippets.

@it-ony
Last active December 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save it-ony/7682654 to your computer and use it in GitHub Desktop.
Save it-ony/7682654 to your computer and use it in GitHub Desktop.
A pegjs grammar to convert css selectors to their respective xpath selector. Try it online http://pegjs.majda.cz/online. Now also with weights.
/**
Parses css selectors to their respective xpath expressions.
TODO:
- pseudo classes
for calculation of weight of the selectors, see
http://aktuell.de.selfhtml.org/archiv/doku/8.1.1/css/formate/kaskade.htm#spezifitaet
**/
start
= selectors
name =
a:[a-zA-Z_]b:[a-zA-Z0-9-_]* {
return a + b.join("");
}
space = " "
whitespace = [ \t\n\r]
selectors
= fss:firstSelectors* fs:firstSelector {
return fss.concat(fs);
}
firstSelectors
= fs:firstSelector whitespace* "," whitespace* {
return fs;
}
firstSelector
= s:selector {
return {
xpath: "//" + s.xpath,
weight: s.weight
}
}
selector
= e:element c:chain s:selector {
return {
xpath: e.xpath + c + s.xpath,
weight: [e.weight[0] + s.weight[0], e.weight[1] + s.weight[1], e.weight[2] + s.weight[2]]
}
} / e:element
chain
= adjacentSelector / childSelector / descentSelector
adjacentSelector
= space* "+" space* {
return "/following-sibling::"
}
childSelector
= space* ">" space* {
return "/"
}
descentSelector
= space+ {
return "//"
}
element
= elementSelector / idSelector / classSelector
idSelector
= id:id {
return {
xpath: "node()" + id.xpath,
weight: [1, 0, 0]
}
}
classSelector
= c:classList {
return {
xpath: "node()" + c.xpath,
weight: c.weight
}
}
classList
= c:class+ {
return {
xpath: "[" + c.join(" and ") + "]",
weight: [0, c.length, 0]
}
}
class
= "." n:name {
return "contains(concat(' ', normalize-space(@class), ' '), ' " + n +" ')";
}
id
= "#" n:name {
return {
xpath: "[@id='" + n + "']",
weight: 1
}
}
elementSelector
= tag:(universalElement / typeElement) id:(id / attributeSelector)? c:classList? {
return {
xpath: (tag.xpath || "") + (id.xpath || "") + (c.xpath || ""),
weight: [id ? id.weight : 0, c ? c.weight[1] : 0, tag.weight[2]]
}
}
universalElement
= "*" {
return {
xpath: "node()",
weight: [0, 0, 0]
}
}
typeElement
= n:name {
return {
xpath: n,
weight: [0, 0, 1]
};
}
attributeSelector
= "[" a:( listAttribute / equalAttribute / hasAttribute ) "]" {
return {
xpath: "[" + a + "]",
weight: 0
}
}
hasAttribute
= n:name {
return "@" + n;
}
listAttribute
= name:name "~=" v:("'" [^']+ "'" / '"' [^"]+ '"') {
return "contains(@" + name + ", '" + v[1].join("") + "')";
}
equalAttribute
= name:name "=" v:("'" [^']+ "'" / '"' [^"]+ '"') {
return "@" + name + "='" + v[1].join("") + "'";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment