grasp-squery
Version:
Grasp query backend using css style selectors
604 lines (603 loc) • 18.7 kB
JavaScript
// Generated by LiveScript 1.4.0
(function(){
var ref$, map, last, lines, compact, join, aliasMap, matchesMap, matchesAliasMap, literalMap, complexTypeMap, attrMap, primitiveOnlyAttributes, eitherAttributes, tokenSplit, operatorMap, toString$ = {}.toString;
ref$ = require('prelude-ls'), map = ref$.map, last = ref$.last, lines = ref$.lines, compact = ref$.compact, join = ref$.join;
ref$ = require('grasp-syntax-javascript'), aliasMap = ref$.aliasMap, matchesMap = ref$.matchesMap, matchesAliasMap = ref$.matchesAliasMap, literalMap = ref$.literalMap, complexTypeMap = ref$.complexTypeMap, attrMap = ref$.attrMap, primitiveOnlyAttributes = ref$.primitiveOnlyAttributes, eitherAttributes = ref$.eitherAttributes;
function parse(selector){
return processTokens(tokenize(selector + ""));
}
tokenSplit = /\s*(\/(?:\\\/|[^\/])*\/[gimy]*)\s*|(type\([a-zA-Z]*\))|([-a-zA-Z$_][-a-zA-Z$_0-9]*)|([-+]?[0-9]*\.?[0-9]+)|("(?:\\"|[^"])*")|('(?:\\'|[^'])*')|(\*|::?|\+\+|#)|\s*(!=|<=|>=|=~|~=)\s*|\s*(\]|\)|!|\.)|(\[&|\[)\s*|\s*(\,|~|<|>|=|\+|\||\(|\s)\s*/;
function tokenize(selector){
var cleanSelector, i$, ref$, len$, token, that, results$ = [];
cleanSelector = join(',')(
compact(
map(function(it){
return it.replace(/^\s*|\s*$/g, '');
})(
lines(
selector))));
for (i$ = 0, len$ = (ref$ = cleanSelector.split(tokenSplit)).length; i$ < len$; ++i$) {
token = ref$[i$];
if (token) {
if (token === '*') {
results$.push({
type: 'wildcard',
value: '*'
});
} else if (that = /^type\(([a-zA-Z]*)\)$/.exec(token)) {
results$.push({
type: 'type',
value: that[1]
});
} else if (token === 'type' || token === 'root' || token === 'not' || token === 'matches' || token === 'first' || token === 'head' || token === 'tail' || token === 'last' || token === 'initial' || token === 'nth' || token === 'nth-last' || token === 'slice' || token === 'first-child' || token === 'nth-child' || token === 'nth-last-child' || token === 'last-child') {
results$.push({
type: 'keyword',
value: token
});
} else if (token === 'true' || token === 'false') {
results$.push({
type: 'boolean',
value: token === 'true'
});
} else if (token === 'null') {
results$.push({
type: 'null',
value: null
});
} else if (that = /^['"](.*)['"]$/.exec(token)) {
results$.push({
type: 'string',
value: that[1].replace(/\\"/, '"').replace(/\\'/, "'")
});
} else if (/^[-+]?[0-9]*\.?[0-9]+$/.test(token)) {
results$.push({
type: 'number',
value: parseFloat(token)
});
} else if (that = /^\/(.*)\/([gimy]*)$/.exec(token)) {
results$.push({
type: 'regexp',
value: new RegExp(that[1], that[2])
});
} else if ((token === '!=' || token === '<=' || token === '>=' || token === '=~' || token === '~=' || token === '>' || token === '<' || token === ',' || token === '~' || token === '=' || token === '!' || token === '#' || token === '.' || token === ':' || token === '::' || token === '+' || token === '[&' || token === '[' || token === ']' || token === '(' || token === ')') || token.match(/\s/)) {
results$.push({
type: 'operator',
value: token
});
} else {
results$.push({
type: 'identifier',
value: token
});
}
}
}
return results$;
}
function processTokens(tokens){
if (!tokens.length) {
return null;
}
tokens.unshift({
type: 'operator',
value: '('
});
tokens.push({
type: 'operator',
value: ')'
});
return consumeImplicitMatches(tokens);
}
function consumeImplicitMatches(tokens){
var args;
args = consumeComplexArgList(tokens);
if (args.length > 1) {
return {
type: 'matches',
selectors: args
};
} else {
return args[0];
}
}
function peekOp(tokens, opValue){
if (tokens.length > 0 && peekType(tokens, 'operator') && (toString$.call(opValue).slice(8, -1) === 'RegExp' && opValue.test(tokens[0].value) || tokens[0].value === opValue)) {
return tokens[0];
}
}
function consumeOp(tokens, opValue){
if (peekOp(tokens, opValue)) {
return tokens.shift();
} else {
throw createError("Expected operator " + opValue + ", but found:", tokens[0], tokens);
}
}
function peekType(tokens, type){
if (tokens.length > 0 && (tokens[0].type === type || toString$.call(type).slice(8, -1) === 'Array' && in$(tokens[0].type, type))) {
return tokens[0];
}
}
function consumeType(tokens, type){
if (peekType(tokens, type)) {
return tokens.shift();
} else {
throw createError("Expected type " + type + ", but found:", tokens[0], tokens);
}
}
operatorMap = {
' ': 'descendant',
'>': 'child',
'~': 'sibling',
'+': 'adjacent'
};
function consumeComplexSelector(tokens){
var ops, root, wildcard, result, op, opVal, selector;
ops = /^[\s>~+]$/;
root = {
type: 'root'
};
wildcard = {
type: 'wildcard'
};
result = peekOp(tokens, ops)
? root
: consumeCompoundSelector(tokens);
while (peekOp(tokens, ops)) {
op = tokens.shift();
opVal = op.value;
selector = consumeCompoundSelector(tokens);
result = {
type: operatorMap[opVal],
operator: opVal,
left: result,
right: selector || wildcard
};
}
return result;
}
function consumeCompoundSelector(tokens){
var result, that, selector;
result = consumeSelector(tokens);
if (that = consumeProps(tokens)) {
result = (that.left = result, that);
}
while (tokens.length > 0) {
selector = consumeSelector(tokens);
if (selector) {
if (result.type !== 'compound') {
result = {
type: 'compound',
selectors: [result]
};
}
result.selectors.push(selector);
if (that = consumeProps(tokens)) {
result = (that.left = result, that);
}
} else {
break;
}
}
return result || selector;
}
function mapSimpleSelector(value){
return {
type: 'identifier',
value: aliasMap[value] || value
};
}
function consumeIdentifier(tokens){
var value, val;
value = tokens.shift().value;
if (value in literalMap) {
return {
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'Literal'
}, {
type: 'attribute',
name: 'value',
operator: '=',
valType: 'primitive',
value: {
type: 'type',
value: literalMap[value]
}
}
]
};
} else if (value in matchesMap || value in matchesAliasMap) {
return {
type: 'matches',
selectors: (function(){
var i$, ref$, len$, results$ = [];
for (i$ = 0, len$ = (ref$ = matchesMap[matchesAliasMap[value] || value]).length; i$ < len$; ++i$) {
val = ref$[i$];
results$.push({
type: 'identifier',
value: val
});
}
return results$;
}())
};
} else if (value in complexTypeMap) {
switch (complexTypeMap[value]) {
case 'ImmediatelyInvokedFunctionExpression':
return {
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'CallExpression'
}, {
type: 'attribute',
name: 'callee',
operator: '=',
valType: 'complex',
value: {
type: 'matches',
selectors: [
{
type: 'identifier',
value: 'FunctionExpression'
}, {
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'MemberExpression'
}, {
type: 'attribute',
name: 'object',
operator: '=',
valType: 'complex',
value: {
type: 'identifier',
value: 'FunctionExpression'
}
}, {
type: 'attribute',
name: 'property',
operator: '=',
valType: 'complex',
value: {
type: 'matches',
selectors: [
{
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'Identifier'
}, {
type: 'attribute',
name: 'name',
operator: '=',
valType: 'primitive',
value: {
type: 'literal',
value: 'call'
}
}
]
}, {
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'Identifier'
}, {
type: 'attribute',
name: 'name',
operator: '=',
valType: 'primitive',
value: {
type: 'literal',
value: 'apply'
}
}
]
}
]
}
}
]
}
]
}
}
]
};
}
} else {
return mapSimpleSelector(value);
}
}
function consumeSelector(tokens){
var selector, token, value;
selector = peekType(tokens, 'wildcard')
? tokens.shift()
: peekOp(tokens, '::')
? (tokens.shift(), consumeIdentifier(tokens))
: peekType(tokens, ['keyword', 'identifier'])
? consumeIdentifier(tokens)
: peekType(tokens, ['number', 'string', 'regexp', 'boolean', 'null'])
? consumeLiteral(tokens)
: peekOp(tokens, ':')
? consumePseudo(tokens)
: peekOp(tokens, /\[&?/)
? consumeAttribute(tokens)
: peekOp(tokens, '#')
? (consumeOp(tokens, '#'), token = tokens.shift(), value = token.value, {
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'Identifier'
}, {
type: 'attribute',
name: 'name',
operator: token.type === 'regexp' ? '=~' : '=',
valType: 'primitive',
value: {
type: 'literal',
value: value
}
}
]
})
: peekOp(tokens, '(')
? consumeImplicitMatches(tokens)
: peekOp(tokens, '.') ? {
type: 'root'
} : void 8;
if (selector) {
if (peekOp(tokens, '!')) {
tokens.shift();
selector.subject = true;
}
}
return selector;
}
function consumeProps(tokens){
var props, propSubjectIndices, i, ref$;
props = [];
propSubjectIndices = {};
i = 0;
while (peekOp(tokens, '.') || peekOp(tokens, ':') && ((ref$ = tokens[1].value) === 'first' || ref$ === 'head' || ref$ === 'tail' || ref$ === 'last' || ref$ === 'initial' || ref$ === 'nth' || ref$ === 'nth-last' || ref$ === 'slice')) {
props.push(peekOp(tokens, '.')
? consumeProp(tokens)
: consumePseudo(tokens));
if (peekOp(tokens, '!')) {
consumeOp(tokens, '!');
propSubjectIndices[i] = true;
}
i++;
}
if (props.length) {
return {
type: 'prop',
props: props,
subjects: propSubjectIndices
};
}
}
function consumeLiteral(tokens){
var token, value;
token = tokens.shift();
value = token.value;
return {
type: 'compound',
selectors: [
{
type: 'identifier',
value: 'Literal'
}, {
type: 'attribute',
name: 'value',
operator: '=',
valType: 'primitive',
value: {
type: 'literal',
value: value
}
}
]
};
}
function consumePseudo(tokens){
var op, id, that;
op = consumeOp(tokens, ':');
id = consumeType(tokens, 'keyword');
switch (that = id.value) {
case 'root':
case 'first':
case 'head':
case 'tail':
case 'last':
case 'initial':
return {
type: that
};
case 'nth':
case 'nth-last':
case 'nth-child':
case 'nth-last-child':
return {
type: that,
index: consumeArg(tokens)
};
case 'slice':
return {
type: that,
indicies: consumeArgList(tokens)
};
case 'first-child':
return {
type: 'nth-child',
index: {
type: 'literal',
value: 0
}
};
case 'last-child':
return {
type: 'nth-last-child',
index: {
type: 'literal',
value: 0
}
};
case 'matches':
return consumeImplicitMatches(tokens);
case 'not':
return {
type: that,
selectors: consumeComplexArgList(tokens)
};
default:
throw createError('Unexpected keyword:', id, tokens);
}
}
function consumeName(tokens){
var name, val;
name = '';
while (!name || peekOp(tokens, '.')) {
if (name) {
consumeOp(tokens, '.');
name += '.';
}
val = consumeType(tokens, ['keyword', 'identifier']).value;
name += attrMap[val] || val;
}
return name;
}
function consumeAttribute(tokens){
var op, name, lastName, nextOp, nextToken, val, ref$, valType, value, selector;
op = consumeType(tokens, 'operator').value;
name = consumeName(tokens);
lastName = last(name.split('.'));
nextOp = consumeType(tokens, 'operator').value;
if (nextOp === ']') {
return {
type: 'attribute',
name: name
};
} else {
nextToken = tokens[0];
ref$ = op === '[&' || nextToken.type === 'type' || in$(lastName, primitiveOnlyAttributes)
? ['primitive', consumeValue(tokens)]
: in$(lastName, eitherAttributes)
? (val = consumeValue([tokens[0]]), [
'either', {
type: val.type,
value: val.value,
sel: consumeSelector(tokens)
}
])
: ['complex', consumeComplexSelector(tokens)], valType = ref$[0], value = ref$[1];
selector = {
type: 'attribute',
name: name,
operator: nextOp,
valType: valType,
value: value
};
consumeOp(tokens, ']');
return selector;
}
}
function consumeProp(tokens){
var token, name;
consumeOp(tokens, '.');
if (peekType(tokens, ['identifier', 'number', 'null', 'boolean'])) {
token = consumeType(tokens, ['identifier', 'number', 'null', 'boolean']);
name = token.value;
return {
type: 'string',
value: attrMap[name] || name
};
} else {
return {
type: 'wildcard'
};
}
}
function consumeComplexArgList(tokens){
var result, arg;
consumeOp(tokens, '(');
result = [];
while (tokens.length > 0) {
arg = consumeComplexSelector(tokens);
if (arg) {
result.push(arg);
} else {
throw createError('Expected selector argument:', tokens[0], tokens);
}
if (peekOp(tokens, ',')) {
consumeOp(tokens, ',');
} else {
break;
}
}
consumeOp(tokens, ')');
return result;
}
function consumeArgList(tokens){
var result, arg;
consumeOp(tokens, '(');
result = [];
while (tokens.length > 0) {
arg = consumeValue(tokens);
if (arg) {
result.push(arg);
} else {
throw createError('Expected argument:', tokens[0], tokens);
}
if (peekOp(tokens, ',')) {
consumeOp(tokens, ',');
} else {
break;
}
}
consumeOp(tokens, ')');
return result;
}
function consumeArg(tokens){
var value;
consumeOp(tokens, '(');
value = consumeValue(tokens);
consumeOp(tokens, ')');
return value;
}
function consumeValue(tokens){
var token, value, type;
token = tokens.shift(), value = token.value, type = token.type;
if (type === 'type') {
if (!value) {
throw createError("Expected argument for 'type'.", token, tokens);
}
return token;
} else if (value !== ',' && value !== '(' && value !== ')' && value !== '[' && value !== ']' && value !== '[&') {
return {
type: 'literal',
value: value
};
}
}
function createError(message, token, tokens){
return new Error(message + " " + JSON.stringify(token) + "\nRemaining tokens: " + JSON.stringify(tokens, null, ' '));
}
module.exports = {
parse: parse,
tokenize: tokenize
};
function in$(x, xs){
var i = -1, l = xs.length >>> 0;
while (++i < l) if (x === xs[i]) return true;
return false;
}
}).call(this);