UNPKG

xpath-parser

Version:
254 lines (243 loc) 8.14 kB
(function() { var close_scope, open_scope, pad, parse, update_scope; pad = function(s, n) { return new Array(n + 1).join(s); }; open_scope = function(scope, stack, string, bracket) { stack.unshift({}); scope.unshift({ bracket: bracket, ptr: stack.length, pos: string.length }); if (bracket === "[") { return stack[0].axis = "self"; } }; close_scope = function(scope, stack, error, opts) { var a, b, exp, swap, _base, _base1, _base2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; if (opts == null) { opts = {}; } exp = stack.splice(0, stack.length - scope[0].ptr + 1); if (((_ref = stack[0]) != null ? _ref.operator : void 0) != null) { ((_ref1 = (_base = stack[0]).args) != null ? _ref1 : _base.args = []).push(exp.reverse()); if (opts.operator) { stack.unshift({}); return; } _ref4 = [stack[0].operator, (_ref2 = stack[0].args[0][0].expression) != null ? (_ref3 = _ref2[0]) != null ? _ref3.operator : void 0 : void 0], a = _ref4[0], b = _ref4[1]; if ((a === "=" && (b != null) && /(and|or|mod|div)/i.test(b)) || (a === "and" && b === "or")) { swap = stack[0]; stack[0] = swap.args[0][0]; swap.args[0][0] = stack[0].expression[0].args[1][0]; stack[0].expression[0].args[1][0] = swap; } exp = [stack[0]]; stack[0] = {}; } if (opts.bracket != null) { if (opts.bracket !== scope[0].bracket) { throw error("unclosed scope"); } else if (!Object.keys(stack[0]).length) { stack.shift(); } } if (opts.bracket && scope[0].bracket === "[") { return ((_ref5 = (_base1 = stack[0]).predicate) != null ? _ref5 : _base1.predicate = []).push(exp.reverse()); } else { if (stack[0].q != null) { return ((_ref6 = (_base2 = stack[0]).args) != null ? _ref6 : _base2.args = []).push(exp.reverse()); } else { return stack[0].expression = exp.reverse(); } } }; update_scope = function(scope, stack, entry, error) { var i, _ref; if (stack.length > 0 && Object.keys(stack[0]).length === 0) { throw error("no first parameter given."); } i = stack.length - scope[0].ptr + 1; if (((_ref = stack[i]) != null ? _ref.operator : void 0) != null) { close_scope(scope, stack, error); } else { i = stack.length - scope[0].ptr++ + 1; } stack.splice(i, 0, entry); close_scope(scope, stack, error, { operator: true }); return scope[0].ptr = stack.length; }; exports.parse = parse = function(string) { var attr, axis, bracket, comparator, err, error, hit, i, name, o, opening, operator, orig, prefix, scope, sep, space, spacebefore, stack, star, union, val, value, _base, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; if (string == null) { string = ""; } error = function(msg) { var p; p = orig.length - string.length; return new Error("" + msg + "\n" + orig + "\n" + (pad('-', p)) + "^"); }; orig = "" + string; stack = [{}]; scope = [ { ptr: 1, pos: string.length } ]; spacebefore = false; while (string.length) { hit = null; if ((star = string.match(/^\*+/))) { star = star[0]; if (star.length > 1) { throw error("only on star at once"); } stack[0].nc = star; hit = star; } else if ((space = string.match(/^\s+/))) { hit = space[0]; } else if ((axis = string.match(/^\.+/))) { axis = axis[0]; if (stack[0].axis != null) { stack.unshift({}); } stack[0].seperator = "/"; stack[0].q = stack[0].nc = "node"; stack[0].args = [{}]; stack[0].axis = (function() { switch (axis.length) { case 1: return "self"; case 2: return "parent"; default: throw error("too many '.'"); } })(); hit = axis; } else if ((sep = string.match(/^\/+/))) { hit = '/'; sep = sep[0]; if (stack[0].axis != null) { stack.unshift({}); } stack[0].seperator = hit; stack[0].axis = (function() { switch (sep.length) { case 1: return "child"; case 2: return "descendant-or-self"; default: throw error("too much /"); } })(); if (sep.length === 2) { stack[0].q = stack[0].nc = "node"; stack[0].args = [{}]; } } else if ((axis = string.match(/^::/))) { axis = axis[0]; if (!((_ref = stack[0]) != null ? (_ref1 = _ref.nc) != null ? _ref1.length : void 0 : void 0)) { throw error("need some chars for axis"); } stack[0].axis = stack[0].nc; stack[0].nc = null; stack[0].q = null; hit = axis; } else if ((prefix = string.match(/^:/))) { prefix = prefix[0]; if (!((_ref2 = stack[0]) != null ? (_ref3 = _ref2.nc) != null ? _ref3.length : void 0 : void 0)) { throw error("need some chars for prefix"); } stack[0].prefix = stack[0].nc; stack[0].nc = null; stack[0].q = null; hit = prefix; } else if ((attr = string.match(/^@+/))) { attr = attr[0]; if (attr.length > 1) { throw error("only one @ at once"); } stack[0].axis = "attribute"; hit = attr; } else if ((bracket = string.match(/^(\[|\()/))) { bracket = bracket[0]; open_scope(scope, stack, string, bracket); hit = bracket; } else if ((bracket = string.match(/^(\]|\))/))) { bracket = bracket[0]; opening = bracket === "]" ? "[" : "("; if (!scope[0].bracket === opening) { string = orig.substr(orig.length - scope[0].pos); throw error("other unclosed scope"); } close_scope(scope, stack, error, { bracket: opening }); scope.shift(); hit = bracket; } else if ((name = string.match(/^([^\(\)\[\]\s'":.=!<>\/|])+/))) { name = name[0]; i = stack.length - scope[0].ptr + 1; if (spacebefore && /(and|or|mod|div)/i.test(name)) { update_scope(scope, stack, { operator: name }, error); } else if (((o = (_ref4 = stack[i]) != null ? _ref4.operator : void 0) != null) && (stack[0].axis == null) && o !== "union") { stack[0].value = name; } else { stack[0].nc = name; stack[0].q = name; if ((_ref5 = (_base = stack[0]).axis) == null) { _base.axis = "child"; } if (stack[0].prefix != null) { stack[0].q = stack[0].prefix + ":" + name; } } hit = name; } else if ((union = string.match(/^\|+/))) { union = union[0]; if (union.length > 1) { throw error("only one | at once"); } update_scope(scope, stack, { operator: "union" }, error); hit = union; } else if ((comparator = string.match(/^((|!|<|>)=)|>|</))) { operator = comparator[0]; update_scope(scope, stack, { operator: operator }, error); hit = operator; } else if ((scope[0] != null) && (value = string.match(/^('([^']*)'|"([^"]*)")/))) { value = value[0]; hit = value; if ((val = value.match(/^("|')(.*)("|')$/))) { value = val[2]; } stack[0].value = value; } else { throw error("not parsable"); } string = string.substr(hit.length); spacebefore = space != null; } if (scope[0].ptr !== 1) { close_scope(scope, stack); scope.shift(); } if (scope.length > 1) { err = scope[scope.length - 2]; string = orig.substr(orig.length - err.pos); throw error("no closing bracket"); } return stack.reverse(); }; }).call(this);