UNPKG

grasp-squery

Version:

Grasp query backend using css style selectors

604 lines (603 loc) 18.7 kB
// 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);