haxe.js 21 KB

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