From a516856c2a214a6c7b5e4a45975fd53317e785e3 Mon Sep 17 00:00:00 2001 From: koernere Date: Tue, 9 Aug 2022 18:08:15 +0200 Subject: [PATCH 01/12] reformat js (vscode default) --- cql.js | 396 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 197 insertions(+), 199 deletions(-) diff --git a/cql.js b/cql.js index 37035d3..d9ffab9 100644 --- a/cql.js +++ b/cql.js @@ -18,19 +18,19 @@ var CQLModifier = function () { CQLModifier.prototype = { toString: function () { - return this.name + this.relation + this.value; + return this.name + this.relation + this.value; }, toXCQL: function (n, c) { - var s = indent(n+1, c) + "\n"; - s = s + indent(n+2, c) + "" + this.name + "\n"; + var s = indent(n + 1, c) + "\n"; + s = s + indent(n + 2, c) + "" + this.name + "\n"; if (this.relation != null) - s = s + indent(n+2, c) + s = s + indent(n + 2, c) + "" + this.relation + "\n"; if (this.value != null) - s = s + indent(n+2, c) - + "" + this.value +"\n"; - s = s + indent(n+1, c) + "\n"; + s = s + indent(n + 2, c) + + "" + this.value + "\n"; + s = s + indent(n + 1, c) + "\n"; return s; }, @@ -38,14 +38,14 @@ CQLModifier.prototype = { //we ignore modifier relation symbol, for value-less modifiers //we assume 'true' var value = this.value.length > 0 ? this.value : "true"; - var s = '"'+this.name+'": "'+value+'"'; + var s = '"' + this.name + '": "' + value + '"'; return s; } } // CQLSearchClause -var CQLSearchClause = function (field, fielduri, relation, relationuri, - modifiers, term, scf, scr) { +var CQLSearchClause = function (field, fielduri, relation, relationuri, + modifiers, term, scf, scr) { this.field = field; this.fielduri = fielduri; this.relation = relation; @@ -58,95 +58,94 @@ var CQLSearchClause = function (field, fielduri, relation, relationuri, CQLSearchClause.prototype = { toString: function () { - var field = this.field; - var relation = this.relation; - if (field == this.scf && relation == this.scr) { - //avoid redundant field/relation - field = null; - relation = null; - } - return (field ? field + ' ' : '') + - (relation ? relation : '') + - (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') + - (relation || this.modifiers.length ? ' ' : '') + - '"' + this.term + '"'; + var field = this.field; + var relation = this.relation; + if (field == this.scf && relation == this.scr) { + //avoid redundant field/relation + field = null; + relation = null; + } + return (field ? field + ' ' : '') + + (relation ? relation : '') + + (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') + + (relation || this.modifiers.length ? ' ' : '') + + '"' + this.term + '"'; }, toXCQL: function (n, c) { var s = indent(n, c) + "\n"; - if (this.fielduri.length > 0) - { - s = s + indent(n+1, c) + "\n" + - indent(n+2, c) + "\n" + - indent(n+3, c) + "" + this.fielduri + + if (this.fielduri.length > 0) { + s = s + indent(n + 1, c) + "\n" + + indent(n + 2, c) + "\n" + + indent(n + 3, c) + "" + this.fielduri + "\n" + - indent(n+2, c) + "\n" + - indent(n+1, c) + "\n"; + indent(n + 2, c) + "\n" + + indent(n + 1, c) + "\n"; } - s = s + indent(n+1, c) + "" + this.field + "\n"; - s = s + indent(n+1, c) + "\n"; + s = s + indent(n + 1, c) + "" + this.field + "\n"; + s = s + indent(n + 1, c) + "\n"; if (this.relationuri.length > 0) { - s = s + indent(n+2, c) + + s = s + indent(n + 2, c) + "" + this.relationuri + "\n"; } - s = s + indent(n+2, c) + "" + this.relation + "\n"; + s = s + indent(n + 2, c) + "" + this.relation + "\n"; if (this.modifiers.length > 0) { - s = s + indent(n+2, c) + "\n"; + s = s + indent(n + 2, c) + "\n"; for (var i = 0; i < this.modifiers.length; i++) - s = s + this.modifiers[i].toXCQL(n+2, c); - s = s + indent(n+2, c) + "\n"; + s = s + this.modifiers[i].toXCQL(n + 2, c); + s = s + indent(n + 2, c) + "\n"; } - s = s + indent(n+1, c) + "\n"; - s = s + indent(n+1, c) + "" + this.term + "\n"; + s = s + indent(n + 1, c) + "\n"; + s = s + indent(n + 1, c) + "" + this.term + "\n"; s = s + indent(n, c) + "\n"; return s; }, toFQ: function () { - var s = '{"term": "'+this.term+'"'; + var s = '{"term": "' + this.term + '"'; if (this.field.length > 0 && this.field != this.scf) - s+= ', "field": "'+this.field+'"'; + s += ', "field": "' + this.field + '"'; if (this.relation.length > 0 && this.relation != this.scr) - s+= ', "relation": "'+this._mapRelation(this.relation)+'"'; + s += ', "relation": "' + this._mapRelation(this.relation) + '"'; for (var i = 0; i < this.modifiers.length; i++) { - //since modifiers are mapped to keys, ignore the reserved ones - if (this.modifiers[i].name == "term" - ||this.modifiers[i].name == "field" - ||this.modifiers[i].name == "relation") - continue; - s += ', ' + this.modifiers[i].toFQ(); + //since modifiers are mapped to keys, ignore the reserved ones + if (this.modifiers[i].name == "term" + || this.modifiers[i].name == "field" + || this.modifiers[i].name == "relation") + continue; + s += ', ' + this.modifiers[i].toFQ(); } s += '}'; return s; }, _mapRelation: function (rel) { - switch(rel) { - case "<" : return "lt"; - case ">" : return "gt"; - case "=" : return "eq"; - case "<>" : return "ne"; - case ">=" : return "ge"; - case "<=" : return "le"; - default: return rel; - } + switch (rel) { + case "<": return "lt"; + case ">": return "gt"; + case "=": return "eq"; + case "<>": return "ne"; + case ">=": return "ge"; + case "<=": return "le"; + default: return rel; + } }, _remapRelation: function (rel) { - switch(rel) { - case "lt" : return "<"; - case "gt" : return ">"; - case "eq" : return "="; - case "ne" : return "<>"; - case "ge" : return ">="; - case "le" : return "<="; - default: return rel; - } + switch (rel) { + case "lt": return "<"; + case "gt": return ">"; + case "eq": return "="; + case "ne": return "<>"; + case "ge": return ">="; + case "le": return "<="; + default: return rel; + } } } // CQLBoolean -var CQLBoolean = function() { +var CQLBoolean = function () { this.op = null; this.modifiers = null; this.left = null; @@ -155,41 +154,41 @@ var CQLBoolean = function() { CQLBoolean.prototype = { toString: function () { - return (this.left.op ? '(' + this.left + ')' : this.left) + ' ' + - this.op.toUpperCase() + - (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') + - ' ' + (this.right.op ? '(' + this.right + ')' : this.right);; + return (this.left.op ? '(' + this.left + ')' : this.left) + ' ' + + this.op.toUpperCase() + + (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') + + ' ' + (this.right.op ? '(' + this.right + ')' : this.right);; }, toXCQL: function (n, c) { var s = indent(n, c) + "\n"; - s = s + indent(n+1, c) + "\n" + - indent(n+2, c) + "" + this.op + "\n"; + s = s + indent(n + 1, c) + "\n" + + indent(n + 2, c) + "" + this.op + "\n"; if (this.modifiers.length > 0) { - s = s + indent(n+2, c) + "\n"; + s = s + indent(n + 2, c) + "\n"; for (var i = 0; i < this.modifiers.length; i++) - s = s + this.modifiers[i].toXCQL(n+2, c); - s = s + indent(n+2, c) + "\n"; + s = s + this.modifiers[i].toXCQL(n + 2, c); + s = s + indent(n + 2, c) + "\n"; } - s = s + indent(n+1, c) + "\n"; - s = s + indent(n+1, c) + "\n" + - this.left.toXCQL(n+2, c) + indent(n+1, c) + "\n"; + s = s + indent(n + 1, c) + "\n"; + s = s + indent(n + 1, c) + "\n" + + this.left.toXCQL(n + 2, c) + indent(n + 1, c) + "\n"; - s = s + indent(n+1, c) + "\n" + - this.right.toXCQL(n+2, c) + indent(n+1, c) + "\n"; + s = s + indent(n + 1, c) + "\n" + + this.right.toXCQL(n + 2, c) + indent(n + 1, c) + "\n"; s = s + indent(n, c) + "\n"; return s; }, toFQ: function (n, c, nl) { - var s = '{"op": "'+this.op+'"'; - //proximity modifiers - for (var i = 0; i < this.modifiers.length; i++) - s += ', ' + this.modifiers[i].toFQ(); - s += ','+nl+indent(n, c)+' "s1": '+this.left.toFQ(n+1, c, nl); - s += ','+nl+indent(n, c)+' "s2": '+this.right.toFQ(n+1, c, nl); - var fill = n && c ? ' ' : ''; - s += nl+indent(n-1, c)+fill+'}'; - return s; + var s = '{"op": "' + this.op + '"'; + //proximity modifiers + for (var i = 0; i < this.modifiers.length; i++) + s += ', ' + this.modifiers[i].toFQ(); + s += ',' + nl + indent(n, c) + ' "s1": ' + this.left.toFQ(n + 1, c, nl); + s += ',' + nl + indent(n, c) + ' "s2": ' + this.right.toFQ(n + 1, c, nl); + var fill = n && c ? ' ' : ''; + s += nl + indent(n - 1, c) + fill + '}'; + return s; } } @@ -211,77 +210,77 @@ CQLParser.prototype = { parse: function (query, scf, scr) { if (!query) throw new Error("The query to be parsed cannot be empty"); - this.scf = typeof scf != 'string' - ? DEFAULT_SERVER_CHOICE_FIELD : scf; - this.scr = typeof scr != 'string' - ? DEFAULT_SERVER_CHOICE_RELATION : scr; + this.scf = typeof scf != 'string' + ? DEFAULT_SERVER_CHOICE_FIELD : scf; + this.scr = typeof scr != 'string' + ? DEFAULT_SERVER_CHOICE_RELATION : scr; this.qs = query; this.ql = this.qs.length; this.qi = 0; - this._move(); + this._move(); this.tree = this._parseQuery(this.scf, this.scr, new Array()); if (this.look != "") throw new Error("EOF expected"); }, parseFromFQ: function (query, scf, scr) { - if (!query) - throw new Error("The query to be parsed cannot be empty"); - if (typeof query == 'string') - query = JSON.parse(query); - this.scf = typeof scf != 'string' - ? DEFAULT_SERVER_CHOICE_FIELD : scf; - this.scr = typeof scr != 'string' - ? DEFAULT_SERVER_CHOICE_RELATION : scr; - this.tree = this._parseFromFQ(query); + if (!query) + throw new Error("The query to be parsed cannot be empty"); + if (typeof query == 'string') + query = JSON.parse(query); + this.scf = typeof scf != 'string' + ? DEFAULT_SERVER_CHOICE_FIELD : scf; + this.scr = typeof scr != 'string' + ? DEFAULT_SERVER_CHOICE_RELATION : scr; + this.tree = this._parseFromFQ(query); }, _parseFromFQ: function (fq) { //op-node - if (fq.hasOwnProperty('op') + if (fq.hasOwnProperty('op') && fq.hasOwnProperty('s1') && fq.hasOwnProperty('s2')) { - var node = new CQLBoolean(); - node.op = fq.op; - node.left = this._parseFromFQ(fq.s1); - node.right = this._parseFromFQ(fq.s2); - //include all other members as modifiers - node.modifiers = []; - for (var key in fq) { - if (key == 'op' || key == 's1' || key == 's2') - continue; - var mod = new CQLModifier(); - mod.name = key; - mod.relation = '='; - mod.value = fq[key]; - node.modifiers.push(mod); - } - return node; + var node = new CQLBoolean(); + node.op = fq.op; + node.left = this._parseFromFQ(fq.s1); + node.right = this._parseFromFQ(fq.s2); + //include all other members as modifiers + node.modifiers = []; + for (var key in fq) { + if (key == 'op' || key == 's1' || key == 's2') + continue; + var mod = new CQLModifier(); + mod.name = key; + mod.relation = '='; + mod.value = fq[key]; + node.modifiers.push(mod); + } + return node; } //search-clause node if (fq.hasOwnProperty('term')) { - var node = new CQLSearchClause(); - node.term = fq.term; - node.scf = this.scf; - node.scr = this.scr; - node.field = fq.hasOwnProperty('field') - ? fq.field : this.scf; - node.relation = fq.hasOwnProperty('relation') - ? node._remapRelation(fq.relation) : this.scr; - //include all other members as modifiers - node.relationuri = ''; - node.fielduri = ''; - node.modifiers = []; - for (var key in fq) { - if (key == 'term' || key == 'field' || key == 'relation') - continue; - var mod = new CQLModifier(); - mod.name = key; - mod.relation = '='; - mod.value = fq[key]; - node.modifiers.push(mod); - } - return node; + var node = new CQLSearchClause(); + node.term = fq.term; + node.scf = this.scf; + node.scr = this.scr; + node.field = fq.hasOwnProperty('field') + ? fq.field : this.scf; + node.relation = fq.hasOwnProperty('relation') + ? node._remapRelation(fq.relation) : this.scr; + //include all other members as modifiers + node.relationuri = ''; + node.fielduri = ''; + node.modifiers = []; + for (var key in fq) { + if (key == 'term' || key == 'field' || key == 'relation') + continue; + var mod = new CQLModifier(); + mod.name = key; + mod.relation = '='; + mod.value = fq[key]; + node.modifiers.push(mod); + } + return node; } - throw new Error('Unknow node type; '+JSON.stringify(fq)); + throw new Error('Unknow node type; ' + JSON.stringify(fq)); }, toXCQL: function (c) { c = typeof c == "undefined" ? ' ' : c; @@ -295,13 +294,13 @@ CQLParser.prototype = { toString: function () { return this.tree.toString(); }, - _parseQuery: function(field, relation, modifiers) { + _parseQuery: function (field, relation, modifiers) { var left = this._parseSearchClause(field, relation, modifiers); while (this.look == "s" && ( - this.lval == "and" || - this.lval == "or" || - this.lval == "not" || - this.lval == "prox")) { + this.lval == "and" || + this.lval == "or" || + this.lval == "not" || + this.lval == "prox")) { var b = new CQLBoolean(); b.op = this.lval; this._move(); @@ -312,22 +311,22 @@ CQLParser.prototype = { } return left; }, - _parseModifiers: function() { + _parseModifiers: function () { var ar = new Array(); while (this.look == "/") { this._move(); if (this.look != "s" && this.look != "q") throw new Error("Invalid modifier.") - + var name = this.lval; this._move(); - if (this.look.length > 0 + if (this.look.length > 0 && this._strchr("<>=", this.look.charAt(0))) { var rel = this.look; this._move(); if (this.look != "s" && this.look != "q") throw new Error("Invalid relation within the modifier."); - + var m = new CQLModifier(); m.name = name; m.relation = rel; @@ -344,7 +343,7 @@ CQLParser.prototype = { } return ar; }, - _parseSearchClause: function(field, relation, modifiers) { + _parseSearchClause: function (field, relation, modifiers) { if (this.look == "(") { this._move(); var b = this._parseQuery(field, relation, modifiers); @@ -358,32 +357,32 @@ CQLParser.prototype = { var first = this.val; // dont know if field or term yet this._move(); if (this.look == "q" || - (this.look == "s" && - this.lval != "and" && - this.lval != "or" && - this.lval != "not" && - this.lval != "prox")) { + (this.look == "s" && + this.lval != "and" && + this.lval != "or" && + this.lval != "not" && + this.lval != "prox")) { var rel = this.val; // string relation this._move(); return this._parseSearchClause(first, rel, - this._parseModifiers()); - } else if (this.look.length > 0 - && this._strchr("<>=", this.look.charAt(0))) { + this._parseModifiers()); + } else if (this.look.length > 0 + && this._strchr("<>=", this.look.charAt(0))) { var rel = this.look; // other relation <, = ,etc this._move(); - return this._parseSearchClause(first, rel, - this._parseModifiers()); + return this._parseSearchClause(first, rel, + this._parseModifiers()); } else { // it's a search term var pos = field.indexOf('.'); var pre = ""; if (pos != -1) pre = field.substring(0, pos); - + var uri = this._lookupPrefix(pre); if (uri.length > 0) - field = field.substring(pos+1); - + field = field.substring(pos + 1); + pos = relation.indexOf('.'); if (pos == -1) pre = "cql"; @@ -392,32 +391,31 @@ CQLParser.prototype = { var reluri = this._lookupPrefix(pre); if (reluri.Length > 0) - relation = relation.Substring(pos+1); + relation = relation.Substring(pos + 1); var sc = new CQLSearchClause(field, - uri, - relation, - reluri, - modifiers, - first, - this.scf, - this.scr); + uri, + relation, + reluri, + modifiers, + first, + this.scf, + this.scr); return sc; } - // prefixes + // prefixes } else if (this.look == ">") { this._move(); if (this.look != "s" && this.look != "q") throw new Error("Expecting string or a quoted expression."); - + var first = this.lval; this._move(); - if (this.look == "=") - { + if (this.look == "=") { this._move(); if (this.look != "s" && this.look != "q") throw new Error("Expecting string or a quoted expression."); - + this._addPrefix(first, this.lval); this._move(); return this._parseQuery(field, relation, modifiers); @@ -432,8 +430,8 @@ CQLParser.prototype = { }, _move: function () { //eat whitespace - while (this.qi < this.ql - && this._strchr(" \t\r\n", this.qs.charAt(this.qi))) + while (this.qi < this.ql + && this._strchr(" \t\r\n", this.qs.charAt(this.qi))) this.qi++; //eof if (this.qi == this.ql) { @@ -446,17 +444,17 @@ CQLParser.prototype = { if (this._strchr("()/", c)) { this.look = c; this.qi++; - //comparitor + //comparitor } else if (this._strchr("<>=", c)) { this.look = c; this.qi++; //comparitors can repeat, could be if - while (this.qi < this.ql - && this._strchr("<>=", this.qs.charAt(this.qi))) { + while (this.qi < this.ql + && this._strchr("<>=", this.qs.charAt(this.qi))) { this.look = this.look + this.qs.charAt(this.qi); this.qi++; } - //quoted string + //quoted string } else if (this._strchr("\"'", c)) { this.look = "q"; //remember quote char @@ -465,26 +463,26 @@ CQLParser.prototype = { this.val = ""; var escaped = false; while (this.qi < this.ql) { - if (!escaped && this.qs.charAt(this.qi) == mark) - break; - if (!escaped && this.qs.charAt(this.qi) == '\\') - escaped = true; - else - escaped = false; - this.val += this.qs.charAt(this.qi); - this.qi++; + if (!escaped && this.qs.charAt(this.qi) == mark) + break; + if (!escaped && this.qs.charAt(this.qi) == '\\') + escaped = true; + else + escaped = false; + this.val += this.qs.charAt(this.qi); + this.qi++; } this.lval = this.val.toLowerCase(); if (this.qi < this.ql) this.qi++; else //unterminated - this.look = ""; //notify error - //unquoted string + this.look = ""; //notify error + //unquoted string } else { this.look = "s"; this.val = ""; - while (this.qi < this.ql - && !this._strchr("()/<>= \t\r\n", this.qs.charAt(this.qi))) { + while (this.qi < this.ql + && !this._strchr("()/<>= \t\r\n", this.qs.charAt(this.qi))) { this.val = this.val + this.qs.charAt(this.qi); this.qi++; } @@ -494,10 +492,10 @@ CQLParser.prototype = { _strchr: function (s, ch) { return s.indexOf(ch) >= 0 }, - _lookupPrefix: function(name) { + _lookupPrefix: function (name) { return this.prefixes[name] ? this.prefixes[name] : ""; }, - _addPrefix: function(name, value) { + _addPrefix: function (name, value) { //overwrite existing items this.prefixes[name] = value; } From f121b10222a52417810b32608146585c25e7ad43 Mon Sep 17 00:00:00 2001 From: koernere Date: Tue, 9 Aug 2022 18:24:53 +0200 Subject: [PATCH 02/12] Add better error context --- cql.js | 33 +++++++++++++++++++++++---------- index.html | 9 +++++++-- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/cql.js b/cql.js index d9ffab9..7f7cc5a 100644 --- a/cql.js +++ b/cql.js @@ -9,6 +9,19 @@ function indent(n, c) { s = s + c; return s; } + +var CQLError = function (message, query, pos, look, val, lval) { + this.name = "CQLError"; + this.message = message; + this.query = query; + this.pos = pos; + this.look = look; + this.val = val; + this.lval = lval; + this.stack = (new Error()).stack; +} +CQLError.prototype = Object.create(Error.prototype); + // CQLModifier var CQLModifier = function () { this.name = null; @@ -209,7 +222,7 @@ var CQLParser = function () { CQLParser.prototype = { parse: function (query, scf, scr) { if (!query) - throw new Error("The query to be parsed cannot be empty"); + throw new CQLError("The query to be parsed cannot be empty", this.qs); this.scf = typeof scf != 'string' ? DEFAULT_SERVER_CHOICE_FIELD : scf; this.scr = typeof scr != 'string' @@ -220,11 +233,11 @@ CQLParser.prototype = { this._move(); this.tree = this._parseQuery(this.scf, this.scr, new Array()); if (this.look != "") - throw new Error("EOF expected"); + throw new CQLError("EOF expected", this.qs, this.qi, this.look, this.val, this.lval); }, parseFromFQ: function (query, scf, scr) { if (!query) - throw new Error("The query to be parsed cannot be empty"); + throw new CQLError("The query to be parsed cannot be empty", this.qs); if (typeof query == 'string') query = JSON.parse(query); this.scf = typeof scf != 'string' @@ -280,7 +293,7 @@ CQLParser.prototype = { } return node; } - throw new Error('Unknow node type; ' + JSON.stringify(fq)); + throw new CQLError('Unknow node type; ' + JSON.stringify(fq), this.qs, this.qi, this.look, this.val, this.lval); }, toXCQL: function (c) { c = typeof c == "undefined" ? ' ' : c; @@ -316,7 +329,7 @@ CQLParser.prototype = { while (this.look == "/") { this._move(); if (this.look != "s" && this.look != "q") - throw new Error("Invalid modifier.") + throw new CQLError("Invalid modifier.", this.qs, this.qi, this.look, this.val, this.lval) var name = this.lval; this._move(); @@ -325,7 +338,7 @@ CQLParser.prototype = { var rel = this.look; this._move(); if (this.look != "s" && this.look != "q") - throw new Error("Invalid relation within the modifier."); + throw new CQLError("Invalid relation within the modifier.", this.qs, this.qi, this.look, this.val, this.lval); var m = new CQLModifier(); m.name = name; @@ -350,7 +363,7 @@ CQLParser.prototype = { if (this.look == ")") this._move(); else - throw new Error("Missing closing parenthesis."); + throw new CQLError("Missing closing parenthesis.", this.qs, _qi, this.look, this.val, this.lval); return b; } else if (this.look == "s" || this.look == "q") { @@ -407,14 +420,14 @@ CQLParser.prototype = { } else if (this.look == ">") { this._move(); if (this.look != "s" && this.look != "q") - throw new Error("Expecting string or a quoted expression."); + throw new CQLError("Expecting string or a quoted expression.", this.qs, this.qi, this.look, this.val, this.lval); var first = this.lval; this._move(); if (this.look == "=") { this._move(); if (this.look != "s" && this.look != "q") - throw new Error("Expecting string or a quoted expression."); + throw new CQLError("Expecting string or a quoted expression.", this.qs, this.qi, this.look, this.val, this.lval); this._addPrefix(first, this.lval); this._move(); @@ -424,7 +437,7 @@ CQLParser.prototype = { return this._parseQuery(field, relation, modifiers); } } else { - throw new Error("Invalid search clause."); + throw new CQLError("Invalid search clause.", this.qs, this.qi, this.look, this.val, this.lval); } }, diff --git a/index.html b/index.html index 50aaef1..f0e8389 100644 --- a/index.html +++ b/index.html @@ -15,8 +15,13 @@ cp.parseFromFQ(fq); document.getElementById("output4").value = cp.toString(); } catch (e) { - document.getElementById("output").value = e.message; - document.getElementById("output2").value = e.message; + var msg = e.toString(); + msg += "\n\n\tquery: " + e.query + "\n\t " + "^".padStart(e.pos) + " (pos: " + e.pos + ")"; + msg += "\n\n\tcontext: " + JSON.stringify({"look": e.look, "val": e.val, "lval": e.lval}); + document.getElementById("output").value = msg; + document.getElementById("output2").value = ""; + document.getElementById("output3").value = ""; + document.getElementById("output4").value = ""; throw e; } } From b22a83f18490e88155dbc67d58258b3f675d03cb Mon Sep 17 00:00:00 2001 From: koernere Date: Tue, 9 Aug 2022 18:26:25 +0200 Subject: [PATCH 03/12] Add comment for module exports/import --- cql.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cql.js b/cql.js index 7f7cc5a..89cd432 100644 --- a/cql.js +++ b/cql.js @@ -513,3 +513,6 @@ CQLParser.prototype = { this.prefixes[name] = value; } } + +// for module import systems uncomment the following line +// module.exports = CQLParser; From 5d05ecfc17dea37548b0c7d0cf425f27977c681d Mon Sep 17 00:00:00 2001 From: koernere Date: Wed, 10 Aug 2022 11:37:55 +0200 Subject: [PATCH 04/12] Add ranges and demo output --- cql.js | 31 +++++++++++++++++++++++++++++++ index.html | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/cql.js b/cql.js index 89cd432..d3aac0d 100644 --- a/cql.js +++ b/cql.js @@ -27,6 +27,8 @@ var CQLModifier = function () { this.name = null; this.relation = null; this.value = null; + this._pos = null; + this._range = null; } CQLModifier.prototype = { @@ -52,6 +54,8 @@ CQLModifier.prototype = { //we assume 'true' var value = this.value.length > 0 ? this.value : "true"; var s = '"' + this.name + '": "' + value + '"'; + if (this._pos !== null) s += ', "' + this.name + '@pos": ' + this._pos; + if (this._range !== null) s += ', "' + this.name + '@range": ' + JSON.stringify(this._range); return s; } } @@ -67,6 +71,8 @@ var CQLSearchClause = function (field, fielduri, relation, relationuri, this.term = term; this.scf = scf || DEFAULT_SERVER_CHOICE_FIELD; this.scr = scr || DEFAULT_SERVER_CHOICE_RELATION; + this._pos = null; + this._range = null; } CQLSearchClause.prototype = { @@ -128,6 +134,8 @@ CQLSearchClause.prototype = { continue; s += ', ' + this.modifiers[i].toFQ(); } + if (this._pos !== null) s += ', "@pos": ' + this._pos; + if (this._range !== null) s += ', "@range": ' + JSON.stringify(this._range); s += '}'; return s; }, @@ -163,6 +171,7 @@ var CQLBoolean = function () { this.modifiers = null; this.left = null; this.right = null; + this._pos = null; } CQLBoolean.prototype = { @@ -199,6 +208,7 @@ CQLBoolean.prototype = { s += ', ' + this.modifiers[i].toFQ(); s += ',' + nl + indent(n, c) + ' "s1": ' + this.left.toFQ(n + 1, c, nl); s += ',' + nl + indent(n, c) + ' "s2": ' + this.right.toFQ(n + 1, c, nl); + if (this._pos !== null) s += ',' + nl + indent(n, c) + ' "@pos": ' + this._pos; var fill = n && c ? ' ' : ''; s += nl + indent(n - 1, c) + fill + '}'; return s; @@ -213,6 +223,7 @@ var CQLParser = function () { this.look = null; this.lval = null; this.val = null; + this._exprStart = null; this.prefixes = new Object(); this.tree = null; this.scf = null; @@ -230,6 +241,7 @@ CQLParser.prototype = { this.qs = query; this.ql = this.qs.length; this.qi = 0; + this.lval = this.val = this._exprStart = null; this._move(); this.tree = this._parseQuery(this.scf, this.scr, new Array()); if (this.look != "") @@ -285,6 +297,8 @@ CQLParser.prototype = { for (var key in fq) { if (key == 'term' || key == 'field' || key == 'relation') continue; + if (key.endsWith("@pos") || key.endsWith("@range")) + continue; var mod = new CQLModifier(); mod.name = key; mod.relation = '='; @@ -315,6 +329,7 @@ CQLParser.prototype = { this.lval == "not" || this.lval == "prox")) { var b = new CQLBoolean(); + b._pos = this.qi - this.lval.length + 1; b.op = this.lval; this._move(); b.modifiers = this._parseModifiers(); @@ -327,20 +342,26 @@ CQLParser.prototype = { _parseModifiers: function () { var ar = new Array(); while (this.look == "/") { + let _mstart = this.qi; this._move(); if (this.look != "s" && this.look != "q") throw new CQLError("Invalid modifier.", this.qs, this.qi, this.look, this.val, this.lval) var name = this.lval; + let _mpos = this.qi - this.lval.length + 1; + let _mend = this.qi; this._move(); if (this.look.length > 0 && this._strchr("<>=", this.look.charAt(0))) { var rel = this.look; this._move(); + _mend = this.qi; if (this.look != "s" && this.look != "q") throw new CQLError("Invalid relation within the modifier.", this.qs, this.qi, this.look, this.val, this.lval); var m = new CQLModifier(); + m._pos = _mpos; + m._range = [_mstart, _mend]; m.name = name; m.relation = rel; m.value = this.val; @@ -348,6 +369,8 @@ CQLParser.prototype = { this._move(); } else { var m = new CQLModifier(); + m._pos = _mpos; + m._range = [_mstart, _mend]; m.name = name; m.relation = ""; m.value = ""; @@ -358,6 +381,7 @@ CQLParser.prototype = { }, _parseSearchClause: function (field, relation, modifiers) { if (this.look == "(") { + let _qi = this.qi; this._move(); var b = this._parseQuery(field, relation, modifiers); if (this.look == ")") @@ -368,6 +392,9 @@ CQLParser.prototype = { return b; } else if (this.look == "s" || this.look == "q") { var first = this.val; // dont know if field or term yet + if (this._exprStart === null) + this._exprStart = this.qi - this.val.length + 1; + let _tend = this.qi; this._move(); if (this.look == "q" || (this.look == "s" && @@ -414,6 +441,8 @@ CQLParser.prototype = { first, this.scf, this.scr); + sc._range = [this._exprStart, _tend]; + this._exprStart = null; return sc; } // prefixes @@ -469,6 +498,8 @@ CQLParser.prototype = { } //quoted string } else if (this._strchr("\"'", c)) { + if (this._exprStart === null) + this._exprStart = this.qi + 1; this.look = "q"; //remember quote char var mark = c; diff --git a/index.html b/index.html index f0e8389..55e71ac 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,37 @@ + CQL parser @@ -73,16 +95,27 @@
Query: -
-
- XCQL: -
- - - - - +
+
XCQL
+ +
+
+
String
+ +
+
+
FQ
+ +
+
+
String (FQ-parsed)
+ +
+
+
Annotated
(original input)
+ +
From 908005a219f6142157e946ca916069d6608cdfaf Mon Sep 17 00:00:00 2001 From: koernere Date: Thu, 17 Nov 2022 17:18:03 +0100 Subject: [PATCH 08/12] Add position of relation --- cql.js | 7 ++++++- index.html | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cql.js b/cql.js index c6c8939..becad15 100644 --- a/cql.js +++ b/cql.js @@ -73,6 +73,7 @@ var CQLSearchClause = function (field, fielduri, relation, relationuri, this.scr = scr || DEFAULT_SERVER_CHOICE_RELATION; this._pos = null; this._range = null; + this._relpos = null; } CQLSearchClause.prototype = { @@ -136,6 +137,7 @@ CQLSearchClause.prototype = { } if (this._pos !== null) s += ', "@pos": ' + this._pos; if (this._range !== null) s += ', "@range": ' + JSON.stringify(this._range); + if (this._relpos !== null) s += ', "relation@pos": ' + this._relpos; s += '}'; return s; }, @@ -226,6 +228,7 @@ var CQLParser = function () { this.lval = null; this.val = null; this._exprStart = null; + this._exprRelStart = null; this.prefixes = new Object(); this.tree = null; this.scf = null; @@ -451,7 +454,8 @@ CQLParser.prototype = { this.scf, this.scr); sc._range = [this._exprStart, _tend]; - this._exprStart = null; + sc._relpos = this._exprRelStart; + this._exprStart = this._exprRelStart = null; return sc; } // prefixes @@ -499,6 +503,7 @@ CQLParser.prototype = { } else if (this._strchr("<>=", c)) { this.look = c; this.qi++; + this._exprRelStart = this.qi; //comparitors can repeat, could be if while (this.qi < this.ql && this._strchr("<>=", this.qs.charAt(this.qi))) { diff --git a/index.html b/index.html index b37f8e3..34eec2d 100644 --- a/index.html +++ b/index.html @@ -16,8 +16,13 @@ let range = obj["@range"]; s += '\n' + ("".padStart(range[0] - 1) + "t".padEnd(range[1] - range[0] + 1, "-")).padEnd(query.length + 1 + lvl) + "[term:" + range + "]" } + if ("relation@pos" in obj) { + let relpos = obj["relation@pos"]; + let relmodend = Math.max(...Object.entries(obj).filter(x => x[0].endsWith("@range") && x[0].length > 6).map(([_, range]) => range[1])); + s += '\n' + ("".padStart(relpos - 1) + "r".padEnd(relmodend - relpos + 1, "-")).padEnd(query.length + 1 + lvl + 1) + "[term:rel:" + obj["relation"] + ":" + relpos + "]" + } Object.entries(obj).filter(x => x[0].endsWith("@range") && x[0].length > 6).map(([_, range]) => { - s += '\n' + ("".padStart(range[0] - 1) + "m".padEnd(range[1] - range[0] + 1, "-")).padEnd(query.length + 1 + lvl + 1) + "[term:mod:" + range + "]" + s += '\n' + ("".padStart(range[0] - 1) + "m".padEnd(range[1] - range[0] + 1, "-")).padEnd(query.length + 1 + lvl + 2) + "[term:mod:" + range + "]" }) } else if ("op" in obj) { if ("@range" in obj) { From 9b8ab81e6c64580eefd65681bc2a0bd2884ffb19 Mon Sep 17 00:00:00 2001 From: koernere Date: Fri, 18 Nov 2022 12:10:16 +0100 Subject: [PATCH 09/12] formatting --- cql.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cql.js b/cql.js index becad15..0adcf8a 100644 --- a/cql.js +++ b/cql.js @@ -165,8 +165,8 @@ CQLSearchClause.prototype = { default: return rel; } } - } + // CQLBoolean var CQLBoolean = function () { this.op = null; @@ -184,6 +184,7 @@ CQLBoolean.prototype = { (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') + ' ' + (this.right.op ? '(' + this.right + ')' : this.right);; }, + toXCQL: function (n, c) { var s = indent(n, c) + "\n"; s = s + indent(n + 1, c) + "\n" + @@ -219,6 +220,7 @@ CQLBoolean.prototype = { } } + // CQLParser var CQLParser = function () { this.qi = null; From eb057498605b0bcbfa467febd8695cf376bd019a Mon Sep 17 00:00:00 2001 From: koernere Date: Fri, 18 Nov 2022 12:35:04 +0100 Subject: [PATCH 10/12] fix relation start position (if a relation operator also appears in a modifier) --- cql.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cql.js b/cql.js index 0adcf8a..948fe7d 100644 --- a/cql.js +++ b/cql.js @@ -352,6 +352,7 @@ CQLParser.prototype = { _parseModifiers: function () { var ar = new Array(); let _safeExprStart = this._exprStart; + let _safeExprRelStart = this._exprRelStart; while (this.look == "/") { let _mstart = this.qi; this._move(); @@ -391,6 +392,7 @@ CQLParser.prototype = { } } this._exprStart = _safeExprStart; + this._exprRelStart = _safeExprRelStart; return ar; }, _parseSearchClause: function (field, relation, modifiers) { From ea00edd5f6851eb13837174c060e80ffba21bffb Mon Sep 17 00:00:00 2001 From: koernere Date: Fri, 18 Nov 2022 12:39:54 +0100 Subject: [PATCH 11/12] Add relation start position for non-math symbols (e.g. strings) --- cql.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cql.js b/cql.js index 948fe7d..c5ee430 100644 --- a/cql.js +++ b/cql.js @@ -419,6 +419,7 @@ CQLParser.prototype = { this.lval != "not" && this.lval != "prox")) { var rel = this.val; // string relation + this._exprRelStart = this.qi - this.val.length + 1; this._move(); return this._parseSearchClause(first, rel, this._parseModifiers()); From c2037d81123f2b6a2c3d37665d4ae4a45aaa21e8 Mon Sep 17 00:00:00 2001 From: koernere Date: Fri, 18 Nov 2022 12:48:59 +0100 Subject: [PATCH 12/12] Add comments for state fields and object properties --- cql.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cql.js b/cql.js index c5ee430..77e05bb 100644 --- a/cql.js +++ b/cql.js @@ -13,10 +13,15 @@ function indent(n, c) { var CQLError = function (message, query, pos, look, val, lval) { this.name = "CQLError"; this.message = message; + // raw query string this.query = query; + // parser (error) position in raw query this.pos = pos; + // state: s | q | | ()/ | <>= | "' this.look = look; + // value of token (may be (quoted) content, "index"; field or term) this.val = val; + // lowercased this.val this.lval = lval; this.stack = (new Error()).stack; } @@ -24,8 +29,11 @@ CQLError.prototype = Object.create(Error.prototype); // CQLModifier var CQLModifier = function () { + // name of (relation) modifier this.name = null; + // relation of modifier value (e.g. equals, lower than, etc.) this.relation = null; + // optional value of modifier ("true" if empty) this.value = null; this._pos = null; this._range = null; @@ -63,16 +71,25 @@ CQLModifier.prototype = { // CQLSearchClause var CQLSearchClause = function (field, fielduri, relation, relationuri, modifiers, term, scf, scr) { + // index or name this.field = field; + // not supported yet? (always empty string) this.fielduri = fielduri; + // relation symbol or string this.relation = relation; + // not supported yet? (always empty string) this.relationuri = relationuri; + // list of modifiers this.modifiers = modifiers; + // value this.term = term; this.scf = scf || DEFAULT_SERVER_CHOICE_FIELD; this.scr = scr || DEFAULT_SERVER_CHOICE_RELATION; + // start position of search clause this._pos = null; + // range (start + end position) of search clause (without surrounding braces or spaces) this._range = null; + // start position of relation this._relpos = null; } @@ -169,9 +186,13 @@ CQLSearchClause.prototype = { // CQLBoolean var CQLBoolean = function () { + // operator: and | or | not | prox this.op = null; + // list of modifiers this.modifiers = null; + // left search clause this.left = null; + // right search clause this.right = null; this._pos = null; this._range = null; @@ -223,15 +244,25 @@ CQLBoolean.prototype = { // CQLParser var CQLParser = function () { + // index/position in raw query this.qi = null; + // raw query string length (char count) this.ql = null; + // raw query string this.qs = null; + // parser state: s | q | ()/ | <>= | "' + // if empty string then error this.look = null; + // lowercased this.val this.lval = null; + // value of token (may be (quoted) content, "index"; field or term) this.val = null; + // (internal) start position of expression this._exprStart = null; + // (internal) start position of relation (in expression) this._exprRelStart = null; this.prefixes = new Object(); + // parsed query this.tree = null; this.scf = null; this.scr = null;