grasp-squery
Version:
Grasp query backend using css style selectors
523 lines (522 loc) • 17.9 kB
JavaScript
// Generated by LiveScript 1.4.0
(function(){
var ref$, map, any, all, literalMap, syntaxFlat, Cache, visitPre, visitChildren, getPath, toString$ = {}.toString, slice$ = [].slice;
ref$ = require('prelude-ls'), map = ref$.map, any = ref$.any, all = ref$.all;
ref$ = require('grasp-syntax-javascript'), literalMap = ref$.literalMap, syntaxFlat = ref$.syntaxFlat;
ref$ = require('./common'), Cache = ref$.Cache, visitPre = ref$.visitPre, visitChildren = ref$.visitChildren, getPath = ref$.getPath;
function finalMatches(results){
var matches, i$, ref$, len$, subjects;
matches = [];
if (results.subject.length > 0) {
for (i$ = 0, len$ = (ref$ = results.subject).length; i$ < len$; ++i$) {
subjects = ref$[i$];
matches = matches.concat(subjects);
}
} else {
matches = results.matches;
}
return matches;
}
function matchAst(ast, selector, cache){
var subject, matches, isSubject, i$, ref$, len$, node, selVal, name, op, value, valueType, sel, left, props, subjects, leftResults, leftSubject, result, subs, propsLen, hasMatch, previousNode, j$, len1$, i, prop, newNode, k$, len2$, p, nodeInfo, l$, len3$, field, res$, propValue, ref1$, sub, ref2$, matchesSelector, ref3$, rightResults, leftMatches, rightSubject, rightMatches, leftI, leftNode;
subject = [];
matches = [];
if (!selector) {
return {
subject: subject,
matches: matches
};
}
isSubject = selector.subject;
switch (selector.type) {
case 'wildcard':
for (i$ = 0, len$ = (ref$ = cache.nodes).length; i$ < len$; ++i$) {
node = ref$[i$];
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
break;
case 'root':
matches.push(ast);
if (isSubject) {
subject.push([ast]);
}
break;
case 'identifier':
if (cache.types.hasOwnProperty(selector.value)) {
for (i$ = 0, len$ = (ref$ = cache.types[selector.value]).length; i$ < len$; ++i$) {
node = ref$[i$];
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
}
break;
case 'nth-child':
visitPre(ast, function(node){
var index, i$, val, len;
index = selector.index.value;
for (i$ in node) {
val = node[i$];
if (toString$.call(val).slice(8, -1) === 'Array') {
len = val.length;
if (0 <= index && index < len) {
matches.push(val[index]);
if (isSubject) {
subject.push([val[index]]);
}
}
}
}
});
break;
case 'nth-last-child':
visitPre(ast, function(node){
var index, i$, val, len, i;
index = selector.index.value;
for (i$ in node) {
val = node[i$];
if (toString$.call(val).slice(8, -1) === 'Array') {
len = val.length;
i = len - index - 1;
if (0 <= i && i < len) {
matches.push(val[i]);
if (isSubject) {
subject.push([val[i]]);
}
}
}
}
});
break;
case 'attribute':
selVal = selector.value;
name = selector.name;
if (selVal != null) {
op = selector.operator;
value = selVal.value;
valueType = toString$.call(value).slice(8, -1);
switch (selector.valType) {
case 'primitive':
switch (selVal.type) {
case 'literal':
visitPre(ast, function(node){
if (isMatchPrimitiveLiteral(getPath(node, name), op, value, valueType)) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
});
break;
case 'type':
visitPre(ast, function(node){
if (isMatchType(getPath(node, name), op, value)) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
});
}
break;
case 'either':
sel = selVal.sel;
visitPre(ast, function(node){
var nodeValue;
nodeValue = getPath(node, name);
if ('object' === typeof nodeValue && (nodeValue != null && nodeValue.type) && isMatchComplex(nodeValue, op, value, sel) || isMatchPrimitiveLiteral(nodeValue, op, value, valueType)) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
});
break;
case 'complex':
visitPre(ast, function(node){
if (isMatchComplex(getPath(node, name), op, value, selVal)) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
});
}
} else {
visitPre(ast, function(node){
if (getPath(node, name) != null) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
});
}
break;
case 'prop':
left = selector.left, props = selector.props, subjects = selector.subjects;
leftResults = finalMatches(matchAst(ast, left, cache));
leftSubject = left.subject;
for (i$ = 0, len$ = leftResults.length; i$ < len$; ++i$) {
result = leftResults[i$];
node = result;
subs = [];
propsLen = props.length;
hasMatch = false;
for (j$ = 0, len1$ = props.length; j$ < len1$; ++j$) {
i = j$;
prop = props[j$];
previousNode = node;
if (prop.type === 'wildcard') {
if (toString$.call(node).slice(8, -1) === 'Array') {
newNode = [];
for (k$ = 0, len2$ = node.length; k$ < len2$; ++k$) {
p = node[k$];
nodeInfo = syntaxFlat[p.type];
if (nodeInfo != null) {
for (l$ = 0, len3$ = (ref$ = nodeInfo.nodes.concat(nodeInfo.nodeArrays)).length; l$ < len3$; ++l$) {
field = ref$[l$];
if (p[field] != null) {
newNode.push(p[field]);
}
}
}
}
node = newNode;
} else {
nodeInfo = syntaxFlat[node.type];
if (nodeInfo != null) {
res$ = [];
for (k$ = 0, len2$ = (ref$ = nodeInfo.nodes.concat(nodeInfo.nodeArrays)).length; k$ < len2$; ++k$) {
field = ref$[k$];
if (node[field] != null) {
res$.push(node[field]);
}
}
node = res$;
}
}
} else if (prop.type === 'string') {
propValue = prop.value;
if (toString$.call(node).slice(8, -1) === 'Array') {
res$ = [];
for (k$ = 0, len2$ = node.length; k$ < len2$; ++k$) {
p = node[k$];
if (p[propValue] != null) {
res$.push(p[propValue]);
}
}
node = res$;
} else {
node = node[propValue];
}
} else if (toString$.call(node).slice(8, -1) === 'Array') {
switch (prop.type) {
case 'first':
case 'head':
node = node[0];
break;
case 'tail':
node = node.slice(1);
break;
case 'last':
node = node[node.length - 1];
break;
case 'initial':
node = node.slice(0, node.length - 1);
break;
case 'nth':
node = node[prop.index.value];
break;
case 'nth-last':
node = node[node.length - prop.index.value - 1];
break;
case 'slice':
node = node.slice.apply(node, map(fn$, prop.indicies));
}
} else {
break;
}
if (node == null) {
break;
}
if (toString$.call(node).slice(8, -1) === 'String' && prop.value === 'operator') {
node = {
type: 'Operator',
value: node,
loc: {
start: (ref$ = previousNode.left.loc) != null ? ref$.end : void 8,
end: (ref1$ = previousNode.right.loc) != null ? ref1$.start : void 8
},
raw: node
};
}
if (node.type != null) {
if (subjects[i]) {
subs.push(node);
}
} else if (toString$.call(node).slice(8, -1) === 'Array' && node.length) {
if (subjects[i]) {
subs = subs.concat(node);
}
} else {
break;
}
if (i === propsLen - 1) {
hasMatch = true;
}
}
if (hasMatch) {
if (toString$.call(node).slice(8, -1) === 'Array') {
matches = matches.concat(node);
} else {
matches.push(node);
}
if (leftSubject) {
subject.push([result]);
}
if (subs.length) {
for (j$ = 0, len1$ = subs.length; j$ < len1$; ++j$) {
sub = subs[j$];
subject.push([sub]);
}
}
}
}
break;
case 'matches':
for (i$ = 0, len$ = (ref2$ = selector.selectors).length; i$ < len$; ++i$) {
matchesSelector = ref2$[i$];
for (j$ = 0, len1$ = (ref3$ = finalMatches(matchAst(ast, matchesSelector, cache))).length; j$ < len1$; ++j$) {
node = ref3$[j$];
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
}
break;
case 'not':
rightResults = [];
for (i$ = 0, len$ = (ref2$ = selector.selectors).length; i$ < len$; ++i$) {
sel = ref2$[i$];
rightResults = rightResults.concat(finalMatches(matchAst(ast, sel, cache)));
}
visitPre(ast, function(node){
if (!in$(node, rightResults)) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
});
break;
case 'compound':
res$ = [];
for (i$ = 0, len$ = (ref2$ = selector.selectors).length; i$ < len$; ++i$) {
sel = ref2$[i$];
res$.push(finalMatches(matchAst(ast, sel, cache)));
}
rightResults = res$;
isSubject = isSubject || any(function(it){
return it.subject;
}, selector.selectors);
for (i$ = 0, len$ = (ref2$ = rightResults[0]).length; i$ < len$; ++i$) {
node = ref2$[i$];
if (all((fn1$), slice$.call(rightResults, 1))) {
matches.push(node);
if (isSubject) {
subject.push([node]);
}
}
}
break;
case 'descendant':
ref2$ = matchAst(ast, selector.left, cache), leftSubject = ref2$.subject, leftMatches = ref2$.matches;
ref2$ = matchAst(ast, selector.right, cache), rightSubject = ref2$.subject, rightMatches = ref2$.matches;
for (i$ = 0, len$ = leftMatches.length; i$ < len$; ++i$) {
leftI = i$;
leftNode = leftMatches[i$];
visitPre(leftNode, fn2$);
}
break;
case 'child':
ref2$ = matchAst(ast, selector.left, cache), leftSubject = ref2$.subject, leftMatches = ref2$.matches;
ref2$ = matchAst(ast, selector.right, cache), rightSubject = ref2$.subject, rightMatches = ref2$.matches;
for (i$ = 0, len$ = leftMatches.length; i$ < len$; ++i$) {
leftI = i$;
leftNode = leftMatches[i$];
visitChildren(leftNode, fn3$);
}
break;
case 'sibling':
ref2$ = matchAst(ast, selector.left, cache), leftSubject = ref2$.subject, leftMatches = ref2$.matches;
ref2$ = matchAst(ast, selector.right, cache), rightSubject = ref2$.subject, rightMatches = ref2$.matches;
visitPre(ast, function(node, context){
var key, val, i$, len$, i, x, leftI, j, rightI, newSubject, that;
for (key in node) {
val = node[key];
if (toString$.call(val).slice(8, -1) === 'Array') {
for (i$ = 0, len$ = val.length; i$ < len$; ++i$) {
i = i$;
x = val[i$];
leftI = leftMatches.indexOf(x);
if (leftI > -1) {
j = i + 1;
for (; j < val.length; j++) {
rightI = rightMatches.indexOf(val[j]);
if (rightI > -1) {
matches.push(val[j]);
newSubject = [];
if (that = leftSubject[leftI]) {
newSubject = that;
}
if (that = rightSubject[rightI]) {
newSubject = newSubject.concat(that);
}
if (newSubject.length > 0) {
subject.push(newSubject);
}
}
}
}
}
}
}
});
break;
case 'adjacent':
ref2$ = matchAst(ast, selector.left, cache), leftSubject = ref2$.subject, leftMatches = ref2$.matches;
ref2$ = matchAst(ast, selector.right, cache), rightSubject = ref2$.subject, rightMatches = ref2$.matches;
visitPre(ast, function(node, context){
var key, val, i$, len$, i, x, leftI, rightI, newSubject, that;
for (key in node) {
val = node[key];
if (toString$.call(val).slice(8, -1) === 'Array') {
for (i$ = 0, len$ = val.length; i$ < len$; ++i$) {
i = i$;
x = val[i$];
leftI = leftMatches.indexOf(x);
if (leftI > -1) {
rightI = rightMatches.indexOf(val[i + 1]);
if (rightI > -1) {
matches.push(val[i + 1]);
newSubject = [];
if (that = leftSubject[leftI]) {
newSubject = that;
}
if (that = rightSubject[rightI]) {
newSubject = newSubject.concat(that);
}
if (newSubject.length > 0) {
subject.push(newSubject);
}
}
}
}
}
}
});
}
return {
subject: subject,
matches: matches
};
function fn$(it){
return it.value;
}
function fn1$(it){
return in$(node, it);
}
function fn2$(rightNode){
var rightI, newSubject, that;
if (leftNode === rightNode) {
return;
}
rightI = rightMatches.indexOf(rightNode);
if (rightI > -1) {
matches.push(rightNode);
newSubject = [];
if (that = leftSubject[leftI]) {
newSubject = that;
}
if (that = rightSubject[rightI]) {
newSubject = newSubject.concat(that);
}
if (newSubject.length > 0) {
subject.push(newSubject);
}
}
}
function fn3$(child){
var rightI, newSubject, that;
rightI = rightMatches.indexOf(child);
if (rightI > -1) {
matches.push(child);
newSubject = [];
if (that = leftSubject[leftI]) {
newSubject = that;
}
if (that = rightSubject[rightI]) {
newSubject = newSubject.concat(that);
}
if (newSubject.length > 0) {
subject.push(newSubject);
}
}
}
}
function isMatchPrimitiveLiteral(nodeValue, op, value, valueType){
var nodeType;
nodeType = toString$.call(nodeValue).slice(8, -1);
if (nodeType === 'Undefined' || nodeType === 'Object') {
return false;
}
return op === '=' && (nodeValue === value || (nodeType === valueType && valueType === 'RegExp') && nodeValue.toString() === value.toString()) || op === '!=' && (nodeType !== 'RegExp' && nodeValue !== value || (nodeType === valueType && valueType === 'RegExp') && nodeValue.toString() !== value.toString()) || (op === '=~' || op === '~=') && value.test(nodeValue) || op === '<=' && nodeValue <= value || op === '>=' && nodeValue >= value || op === '<' && nodeValue < value || op === '>' && nodeValue > value;
}
function isMatchType(nodeValue, op, value){
var test;
test = (literalMap[value] || value).match(RegExp(toString$.call(nodeValue).slice(8, -1) + '', 'i'));
return op === '=' && test || op === '!=' && !test;
}
function addSubjectToFirst(sel){
var ref$;
if ((ref$ = sel.type) === 'descendant' || ref$ === 'child' || ref$ === 'sibling' || ref$ === 'adjacent') {
return addSubjectToFirst(sel.left);
} else {
return sel.subject = true;
}
}
function isMatchComplex(nodeValue, op, value, selector){
var cache, sel, subMatches, subMatchesLen;
if (nodeValue == null) {
return false;
}
cache = new Cache(nodeValue);
addSubjectToFirst(selector);
sel = {
type: 'compound',
selectors: [
selector, {
type: 'root'
}
]
};
subMatches = finalMatches(matchAst(nodeValue, sel, cache));
subMatchesLen = subMatches.length;
return op === '=' && subMatchesLen || op === '!=' && !subMatchesLen;
}
module.exports = {
finalMatches: finalMatches,
matchAst: matchAst
};
function in$(x, xs){
var i = -1, l = xs.length >>> 0;
while (++i < l) if (x === xs[i]) return true;
return false;
}
}).call(this);