xpath-parser
Version:
simple xpath parser
254 lines (243 loc) • 8.14 kB
JavaScript
(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);