verilog.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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('verilog', function (config, parserConfig) {
  15. var indentUnit = config.indentUnit,
  16. statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
  17. dontAlignCalls = parserConfig.dontAlignCalls,
  18. // compilerDirectivesUseRegularIndentation - If set, Compiler directive
  19. // indentation follows the same rules as everything else. Otherwise if
  20. // false, compiler directives will track their own indentation.
  21. // For example, `ifdef nested inside another `ifndef will be indented,
  22. // but a `ifdef inside a function block may not be indented.
  23. compilerDirectivesUseRegularIndentation = parserConfig.compilerDirectivesUseRegularIndentation,
  24. noIndentKeywords = parserConfig.noIndentKeywords || [],
  25. multiLineStrings = parserConfig.multiLineStrings,
  26. hooks = parserConfig.hooks || {}
  27. function words(str) {
  28. var obj = {},
  29. words = str.split(' ')
  30. for (var i = 0; i < words.length; ++i) obj[words[i]] = true
  31. return obj
  32. }
  33. /**
  34. * Keywords from IEEE 1800-2012
  35. */
  36. var keywords = words(
  37. 'accept_on alias always always_comb always_ff always_latch and assert assign assume automatic before begin bind ' +
  38. 'bins binsof bit break buf bufif0 bufif1 byte case casex casez cell chandle checker class clocking cmos config ' +
  39. 'const constraint context continue cover covergroup coverpoint cross deassign default defparam design disable ' +
  40. 'dist do edge else end endcase endchecker endclass endclocking endconfig endfunction endgenerate endgroup ' +
  41. 'endinterface endmodule endpackage endprimitive endprogram endproperty endspecify endsequence endtable endtask ' +
  42. 'enum event eventually expect export extends extern final first_match for force foreach forever fork forkjoin ' +
  43. 'function generate genvar global highz0 highz1 if iff ifnone ignore_bins illegal_bins implements implies import ' +
  44. 'incdir include initial inout input inside instance int integer interconnect interface intersect join join_any ' +
  45. 'join_none large let liblist library local localparam logic longint macromodule matches medium modport module ' +
  46. 'nand negedge nettype new nexttime nmos nor noshowcancelled not notif0 notif1 null or output package packed ' +
  47. 'parameter pmos posedge primitive priority program property protected pull0 pull1 pulldown pullup ' +
  48. 'pulsestyle_ondetect pulsestyle_onevent pure rand randc randcase randsequence rcmos real realtime ref reg ' +
  49. 'reject_on release repeat restrict return rnmos rpmos rtran rtranif0 rtranif1 s_always s_eventually s_nexttime ' +
  50. 's_until s_until_with scalared sequence shortint shortreal showcancelled signed small soft solve specify ' +
  51. 'specparam static string strong strong0 strong1 struct super supply0 supply1 sync_accept_on sync_reject_on ' +
  52. 'table tagged task this throughout time timeprecision timeunit tran tranif0 tranif1 tri tri0 tri1 triand trior ' +
  53. 'trireg type typedef union unique unique0 unsigned until until_with untyped use uwire var vectored virtual void ' +
  54. 'wait wait_order wand weak weak0 weak1 while wildcard wire with within wor xnor xor'
  55. )
  56. /** Operators from IEEE 1800-2012
  57. unary_operator ::=
  58. + | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
  59. binary_operator ::=
  60. + | - | * | / | % | == | != | === | !== | ==? | !=? | && | || | **
  61. | < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<<
  62. | -> | <->
  63. inc_or_dec_operator ::= ++ | --
  64. unary_module_path_operator ::=
  65. ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
  66. binary_module_path_operator ::=
  67. == | != | && | || | & | | | ^ | ^~ | ~^
  68. */
  69. var isOperatorChar = /[\+\-\*\/!~&|^%=?:<>]/
  70. var isBracketChar = /[\[\]{}()]/
  71. var unsignedNumber = /\d[0-9_]*/
  72. var decimalLiteral = /\d*\s*'s?d\s*\d[0-9_]*/i
  73. var binaryLiteral = /\d*\s*'s?b\s*[xz01][xz01_]*/i
  74. var octLiteral = /\d*\s*'s?o\s*[xz0-7][xz0-7_]*/i
  75. var hexLiteral = /\d*\s*'s?h\s*[0-9a-fxz?][0-9a-fxz?_]*/i
  76. var realLiteral = /(\d[\d_]*(\.\d[\d_]*)?E-?[\d_]+)|(\d[\d_]*\.\d[\d_]*)/i
  77. var closingBracketOrWord = /^((`?\w+)|[)}\]])/
  78. var closingBracket = /[)}\]]/
  79. var compilerDirectiveRegex = new RegExp(
  80. '^(`(?:ifdef|ifndef|elsif|else|endif|undef|undefineall|define|include|begin_keywords|celldefine|default|' +
  81. 'nettype|end_keywords|endcelldefine|line|nounconnected_drive|pragma|resetall|timescale|unconnected_drive))\\b'
  82. )
  83. var compilerDirectiveBeginRegex = /^(`(?:ifdef|ifndef|elsif|else))\b/
  84. var compilerDirectiveEndRegex = /^(`(?:elsif|else|endif))\b/
  85. var curPunc
  86. var curKeyword
  87. // Block openings which are closed by a matching keyword in the form of ("end" + keyword)
  88. // E.g. "task" => "endtask"
  89. var blockKeywords = words('case checker class clocking config function generate interface module package ' + 'primitive program property specify sequence table task')
  90. // Opening/closing pairs
  91. var openClose = {}
  92. for (var keyword in blockKeywords) {
  93. openClose[keyword] = 'end' + keyword
  94. }
  95. openClose['begin'] = 'end'
  96. openClose['casex'] = 'endcase'
  97. openClose['casez'] = 'endcase'
  98. openClose['do'] = 'while'
  99. openClose['fork'] = 'join;join_any;join_none'
  100. openClose['covergroup'] = 'endgroup'
  101. openClose['macro_begin'] = 'macro_end'
  102. for (var i in noIndentKeywords) {
  103. var keyword = noIndentKeywords[i]
  104. if (openClose[keyword]) {
  105. openClose[keyword] = undefined
  106. }
  107. }
  108. // Keywords which open statements that are ended with a semi-colon
  109. var statementKeywords = words('always always_comb always_ff always_latch assert assign assume else export for foreach forever if import initial repeat while extern typedef')
  110. function tokenBase(stream, state) {
  111. var ch = stream.peek(),
  112. style
  113. if (hooks[ch] && (style = hooks[ch](stream, state)) != false) return style
  114. if (hooks.tokenBase && (style = hooks.tokenBase(stream, state)) != false) return style
  115. if (/[,;:\.]/.test(ch)) {
  116. curPunc = stream.next()
  117. return null
  118. }
  119. if (isBracketChar.test(ch)) {
  120. curPunc = stream.next()
  121. return 'bracket'
  122. }
  123. // Macros (tick-defines)
  124. if (ch == '`') {
  125. stream.next()
  126. if (stream.eatWhile(/[\w\$_]/)) {
  127. var cur = stream.current()
  128. curKeyword = cur
  129. // Macros that end in _begin, are start of block and end with _end
  130. if (cur.startsWith('`uvm_') && cur.endsWith('_begin')) {
  131. var keywordClose = curKeyword.substr(0, curKeyword.length - 5) + 'end'
  132. openClose[cur] = keywordClose
  133. curPunc = 'newblock'
  134. } else {
  135. stream.eatSpace()
  136. if (stream.peek() == '(') {
  137. // Check if this is a block
  138. curPunc = 'newmacro'
  139. }
  140. var withSpace = stream.current()
  141. // Move the stream back before the spaces
  142. stream.backUp(withSpace.length - cur.length)
  143. }
  144. return 'def'
  145. } else {
  146. return null
  147. }
  148. }
  149. // System calls
  150. if (ch == '$') {
  151. stream.next()
  152. if (stream.eatWhile(/[\w\$_]/)) {
  153. return 'meta'
  154. } else {
  155. return null
  156. }
  157. }
  158. // Time literals
  159. if (ch == '#') {
  160. stream.next()
  161. stream.eatWhile(/[\d_.]/)
  162. return 'def'
  163. }
  164. // Event
  165. if (ch == '@') {
  166. stream.next()
  167. stream.eatWhile(/[@]/)
  168. return 'def'
  169. }
  170. // Strings
  171. if (ch == '"') {
  172. stream.next()
  173. state.tokenize = tokenString(ch)
  174. return state.tokenize(stream, state)
  175. }
  176. // Comments
  177. if (ch == '/') {
  178. stream.next()
  179. if (stream.eat('*')) {
  180. state.tokenize = tokenComment
  181. return tokenComment(stream, state)
  182. }
  183. if (stream.eat('/')) {
  184. stream.skipToEnd()
  185. return 'comment'
  186. }
  187. stream.backUp(1)
  188. }
  189. // Numeric literals
  190. if (
  191. stream.match(realLiteral) ||
  192. stream.match(decimalLiteral) ||
  193. stream.match(binaryLiteral) ||
  194. stream.match(octLiteral) ||
  195. stream.match(hexLiteral) ||
  196. stream.match(unsignedNumber) ||
  197. stream.match(realLiteral)
  198. ) {
  199. return 'number'
  200. }
  201. // Operators
  202. if (stream.eatWhile(isOperatorChar)) {
  203. curPunc = stream.current()
  204. return 'meta'
  205. }
  206. // Keywords / plain variables
  207. if (stream.eatWhile(/[\w\$_]/)) {
  208. var cur = stream.current()
  209. if (keywords[cur]) {
  210. if (openClose[cur]) {
  211. curPunc = 'newblock'
  212. if (cur === 'fork') {
  213. // Fork can be a statement instead of block in cases of:
  214. // "disable fork;" and "wait fork;" (trailing semicolon)
  215. stream.eatSpace()
  216. if (stream.peek() == ';') {
  217. curPunc = 'newstatement'
  218. }
  219. stream.backUp(stream.current().length - cur.length)
  220. }
  221. }
  222. if (statementKeywords[cur]) {
  223. curPunc = 'newstatement'
  224. }
  225. curKeyword = cur
  226. return 'keyword'
  227. }
  228. return 'variable'
  229. }
  230. stream.next()
  231. return null
  232. }
  233. function tokenString(quote) {
  234. return function (stream, state) {
  235. var escaped = false,
  236. next,
  237. end = false
  238. while ((next = stream.next()) != null) {
  239. if (next == quote && !escaped) {
  240. end = true
  241. break
  242. }
  243. escaped = !escaped && next == '\\'
  244. }
  245. if (end || !(escaped || multiLineStrings)) state.tokenize = tokenBase
  246. return 'string'
  247. }
  248. }
  249. function tokenComment(stream, state) {
  250. var maybeEnd = false,
  251. ch
  252. while ((ch = stream.next())) {
  253. if (ch == '/' && maybeEnd) {
  254. state.tokenize = tokenBase
  255. break
  256. }
  257. maybeEnd = ch == '*'
  258. }
  259. return 'comment'
  260. }
  261. function Context(indented, column, type, scopekind, align, prev) {
  262. this.indented = indented
  263. this.column = column
  264. this.type = type
  265. this.scopekind = scopekind
  266. this.align = align
  267. this.prev = prev
  268. }
  269. function pushContext(state, col, type, scopekind) {
  270. var indent = state.indented
  271. var c = new Context(indent, col, type, scopekind ? scopekind : '', null, state.context)
  272. return (state.context = c)
  273. }
  274. function popContext(state) {
  275. var t = state.context.type
  276. if (t == ')' || t == ']' || t == '}') {
  277. state.indented = state.context.indented
  278. }
  279. return (state.context = state.context.prev)
  280. }
  281. function isClosing(text, contextClosing) {
  282. if (text == contextClosing) {
  283. return true
  284. } else {
  285. // contextClosing may be multiple keywords separated by ;
  286. var closingKeywords = contextClosing.split(';')
  287. for (var i in closingKeywords) {
  288. if (text == closingKeywords[i]) {
  289. return true
  290. }
  291. }
  292. return false
  293. }
  294. }
  295. function isInsideScopeKind(ctx, scopekind) {
  296. if (ctx == null) {
  297. return false
  298. }
  299. if (ctx.scopekind === scopekind) {
  300. return true
  301. }
  302. return isInsideScopeKind(ctx.prev, scopekind)
  303. }
  304. function buildElectricInputRegEx() {
  305. // Reindentation should occur on any bracket char: {}()[]
  306. // or on a match of any of the block closing keywords, at
  307. // the end of a line
  308. var allClosings = []
  309. for (var i in openClose) {
  310. if (openClose[i]) {
  311. var closings = openClose[i].split(';')
  312. for (var j in closings) {
  313. allClosings.push(closings[j])
  314. }
  315. }
  316. }
  317. var re = new RegExp('[{}()\\[\\]]|(' + allClosings.join('|') + ')$')
  318. return re
  319. }
  320. // Interface
  321. return {
  322. // Regex to force current line to reindent
  323. electricInput: buildElectricInputRegEx(),
  324. startState: function (basecolumn) {
  325. var state = {
  326. tokenize: null,
  327. context: new Context((basecolumn || 0) - indentUnit, 0, 'top', 'top', false),
  328. indented: 0,
  329. compilerDirectiveIndented: 0,
  330. startOfLine: true,
  331. }
  332. if (hooks.startState) hooks.startState(state)
  333. return state
  334. },
  335. token: function (stream, state) {
  336. var ctx = state.context
  337. if (stream.sol()) {
  338. if (ctx.align == null) ctx.align = false
  339. state.indented = stream.indentation()
  340. state.startOfLine = true
  341. }
  342. if (hooks.token) {
  343. // Call hook, with an optional return value of a style to override verilog styling.
  344. var style = hooks.token(stream, state)
  345. if (style !== undefined) {
  346. return style
  347. }
  348. }
  349. if (stream.eatSpace()) return null
  350. curPunc = null
  351. curKeyword = null
  352. var style = (state.tokenize || tokenBase)(stream, state)
  353. if (style == 'comment' || style == 'meta' || style == 'variable') {
  354. if ((curPunc === '=' || curPunc === '<=') && !isInsideScopeKind(ctx, 'assignment')) {
  355. // '<=' could be nonblocking assignment or lessthan-equals (which shouldn't cause indent)
  356. // Search through the context to see if we are already in an assignment.
  357. // '=' could be inside port declaration with comma or ')' afterward, or inside for(;;) block.
  358. pushContext(state, stream.column() + curPunc.length, 'assignment', 'assignment')
  359. if (ctx.align == null) ctx.align = true
  360. }
  361. return style
  362. }
  363. if (ctx.align == null) ctx.align = true
  364. var isClosingAssignment = ctx.type == 'assignment' && closingBracket.test(curPunc) && ctx.prev && ctx.prev.type === curPunc
  365. if (curPunc == ctx.type || isClosingAssignment) {
  366. if (isClosingAssignment) {
  367. ctx = popContext(state)
  368. }
  369. ctx = popContext(state)
  370. if (curPunc == ')') {
  371. // Handle closing macros, assuming they could have a semicolon or begin/end block inside.
  372. if (ctx && ctx.type === 'macro') {
  373. ctx = popContext(state)
  374. while (ctx && (ctx.type == 'statement' || ctx.type == 'assignment')) ctx = popContext(state)
  375. }
  376. } else if (curPunc == '}') {
  377. // Handle closing statements like constraint block: "foreach () {}" which
  378. // do not have semicolon at end.
  379. if (ctx && ctx.type === 'statement') {
  380. while (ctx && ctx.type == 'statement') ctx = popContext(state)
  381. }
  382. }
  383. } else if (((curPunc == ';' || curPunc == ',') && (ctx.type == 'statement' || ctx.type == 'assignment')) || (ctx.type && isClosing(curKeyword, ctx.type))) {
  384. ctx = popContext(state)
  385. while (ctx && (ctx.type == 'statement' || ctx.type == 'assignment')) ctx = popContext(state)
  386. } else if (curPunc == '{') {
  387. pushContext(state, stream.column(), '}')
  388. } else if (curPunc == '[') {
  389. pushContext(state, stream.column(), ']')
  390. } else if (curPunc == '(') {
  391. pushContext(state, stream.column(), ')')
  392. } else if (ctx && ctx.type == 'endcase' && curPunc == ':') {
  393. pushContext(state, stream.column(), 'statement', 'case')
  394. } else if (curPunc == 'newstatement') {
  395. pushContext(state, stream.column(), 'statement', curKeyword)
  396. } else if (curPunc == 'newblock') {
  397. if (curKeyword == 'function' && ctx && (ctx.type == 'statement' || ctx.type == 'endgroup')) {
  398. // The 'function' keyword can appear in some other contexts where it actually does not
  399. // indicate a function (import/export DPI and covergroup definitions).
  400. // Do nothing in this case
  401. } else if (curKeyword == 'task' && ctx && ctx.type == 'statement') {
  402. // Same thing for task
  403. } else if (curKeyword == 'class' && ctx && ctx.type == 'statement') {
  404. // Same thing for class (e.g. typedef)
  405. } else {
  406. var close = openClose[curKeyword]
  407. pushContext(state, stream.column(), close, curKeyword)
  408. }
  409. } else if (curPunc == 'newmacro' || (curKeyword && curKeyword.match(compilerDirectiveRegex))) {
  410. if (curPunc == 'newmacro') {
  411. // Macros (especially if they have parenthesis) potentially have a semicolon
  412. // or complete statement/block inside, and should be treated as such.
  413. pushContext(state, stream.column(), 'macro', 'macro')
  414. }
  415. if (curKeyword.match(compilerDirectiveEndRegex)) {
  416. state.compilerDirectiveIndented -= statementIndentUnit
  417. }
  418. if (curKeyword.match(compilerDirectiveBeginRegex)) {
  419. state.compilerDirectiveIndented += statementIndentUnit
  420. }
  421. }
  422. state.startOfLine = false
  423. return style
  424. },
  425. indent: function (state, textAfter) {
  426. if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass
  427. if (hooks.indent) {
  428. var fromHook = hooks.indent(state)
  429. if (fromHook >= 0) return fromHook
  430. }
  431. var ctx = state.context,
  432. firstChar = textAfter && textAfter.charAt(0)
  433. if (ctx.type == 'statement' && firstChar == '}') ctx = ctx.prev
  434. var closing = false
  435. var possibleClosing = textAfter.match(closingBracketOrWord)
  436. if (possibleClosing) closing = isClosing(possibleClosing[0], ctx.type)
  437. if (!compilerDirectivesUseRegularIndentation && textAfter.match(compilerDirectiveRegex)) {
  438. if (textAfter.match(compilerDirectiveEndRegex)) {
  439. return state.compilerDirectiveIndented - statementIndentUnit
  440. }
  441. return state.compilerDirectiveIndented
  442. }
  443. if (ctx.type == 'statement') return ctx.indented + (firstChar == '{' ? 0 : statementIndentUnit)
  444. else if ((closingBracket.test(ctx.type) || ctx.type == 'assignment') && ctx.align && !dontAlignCalls) return ctx.column + (closing ? 0 : 1)
  445. else if (ctx.type == ')' && !closing) return ctx.indented + statementIndentUnit
  446. else return ctx.indented + (closing ? 0 : indentUnit)
  447. },
  448. blockCommentStart: '/*',
  449. blockCommentEnd: '*/',
  450. lineComment: '//',
  451. fold: 'indent',
  452. }
  453. })
  454. CodeMirror.defineMIME('text/x-verilog', {
  455. name: 'verilog',
  456. })
  457. CodeMirror.defineMIME('text/x-systemverilog', {
  458. name: 'verilog',
  459. })
  460. // TL-Verilog mode.
  461. // See tl-x.org for language spec.
  462. // See the mode in action at makerchip.com.
  463. // Contact: steve.hoover@redwoodeda.com
  464. // TLV Identifier prefixes.
  465. // Note that sign is not treated separately, so "+/-" versions of numeric identifiers
  466. // are included.
  467. var tlvIdentifierStyle = {
  468. '|': 'link',
  469. '>': 'property', // Should condition this off for > TLV 1c.
  470. $: 'variable',
  471. $$: 'variable',
  472. '?$': 'qualifier',
  473. '?*': 'qualifier',
  474. '-': 'hr',
  475. '/': 'property',
  476. '/-': 'property',
  477. '@': 'variable-3',
  478. '@-': 'variable-3',
  479. '@++': 'variable-3',
  480. '@+=': 'variable-3',
  481. '@+=-': 'variable-3',
  482. '@--': 'variable-3',
  483. '@-=': 'variable-3',
  484. '%+': 'tag',
  485. '%-': 'tag',
  486. '%': 'tag',
  487. '>>': 'tag',
  488. '<<': 'tag',
  489. '<>': 'tag',
  490. '#': 'tag', // Need to choose a style for this.
  491. '^': 'attribute',
  492. '^^': 'attribute',
  493. '^!': 'attribute',
  494. '*': 'variable-2',
  495. '**': 'variable-2',
  496. '\\': 'keyword',
  497. '"': 'comment',
  498. }
  499. // Lines starting with these characters define scope (result in indentation).
  500. var tlvScopePrefixChars = {
  501. '/': 'beh-hier',
  502. '>': 'beh-hier',
  503. '-': 'phys-hier',
  504. '|': 'pipe',
  505. '?': 'when',
  506. '@': 'stage',
  507. '\\': 'keyword',
  508. }
  509. var tlvIndentUnit = 3
  510. var tlvTrackStatements = false
  511. var tlvIdentMatch = /^([~!@#\$%\^&\*-\+=\?\/\\\|'"<>]+)([\d\w_]*)/ // Matches an identifier.
  512. // Note that ':' is excluded, because of it's use in [:].
  513. var tlvFirstLevelIndentMatch = /^[! ] /
  514. var tlvLineIndentationMatch = /^[! ] */
  515. var tlvCommentMatch = /^\/[\/\*]/
  516. // Returns a style specific to the scope at the given indentation column.
  517. // Type is one of: "indent", "scope-ident", "before-scope-ident".
  518. function tlvScopeStyle(state, indentation, type) {
  519. // Begin scope.
  520. var depth = indentation / tlvIndentUnit // TODO: Pass this in instead.
  521. return 'tlv-' + state.tlvIndentationStyle[depth] + '-' + type
  522. }
  523. // Return true if the next thing in the stream is an identifier with a mnemonic.
  524. function tlvIdentNext(stream) {
  525. var match
  526. return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0
  527. }
  528. CodeMirror.defineMIME('text/x-tlv', {
  529. name: 'verilog',
  530. hooks: {
  531. electricInput: false,
  532. // Return undefined for verilog tokenizing, or style for TLV token (null not used).
  533. // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting
  534. // can be enabled with the definition of cm-tlv-* styles, including highlighting for:
  535. // - M4 tokens
  536. // - TLV scope indentation
  537. // - Statement delimitation (enabled by tlvTrackStatements)
  538. token: function (stream, state) {
  539. var style = undefined
  540. var match // Return value of pattern matches.
  541. // Set highlighting mode based on code region (TLV or SV).
  542. if (stream.sol() && !state.tlvInBlockComment) {
  543. // Process region.
  544. if (stream.peek() == '\\') {
  545. style = 'def'
  546. stream.skipToEnd()
  547. if (stream.string.match(/\\SV/)) {
  548. state.tlvCodeActive = false
  549. } else if (stream.string.match(/\\TLV/)) {
  550. state.tlvCodeActive = true
  551. }
  552. }
  553. // Correct indentation in the face of a line prefix char.
  554. if (state.tlvCodeActive && stream.pos == 0 && state.indented == 0 && (match = stream.match(tlvLineIndentationMatch, false))) {
  555. state.indented = match[0].length
  556. }
  557. // Compute indentation state:
  558. // o Auto indentation on next line
  559. // o Indentation scope styles
  560. var indented = state.indented
  561. var depth = indented / tlvIndentUnit
  562. if (depth <= state.tlvIndentationStyle.length) {
  563. // not deeper than current scope
  564. var blankline = stream.string.length == indented
  565. var chPos = depth * tlvIndentUnit
  566. if (chPos < stream.string.length) {
  567. var bodyString = stream.string.slice(chPos)
  568. var ch = bodyString[0]
  569. if (tlvScopePrefixChars[ch] && (match = bodyString.match(tlvIdentMatch)) && tlvIdentifierStyle[match[1]]) {
  570. // This line begins scope.
  571. // Next line gets indented one level.
  572. indented += tlvIndentUnit
  573. // Style the next level of indentation (except non-region keyword identifiers,
  574. // which are statements themselves)
  575. if (!(ch == '\\' && chPos > 0)) {
  576. state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch]
  577. if (tlvTrackStatements) {
  578. state.statementComment = false
  579. }
  580. depth++
  581. }
  582. }
  583. }
  584. // Clear out deeper indentation levels unless line is blank.
  585. if (!blankline) {
  586. while (state.tlvIndentationStyle.length > depth) {
  587. state.tlvIndentationStyle.pop()
  588. }
  589. }
  590. }
  591. // Set next level of indentation.
  592. state.tlvNextIndent = indented
  593. }
  594. if (state.tlvCodeActive) {
  595. // Highlight as TLV.
  596. var beginStatement = false
  597. if (tlvTrackStatements) {
  598. // This starts a statement if the position is at the scope level
  599. // and we're not within a statement leading comment.
  600. beginStatement =
  601. stream.peek() != ' ' && // not a space
  602. style === undefined && // not a region identifier
  603. !state.tlvInBlockComment && // not in block comment
  604. //!stream.match(tlvCommentMatch, false) && // not comment start
  605. stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit // at scope level
  606. if (beginStatement) {
  607. if (state.statementComment) {
  608. // statement already started by comment
  609. beginStatement = false
  610. }
  611. state.statementComment = stream.match(tlvCommentMatch, false) // comment start
  612. }
  613. }
  614. var match
  615. if (style !== undefined) {
  616. // Region line.
  617. style += ' ' + tlvScopeStyle(state, 0, 'scope-ident')
  618. } else if (stream.pos / tlvIndentUnit < state.tlvIndentationStyle.length && (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^ /))) {
  619. // Indentation
  620. style = // make this style distinct from the previous one to prevent
  621. // codemirror from combining spans
  622. 'tlv-indent-' +
  623. (stream.pos % 2 == 0 ? 'even' : 'odd') +
  624. // and style it
  625. ' ' +
  626. tlvScopeStyle(state, stream.pos - tlvIndentUnit, 'indent')
  627. // Style the line prefix character.
  628. if (match[0].charAt(0) == '!') {
  629. style += ' tlv-alert-line-prefix'
  630. }
  631. // Place a class before a scope identifier.
  632. if (tlvIdentNext(stream)) {
  633. style += ' ' + tlvScopeStyle(state, stream.pos, 'before-scope-ident')
  634. }
  635. } else if (state.tlvInBlockComment) {
  636. // In a block comment.
  637. if (stream.match(/^.*?\*\//)) {
  638. // Exit block comment.
  639. state.tlvInBlockComment = false
  640. if (tlvTrackStatements && !stream.eol()) {
  641. // Anything after comment is assumed to be real statement content.
  642. state.statementComment = false
  643. }
  644. } else {
  645. stream.skipToEnd()
  646. }
  647. style = 'comment'
  648. } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) {
  649. // Start comment.
  650. if (match[0] == '//') {
  651. // Line comment.
  652. stream.skipToEnd()
  653. } else {
  654. // Block comment.
  655. state.tlvInBlockComment = true
  656. }
  657. style = 'comment'
  658. } else if ((match = stream.match(tlvIdentMatch))) {
  659. // looks like an identifier (or identifier prefix)
  660. var prefix = match[1]
  661. var mnemonic = match[2]
  662. if (
  663. // is identifier prefix
  664. tlvIdentifierStyle.hasOwnProperty(prefix) &&
  665. // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet)
  666. (mnemonic.length > 0 || stream.eol())
  667. ) {
  668. style = tlvIdentifierStyle[prefix]
  669. if (stream.column() == state.indented) {
  670. // Begin scope.
  671. style += ' ' + tlvScopeStyle(state, stream.column(), 'scope-ident')
  672. }
  673. } else {
  674. // Just swallow one character and try again.
  675. // This enables subsequent identifier match with preceding symbol character, which
  676. // is legal within a statement. (E.g., !$reset). It also enables detection of
  677. // comment start with preceding symbols.
  678. stream.backUp(stream.current().length - 1)
  679. style = 'tlv-default'
  680. }
  681. } else if (stream.match(/^\t+/)) {
  682. // Highlight tabs, which are illegal.
  683. style = 'tlv-tab'
  684. } else if (stream.match(/^[\[\]{}\(\);\:]+/)) {
  685. // [:], (), {}, ;.
  686. style = 'meta'
  687. } else if ((match = stream.match(/^[mM]4([\+_])?[\w\d_]*/))) {
  688. // m4 pre proc
  689. style = match[1] == '+' ? 'tlv-m4-plus' : 'tlv-m4'
  690. } else if (stream.match(/^ +/)) {
  691. // Skip over spaces.
  692. if (stream.eol()) {
  693. // Trailing spaces.
  694. style = 'error'
  695. } else {
  696. // Non-trailing spaces.
  697. style = 'tlv-default'
  698. }
  699. } else if (stream.match(/^[\w\d_]+/)) {
  700. // alpha-numeric token.
  701. style = 'number'
  702. } else {
  703. // Eat the next char w/ no formatting.
  704. stream.next()
  705. style = 'tlv-default'
  706. }
  707. if (beginStatement) {
  708. style += ' tlv-statement'
  709. }
  710. } else {
  711. if (stream.match(/^[mM]4([\w\d_]*)/)) {
  712. // m4 pre proc
  713. style = 'tlv-m4'
  714. }
  715. }
  716. return style
  717. },
  718. indent: function (state) {
  719. return state.tlvCodeActive == true ? state.tlvNextIndent : -1
  720. },
  721. startState: function (state) {
  722. state.tlvIndentationStyle = [] // Styles to use for each level of indentation.
  723. state.tlvCodeActive = true // True when we're in a TLV region (and at beginning of file).
  724. state.tlvNextIndent = -1 // The number of spaces to autoindent the next line if tlvCodeActive.
  725. state.tlvInBlockComment = false // True inside /**/ comment.
  726. if (tlvTrackStatements) {
  727. state.statementComment = false // True inside a statement's header comment.
  728. }
  729. },
  730. },
  731. })
  732. })