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