haxe.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: http://codemirror.net/LICENSE
  3. (function(mod) {
  4. if (typeof exports == "object" && typeof module == "object") // CommonJS
  5. mod(require("../../lib/codemirror"));
  6. else if (typeof define == "function" && define.amd) // AMD
  7. define(["../../lib/codemirror"], mod);
  8. else // Plain browser env
  9. mod(CodeMirror);
  10. })(function(CodeMirror) {
  11. "use strict";
  12. CodeMirror.defineMode("haxe", function(config, parserConfig) {
  13. var indentUnit = config.indentUnit;
  14. // Tokenizer
  15. var keywords = function(){
  16. function kw(type) {return {type: type, style: "keyword"};}
  17. var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
  18. var operator = kw("operator"), atom = {type: "atom", style: "atom"}, attribute = {type:"attribute", style: "attribute"};
  19. var type = kw("typedef");
  20. return {
  21. "if": A, "while": A, "else": B, "do": B, "try": B,
  22. "return": C, "break": C, "continue": C, "new": C, "throw": C,
  23. "var": kw("var"), "inline":attribute, "static": attribute, "using":kw("import"),
  24. "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"),
  25. "function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"),
  26. "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
  27. "in": operator, "never": kw("property_access"), "trace":kw("trace"),
  28. "class": type, "abstract":type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type,
  29. "true": atom, "false": atom, "null": atom
  30. };
  31. }();
  32. var isOperatorChar = /[+\-*&%=<>!?|]/;
  33. function chain(stream, state, f) {
  34. state.tokenize = f;
  35. return f(stream, state);
  36. }
  37. function nextUntilUnescaped(stream, end) {
  38. var escaped = false, next;
  39. while ((next = stream.next()) != null) {
  40. if (next == end && !escaped)
  41. return false;
  42. escaped = !escaped && next == "\\";
  43. }
  44. return escaped;
  45. }
  46. // Used as scratch variables to communicate multiple values without
  47. // consing up tons of objects.
  48. var type, content;
  49. function ret(tp, style, cont) {
  50. type = tp; content = cont;
  51. return style;
  52. }
  53. function haxeTokenBase(stream, state) {
  54. var ch = stream.next();
  55. if (ch == '"' || ch == "'")
  56. return chain(stream, state, haxeTokenString(ch));
  57. else if (/[\[\]{}\(\),;\:\.]/.test(ch))
  58. return ret(ch);
  59. else if (ch == "0" && stream.eat(/x/i)) {
  60. stream.eatWhile(/[\da-f]/i);
  61. return ret("number", "number");
  62. }
  63. else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
  64. stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
  65. return ret("number", "number");
  66. }
  67. else if (state.reAllowed && (ch == "~" && stream.eat(/\//))) {
  68. nextUntilUnescaped(stream, "/");
  69. stream.eatWhile(/[gimsu]/);
  70. return ret("regexp", "string-2");
  71. }
  72. else if (ch == "/") {
  73. if (stream.eat("*")) {
  74. return chain(stream, state, haxeTokenComment);
  75. }
  76. else if (stream.eat("/")) {
  77. stream.skipToEnd();
  78. return ret("comment", "comment");
  79. }
  80. else {
  81. stream.eatWhile(isOperatorChar);
  82. return ret("operator", null, stream.current());
  83. }
  84. }
  85. else if (ch == "#") {
  86. stream.skipToEnd();
  87. return ret("conditional", "meta");
  88. }
  89. else if (ch == "@") {
  90. stream.eat(/:/);
  91. stream.eatWhile(/[\w_]/);
  92. return ret ("metadata", "meta");
  93. }
  94. else if (isOperatorChar.test(ch)) {
  95. stream.eatWhile(isOperatorChar);
  96. return ret("operator", null, stream.current());
  97. }
  98. else {
  99. var word;
  100. if(/[A-Z]/.test(ch))
  101. {
  102. stream.eatWhile(/[\w_<>]/);
  103. word = stream.current();
  104. return ret("type", "variable-3", word);
  105. }
  106. else
  107. {
  108. stream.eatWhile(/[\w_]/);
  109. var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
  110. return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
  111. ret("variable", "variable", word);
  112. }
  113. }
  114. }
  115. function haxeTokenString(quote) {
  116. return function(stream, state) {
  117. if (!nextUntilUnescaped(stream, quote))
  118. state.tokenize = haxeTokenBase;
  119. return ret("string", "string");
  120. };
  121. }
  122. function haxeTokenComment(stream, state) {
  123. var maybeEnd = false, ch;
  124. while (ch = stream.next()) {
  125. if (ch == "/" && maybeEnd) {
  126. state.tokenize = haxeTokenBase;
  127. break;
  128. }
  129. maybeEnd = (ch == "*");
  130. }
  131. return ret("comment", "comment");
  132. }
  133. // Parser
  134. var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
  135. function HaxeLexical(indented, column, type, align, prev, info) {
  136. this.indented = indented;
  137. this.column = column;
  138. this.type = type;
  139. this.prev = prev;
  140. this.info = info;
  141. if (align != null) this.align = align;
  142. }
  143. function inScope(state, varname) {
  144. for (var v = state.localVars; v; v = v.next)
  145. if (v.name == varname) return true;
  146. }
  147. function parseHaxe(state, style, type, content, stream) {
  148. var cc = state.cc;
  149. // Communicate our context to the combinators.
  150. // (Less wasteful than consing up a hundred closures on every call.)
  151. cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
  152. if (!state.lexical.hasOwnProperty("align"))
  153. state.lexical.align = true;
  154. while(true) {
  155. var combinator = cc.length ? cc.pop() : statement;
  156. if (combinator(type, content)) {
  157. while(cc.length && cc[cc.length - 1].lex)
  158. cc.pop()();
  159. if (cx.marked) return cx.marked;
  160. if (type == "variable" && inScope(state, content)) return "variable-2";
  161. if (type == "variable" && imported(state, content)) return "variable-3";
  162. return style;
  163. }
  164. }
  165. }
  166. function imported(state, typename)
  167. {
  168. if (/[a-z]/.test(typename.charAt(0)))
  169. return false;
  170. var len = state.importedtypes.length;
  171. for (var i = 0; i<len; i++)
  172. if(state.importedtypes[i]==typename) return true;
  173. }
  174. function registerimport(importname) {
  175. var state = cx.state;
  176. for (var t = state.importedtypes; t; t = t.next)
  177. if(t.name == importname) return;
  178. state.importedtypes = { name: importname, next: state.importedtypes };
  179. }
  180. // Combinator utils
  181. var cx = {state: null, column: null, marked: null, cc: null};
  182. function pass() {
  183. for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
  184. }
  185. function cont() {
  186. pass.apply(null, arguments);
  187. return true;
  188. }
  189. function register(varname) {
  190. var state = cx.state;
  191. if (state.context) {
  192. cx.marked = "def";
  193. for (var v = state.localVars; v; v = v.next)
  194. if (v.name == varname) return;
  195. state.localVars = {name: varname, next: state.localVars};
  196. }
  197. }
  198. // Combinators
  199. var defaultVars = {name: "this", next: null};
  200. function pushcontext() {
  201. if (!cx.state.context) cx.state.localVars = defaultVars;
  202. cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
  203. }
  204. function popcontext() {
  205. cx.state.localVars = cx.state.context.vars;
  206. cx.state.context = cx.state.context.prev;
  207. }
  208. function pushlex(type, info) {
  209. var result = function() {
  210. var state = cx.state;
  211. state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
  212. };
  213. result.lex = true;
  214. return result;
  215. }
  216. function poplex() {
  217. var state = cx.state;
  218. if (state.lexical.prev) {
  219. if (state.lexical.type == ")")
  220. state.indented = state.lexical.indented;
  221. state.lexical = state.lexical.prev;
  222. }
  223. }
  224. poplex.lex = true;
  225. function expect(wanted) {
  226. function f(type) {
  227. if (type == wanted) return cont();
  228. else if (wanted == ";") return pass();
  229. else return cont(f);
  230. };
  231. return f;
  232. }
  233. function statement(type) {
  234. if (type == "@") return cont(metadef);
  235. if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
  236. if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
  237. if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
  238. if (type == "{") return cont(pushlex("}"), pushcontext, block, poplex, popcontext);
  239. if (type == ";") return cont();
  240. if (type == "attribute") return cont(maybeattribute);
  241. if (type == "function") return cont(functiondef);
  242. if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
  243. poplex, statement, poplex);
  244. if (type == "variable") return cont(pushlex("stat"), maybelabel);
  245. if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
  246. block, poplex, poplex);
  247. if (type == "case") return cont(expression, expect(":"));
  248. if (type == "default") return cont(expect(":"));
  249. if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
  250. statement, poplex, popcontext);
  251. if (type == "import") return cont(importdef, expect(";"));
  252. if (type == "typedef") return cont(typedef);
  253. return pass(pushlex("stat"), expression, expect(";"), poplex);
  254. }
  255. function expression(type) {
  256. if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
  257. if (type == "function") return cont(functiondef);
  258. if (type == "keyword c") return cont(maybeexpression);
  259. if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
  260. if (type == "operator") return cont(expression);
  261. if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
  262. if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
  263. return cont();
  264. }
  265. function maybeexpression(type) {
  266. if (type.match(/[;\}\)\],]/)) return pass();
  267. return pass(expression);
  268. }
  269. function maybeoperator(type, value) {
  270. if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
  271. if (type == "operator" || type == ":") return cont(expression);
  272. if (type == ";") return;
  273. if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
  274. if (type == ".") return cont(property, maybeoperator);
  275. if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
  276. }
  277. function maybeattribute(type) {
  278. if (type == "attribute") return cont(maybeattribute);
  279. if (type == "function") return cont(functiondef);
  280. if (type == "var") return cont(vardef1);
  281. }
  282. function metadef(type) {
  283. if(type == ":") return cont(metadef);
  284. if(type == "variable") return cont(metadef);
  285. if(type == "(") return cont(pushlex(")"), commasep(metaargs, ")"), poplex, statement);
  286. }
  287. function metaargs(type) {
  288. if(type == "variable") return cont();
  289. }
  290. function importdef (type, value) {
  291. if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
  292. else if(type == "variable" || type == "property" || type == "." || value == "*") return cont(importdef);
  293. }
  294. function typedef (type, value)
  295. {
  296. if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
  297. else if (type == "type" && /[A-Z]/.test(value.charAt(0))) { return cont(); }
  298. }
  299. function maybelabel(type) {
  300. if (type == ":") return cont(poplex, statement);
  301. return pass(maybeoperator, expect(";"), poplex);
  302. }
  303. function property(type) {
  304. if (type == "variable") {cx.marked = "property"; return cont();}
  305. }
  306. function objprop(type) {
  307. if (type == "variable") cx.marked = "property";
  308. if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
  309. }
  310. function commasep(what, end) {
  311. function proceed(type) {
  312. if (type == ",") return cont(what, proceed);
  313. if (type == end) return cont();
  314. return cont(expect(end));
  315. }
  316. return function(type) {
  317. if (type == end) return cont();
  318. else return pass(what, proceed);
  319. };
  320. }
  321. function block(type) {
  322. if (type == "}") return cont();
  323. return pass(statement, block);
  324. }
  325. function vardef1(type, value) {
  326. if (type == "variable"){register(value); return cont(typeuse, vardef2);}
  327. return cont();
  328. }
  329. function vardef2(type, value) {
  330. if (value == "=") return cont(expression, vardef2);
  331. if (type == ",") return cont(vardef1);
  332. }
  333. function forspec1(type, value) {
  334. if (type == "variable") {
  335. register(value);
  336. }
  337. return cont(pushlex(")"), pushcontext, forin, expression, poplex, statement, popcontext);
  338. }
  339. function forin(_type, value) {
  340. if (value == "in") return cont();
  341. }
  342. function functiondef(type, value) {
  343. if (type == "variable") {register(value); return cont(functiondef);}
  344. if (value == "new") return cont(functiondef);
  345. if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, typeuse, statement, popcontext);
  346. }
  347. function typeuse(type) {
  348. if(type == ":") return cont(typestring);
  349. }
  350. function typestring(type) {
  351. if(type == "type") return cont();
  352. if(type == "variable") return cont();
  353. if(type == "{") return cont(pushlex("}"), commasep(typeprop, "}"), poplex);
  354. }
  355. function typeprop(type) {
  356. if(type == "variable") return cont(typeuse);
  357. }
  358. function funarg(type, value) {
  359. if (type == "variable") {register(value); return cont(typeuse);}
  360. }
  361. // Interface
  362. return {
  363. startState: function(basecolumn) {
  364. var defaulttypes = ["Int", "Float", "String", "Void", "Std", "Bool", "Dynamic", "Array"];
  365. return {
  366. tokenize: haxeTokenBase,
  367. reAllowed: true,
  368. kwAllowed: true,
  369. cc: [],
  370. lexical: new HaxeLexical((basecolumn || 0) - indentUnit, 0, "block", false),
  371. localVars: parserConfig.localVars,
  372. importedtypes: defaulttypes,
  373. context: parserConfig.localVars && {vars: parserConfig.localVars},
  374. indented: 0
  375. };
  376. },
  377. token: function(stream, state) {
  378. if (stream.sol()) {
  379. if (!state.lexical.hasOwnProperty("align"))
  380. state.lexical.align = false;
  381. state.indented = stream.indentation();
  382. }
  383. if (stream.eatSpace()) return null;
  384. var style = state.tokenize(stream, state);
  385. if (type == "comment") return style;
  386. state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
  387. state.kwAllowed = type != '.';
  388. return parseHaxe(state, style, type, content, stream);
  389. },
  390. indent: function(state, textAfter) {
  391. if (state.tokenize != haxeTokenBase) return 0;
  392. var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
  393. if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
  394. var type = lexical.type, closing = firstChar == type;
  395. if (type == "vardef") return lexical.indented + 4;
  396. else if (type == "form" && firstChar == "{") return lexical.indented;
  397. else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
  398. else if (lexical.info == "switch" && !closing)
  399. return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
  400. else if (lexical.align) return lexical.column + (closing ? 0 : 1);
  401. else return lexical.indented + (closing ? 0 : indentUnit);
  402. },
  403. electricChars: "{}",
  404. blockCommentStart: "/*",
  405. blockCommentEnd: "*/",
  406. lineComment: "//"
  407. };
  408. });
  409. CodeMirror.defineMIME("text/x-haxe", "haxe");
  410. CodeMirror.defineMode("hxml", function () {
  411. return {
  412. startState: function () {
  413. return {
  414. define: false,
  415. inString: false
  416. };
  417. },
  418. token: function (stream, state) {
  419. var ch = stream.peek();
  420. var sol = stream.sol();
  421. ///* comments */
  422. if (ch == "#") {
  423. stream.skipToEnd();
  424. return "comment";
  425. }
  426. if (sol && ch == "-") {
  427. var style = "variable-2";
  428. stream.eat(/-/);
  429. if (stream.peek() == "-") {
  430. stream.eat(/-/);
  431. style = "keyword a";
  432. }
  433. if (stream.peek() == "D") {
  434. stream.eat(/[D]/);
  435. style = "keyword c";
  436. state.define = true;
  437. }
  438. stream.eatWhile(/[A-Z]/i);
  439. return style;
  440. }
  441. var ch = stream.peek();
  442. if (state.inString == false && ch == "'") {
  443. state.inString = true;
  444. ch = stream.next();
  445. }
  446. if (state.inString == true) {
  447. if (stream.skipTo("'")) {
  448. } else {
  449. stream.skipToEnd();
  450. }
  451. if (stream.peek() == "'") {
  452. stream.next();
  453. state.inString = false;
  454. }
  455. return "string";
  456. }
  457. stream.next();
  458. return null;
  459. },
  460. lineComment: "#"
  461. };
  462. });
  463. CodeMirror.defineMIME("text/x-hxml", "hxml");
  464. });