JSONiqLexer.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Distributed under the BSD license:
  3. *
  4. * Copyright (c) 2010, Ajax.org B.V.
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of Ajax.org B.V. nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * ***** END LICENSE BLOCK ***** */
  30. define(function(require, exports, module){
  31. var JSONiqTokenizer = require("./JSONiqTokenizer").JSONiqTokenizer;
  32. var TokenHandler = function(code) {
  33. var input = code;
  34. this.tokens = [];
  35. this.reset = function(code) {
  36. input = input;
  37. this.tokens = [];
  38. };
  39. this.startNonterminal = function(name, begin) {};
  40. this.endNonterminal = function(name, end) {};
  41. this.terminal = function(name, begin, end) {
  42. this.tokens.push({
  43. name: name,
  44. value: input.substring(begin, end)
  45. });
  46. };
  47. this.whitespace = function(begin, end) {
  48. this.tokens.push({
  49. name: "WS",
  50. value: input.substring(begin, end)
  51. });
  52. };
  53. };
  54. var keys = "NaN|after|allowing|ancestor|ancestor-or-self|and|append|array|as|ascending|at|attribute|base-uri|before|boundary-space|break|by|case|cast|castable|catch|child|collation|comment|constraint|construction|contains|context|continue|copy|copy-namespaces|count|decimal-format|decimal-separator|declare|default|delete|descendant|descendant-or-self|descending|digit|div|document|document-node|element|else|empty|empty-sequence|encoding|end|eq|every|except|exit|external|false|first|following|following-sibling|for|from|ft-option|function|ge|greatest|group|grouping-separator|gt|idiv|if|import|in|index|infinity|insert|instance|integrity|intersect|into|is|item|json|json-item|jsoniq|last|lax|le|least|let|loop|lt|minus-sign|mod|modify|module|namespace|namespace-node|ne|next|node|nodes|not|null|object|of|only|option|or|order|ordered|ordering|paragraphs|parent|pattern-separator|per-mille|percent|preceding|preceding-sibling|previous|processing-instruction|rename|replace|return|returning|revalidation|satisfies|schema|schema-attribute|schema-element|score|select|self|sentences|sliding|some|stable|start|strict|switch|text|then|times|to|treat|true|try|tumbling|type|typeswitch|union|unordered|updating|validate|value|variable|version|when|where|while|window|with|words|xquery|zero-digit".split("|");
  55. var keywords = keys.map(
  56. function(val) { return { name: "'" + val + "'", token: "keyword" }; }
  57. );
  58. var ncnames = keys.map(
  59. function(val) { return { name: "'" + val + "'", token: "text", next: function(stack){ stack.pop(); } }; }
  60. );
  61. var cdata = "constant.language";
  62. var number = "constant";
  63. var xmlcomment = "comment";
  64. var pi = "xml-pe";
  65. var pragma = "constant.buildin";
  66. var Rules = {
  67. start: [
  68. { name: "'(#'", token: pragma, next: function(stack){ stack.push("Pragma"); } },
  69. { name: "'(:'", token: "comment", next: function(stack){ stack.push("Comment"); } },
  70. { name: "'(:~'", token: "comment.doc", next: function(stack){ stack.push("CommentDoc"); } },
  71. { name: "'<!--'", token: xmlcomment, next: function(stack){ stack.push("XMLComment"); } },
  72. { name: "'<?'", token: pi, next: function(stack) { stack.push("PI"); } },
  73. { name: "''''", token: "string", next: function(stack){ stack.push("AposString"); } },
  74. { name: "'\"'", token: "string", next: function(stack){ stack.push("QuotString"); } },
  75. { name: "Annotation", token: "support.function" },
  76. { name: "ModuleDecl", token: "keyword", next: function(stack){ stack.push("Prefix"); } },
  77. { name: "OptionDecl", token: "keyword", next: function(stack){ stack.push("_EQName"); } },
  78. { name: "AttrTest", token: "support.type" },
  79. { name: "Variable", token: "variable" },
  80. { name: "'<![CDATA['", token: cdata, next: function(stack){ stack.push("CData"); } },
  81. { name: "IntegerLiteral", token: number },
  82. { name: "DecimalLiteral", token: number },
  83. { name: "DoubleLiteral", token: number },
  84. { name: "Operator", token: "keyword.operator" },
  85. { name: "EQName", token: function(val) { return keys.indexOf(val) !== -1 ? "keyword" : "support.function"; } },
  86. { name: "'('", token:"lparen" },
  87. { name: "')'", token:"rparen" },
  88. { name: "Tag", token: "meta.tag", next: function(stack){ stack.push("StartTag"); } },
  89. { name: "'}'", token: "text", next: function(stack){ if(stack.length > 1) stack.pop(); } },
  90. { name: "'{'", token: "text", next: function(stack){ stack.push("start"); } } //, next: function(stack){ if(stack.length > 1) { stack.pop(); } } }
  91. ].concat(keywords),
  92. _EQName: [
  93. { name: "EQName", token: "text", next: function(stack) { stack.pop(); } }
  94. ].concat(ncnames),
  95. Prefix: [
  96. { name: "NCName", token: "text", next: function(stack) { stack.pop(); } }
  97. ].concat(ncnames),
  98. StartTag: [
  99. { name: "'>'", token: "meta.tag", next: function(stack){ stack.push("TagContent"); } },
  100. { name: "QName", token: "entity.other.attribute-name" },
  101. { name: "'='", token: "text" },
  102. { name: "''''", token: "string", next: function(stack){ stack.push("AposAttr"); } },
  103. { name: "'\"'", token: "string", next: function(stack){ stack.push("QuotAttr"); } },
  104. { name: "'/>'", token: "meta.tag.r", next: function(stack){ stack.pop(); } }
  105. ],
  106. TagContent: [
  107. { name: "ElementContentChar", token: "text" },
  108. { name: "'<![CDATA['", token: cdata, next: function(stack){ stack.push("CData"); } },
  109. { name: "'<!--'", token: xmlcomment, next: function(stack){ stack.push("XMLComment"); } },
  110. { name: "Tag", token: "meta.tag", next: function(stack){ stack.push("StartTag"); } },
  111. { name: "PredefinedEntityRef", token: "constant.language.escape" },
  112. { name: "CharRef", token: "constant.language.escape" },
  113. { name: "'{{'", token: "text" },
  114. { name: "'}}'", token: "text" },
  115. { name: "'{'", token: "text", next: function(stack){ stack.push("start"); } },
  116. { name: "EndTag", token: "meta.tag", next: function(stack){ stack.pop(); stack.pop(); } }
  117. ],
  118. AposAttr: [
  119. { name: "''''", token: "string", next: function(stack){ stack.pop(); } },
  120. { name: "EscapeApos", token: "constant.language.escape" },
  121. { name: "AposAttrContentChar", token: "string" },
  122. { name: "PredefinedEntityRef", token: "constant.language.escape" },
  123. { name: "CharRef", token: "constant.language.escape" },
  124. { name: "'{{'", token: "string" },
  125. { name: "'}}'", token: "string" },
  126. { name: "'{'", token: "text", next: function(stack){ stack.push("start"); } }
  127. ],
  128. QuotAttr: [
  129. { name: "'\"'", token: "string", next: function(stack){ stack.pop(); } },
  130. { name: "EscapeQuot", token: "constant.language.escape" },
  131. { name: "QuotAttrContentChar", token: "string" },
  132. { name: "PredefinedEntityRef", token: "constant.language.escape" },
  133. { name: "CharRef", token: "constant.language.escape" },
  134. { name: "'{{'", token: "string" },
  135. { name: "'}}'", token: "string" },
  136. { name: "'{'", token: "text", next: function(stack){ stack.push("start"); } }
  137. ],
  138. Pragma: [
  139. { name: "PragmaContents", token: pragma },
  140. { name: "'#'", token: pragma },
  141. { name: "'#)'", token: pragma, next: function(stack){ stack.pop(); } }
  142. ],
  143. Comment: [
  144. { name: "CommentContents", token: "comment" },
  145. { name: "'(:'", token: "comment", next: function(stack){ stack.push("Comment"); } },
  146. { name: "':)'", token: "comment", next: function(stack){ stack.pop(); } }
  147. ],
  148. CommentDoc: [
  149. { name: "DocCommentContents", token: "comment.doc" },
  150. { name: "DocTag", token: "comment.doc.tag" },
  151. { name: "'(:'", token: "comment.doc", next: function(stack){ stack.push("CommentDoc"); } },
  152. { name: "':)'", token: "comment.doc", next: function(stack){ stack.pop(); } }
  153. ],
  154. XMLComment: [
  155. { name: "DirCommentContents", token: xmlcomment },
  156. { name: "'-->'", token: xmlcomment, next: function(stack){ stack.pop(); } }
  157. ],
  158. CData: [
  159. { name: "CDataSectionContents", token: cdata },
  160. { name: "']]>'", token: cdata, next: function(stack){ stack.pop(); } }
  161. ],
  162. PI: [
  163. { name: "DirPIContents", token: pi },
  164. { name: "'?'", token: pi },
  165. { name: "'?>'", token: pi, next: function(stack){ stack.pop(); } }
  166. ],
  167. AposString: [
  168. { name: "''''", token: "string", next: function(stack){ stack.pop(); } },
  169. { name: "PredefinedEntityRef", token: "constant.language.escape" },
  170. { name: "CharRef", token: "constant.language.escape" },
  171. { name: "EscapeApos", token: "constant.language.escape" },
  172. { name: "AposChar", token: "string" }
  173. ],
  174. QuotString: [
  175. { name: "'\"'", token: "string", next: function(stack){ stack.pop(); } },
  176. { name: "PredefinedEntityRef", token: "constant.language.escape" },
  177. { name: "CharRef", token: "constant.language.escape" },
  178. { name: "EscapeQuot", token: "constant.language.escape" },
  179. { name: "QuotChar", token: "string" }
  180. ]
  181. };
  182. exports.JSONiqLexer = function() {
  183. this.tokens = [];
  184. this.getLineTokens = function(line, state, row) {
  185. state = (state === "start" || !state) ? '["start"]' : state;
  186. var stack = JSON.parse(state);
  187. var h = new TokenHandler(line);
  188. var tokenizer = new JSONiqTokenizer(line, h);
  189. var tokens = [];
  190. while(true) {
  191. var currentState = stack[stack.length - 1];
  192. try {
  193. h.tokens = [];
  194. tokenizer["parse_" + currentState]();
  195. var info = null;
  196. if(h.tokens.length > 1 && h.tokens[0].name === "WS") {
  197. tokens.push({
  198. type: "text",
  199. value: h.tokens[0].value
  200. });
  201. h.tokens.splice(0, 1);
  202. }
  203. var token = h.tokens[0];
  204. var rules = Rules[currentState];
  205. for(var k = 0; k < rules.length; k++) {
  206. var rule = Rules[currentState][k];
  207. if((typeof(rule.name) === "function" && rule.name(token)) || rule.name === token.name) {
  208. info = rule;
  209. break;
  210. }
  211. }
  212. if(token.name === "EOF") { break; }
  213. if(token.value === "") { throw "Encountered empty string lexical rule."; }
  214. tokens.push({
  215. type: info === null ? "text" : (typeof(info.token) === "function" ? info.token(token.value) : info.token),
  216. value: token.value
  217. });
  218. if(info && info.next) {
  219. info.next(stack);
  220. }
  221. } catch(e) {
  222. if(e instanceof tokenizer.ParseException) {
  223. var index = 0;
  224. for(var i=0; i < tokens.length; i++) {
  225. index += tokens[i].value.length;
  226. }
  227. tokens.push({ type: "text", value: line.substring(index) });
  228. return {
  229. tokens: tokens,
  230. state: JSON.stringify(["start"])
  231. };
  232. } else {
  233. throw e;
  234. }
  235. }
  236. }
  237. if(this.tokens[row] !== undefined) {
  238. var cachedLine = this.lines[row];
  239. var begin = sharedStart([line, cachedLine]);
  240. var diff = cachedLine.length - line.length;
  241. var idx = 0;
  242. var col = 0;
  243. for(var i = 0; i < tokens.length; i++) {
  244. var token = tokens[i];
  245. for(var j = 0; j < this.tokens[row].length; j++) {
  246. var semanticToken = this.tokens[row][j];
  247. if(
  248. ((col + token.value.length) <= begin.length && semanticToken.sc === col && semanticToken.ec === (col + token.value.length)) ||
  249. (semanticToken.sc === (col + diff) && semanticToken.ec === (col + token.value.length + diff))
  250. ) {
  251. idx = i;
  252. tokens[i].type = semanticToken.type;
  253. }
  254. }
  255. col += token.value.length;
  256. }
  257. }
  258. return {
  259. tokens: tokens,
  260. state: JSON.stringify(stack)
  261. };
  262. };
  263. function sharedStart(A) {
  264. var tem1, tem2, s, A = A.slice(0).sort();
  265. tem1 = A[0];
  266. s = tem1.length;
  267. tem2 = A.pop();
  268. while(s && tem2.indexOf(tem1) == -1) {
  269. tem1 = tem1.substring(0, --s);
  270. }
  271. return tem1;
  272. }
  273. };
  274. });