openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

verilog.js (24588B)


      1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
      2 // Distributed under an MIT license: http://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 
     14 CodeMirror.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   };
    374 });
    375 
    376   CodeMirror.defineMIME("text/x-verilog", {
    377     name: "verilog"
    378   });
    379 
    380   CodeMirror.defineMIME("text/x-systemverilog", {
    381     name: "verilog"
    382   });
    383 
    384 
    385 
    386   // TL-Verilog mode.
    387   // See tl-x.org for language spec.
    388   // See the mode in action at makerchip.com.
    389   // Contact: steve.hoover@redwoodeda.com
    390 
    391   // TLV Identifier prefixes.
    392   // Note that sign is not treated separately, so "+/-" versions of numeric identifiers
    393   // are included.
    394   var tlvIdentifierStyle = {
    395     "|": "link",
    396     ">": "property",  // Should condition this off for > TLV 1c.
    397     "$": "variable",
    398     "$$": "variable",
    399     "?$": "qualifier",
    400     "?*": "qualifier",
    401     "-": "hr",
    402     "/": "property",
    403     "/-": "property",
    404     "@": "variable-3",
    405     "@-": "variable-3",
    406     "@++": "variable-3",
    407     "@+=": "variable-3",
    408     "@+=-": "variable-3",
    409     "@--": "variable-3",
    410     "@-=": "variable-3",
    411     "%+": "tag",
    412     "%-": "tag",
    413     "%": "tag",
    414     ">>": "tag",
    415     "<<": "tag",
    416     "<>": "tag",
    417     "#": "tag",  // Need to choose a style for this.
    418     "^": "attribute",
    419     "^^": "attribute",
    420     "^!": "attribute",
    421     "*": "variable-2",
    422     "**": "variable-2",
    423     "\\": "keyword",
    424     "\"": "comment"
    425   };
    426 
    427   // Lines starting with these characters define scope (result in indentation).
    428   var tlvScopePrefixChars = {
    429     "/": "beh-hier",
    430     ">": "beh-hier",
    431     "-": "phys-hier",
    432     "|": "pipe",
    433     "?": "when",
    434     "@": "stage",
    435     "\\": "keyword"
    436   };
    437   var tlvIndentUnit = 3;
    438   var tlvTrackStatements = false;
    439   var tlvIdentMatch = /^([~!@#\$%\^&\*-\+=\?\/\\\|'"<>]+)([\d\w_]*)/;  // Matches an identifiere.
    440   // Note that ':' is excluded, because of it's use in [:].
    441   var tlvFirstLevelIndentMatch = /^[! ]  /;
    442   var tlvLineIndentationMatch = /^[! ] */;
    443   var tlvCommentMatch = /^\/[\/\*]/;
    444 
    445 
    446   // Returns a style specific to the scope at the given indentation column.
    447   // Type is one of: "indent", "scope-ident", "before-scope-ident".
    448   function tlvScopeStyle(state, indentation, type) {
    449     // Begin scope.
    450     var depth = indentation / tlvIndentUnit;  // TODO: Pass this in instead.
    451     return "tlv-" + state.tlvIndentationStyle[depth] + "-" + type;
    452   }
    453 
    454   // Return true if the next thing in the stream is an identifier with a mnemonic.
    455   function tlvIdentNext(stream) {
    456     var match;
    457     return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0;
    458   }
    459 
    460   CodeMirror.defineMIME("text/x-tlv", {
    461     name: "verilog",
    462 
    463     hooks: {
    464 
    465       electricInput: false,
    466 
    467 
    468       // Return undefined for verilog tokenizing, or style for TLV token (null not used).
    469       // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting
    470       // can be enabled with the definition of cm-tlv-* styles, including highlighting for:
    471       //   - M4 tokens
    472       //   - TLV scope indentation
    473       //   - Statement delimitation (enabled by tlvTrackStatements)
    474       token: function(stream, state) {
    475         var style = undefined;
    476         var match;  // Return value of pattern matches.
    477 
    478         // Set highlighting mode based on code region (TLV or SV).
    479         if (stream.sol() && ! state.tlvInBlockComment) {
    480           // Process region.
    481           if (stream.peek() == '\\') {
    482             style = "def";
    483             stream.skipToEnd();
    484             if (stream.string.match(/\\SV/)) {
    485               state.tlvCodeActive = false;
    486             } else if (stream.string.match(/\\TLV/)){
    487               state.tlvCodeActive = true;
    488             }
    489           }
    490           // Correct indentation in the face of a line prefix char.
    491           if (state.tlvCodeActive && stream.pos == 0 &&
    492               (state.indented == 0) && (match = stream.match(tlvLineIndentationMatch, false))) {
    493             state.indented = match[0].length;
    494           }
    495 
    496           // Compute indentation state:
    497           //   o Auto indentation on next line
    498           //   o Indentation scope styles
    499           var indented = state.indented;
    500           var depth = indented / tlvIndentUnit;
    501           if (depth <= state.tlvIndentationStyle.length) {
    502             // not deeper than current scope
    503 
    504             var blankline = stream.string.length == indented;
    505             var chPos = depth * tlvIndentUnit;
    506             if (chPos < stream.string.length) {
    507               var bodyString = stream.string.slice(chPos);
    508               var ch = bodyString[0];
    509               if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) &&
    510                   tlvIdentifierStyle[match[1]])) {
    511                 // This line begins scope.
    512                 // Next line gets indented one level.
    513                 indented += tlvIndentUnit;
    514                 // Style the next level of indentation (except non-region keyword identifiers,
    515                 //   which are statements themselves)
    516                 if (!(ch == "\\" && chPos > 0)) {
    517                   state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch];
    518                   if (tlvTrackStatements) {state.statementComment = false;}
    519                   depth++;
    520                 }
    521               }
    522             }
    523             // Clear out deeper indentation levels unless line is blank.
    524             if (!blankline) {
    525               while (state.tlvIndentationStyle.length > depth) {
    526                 state.tlvIndentationStyle.pop();
    527               }
    528             }
    529           }
    530           // Set next level of indentation.
    531           state.tlvNextIndent = indented;
    532         }
    533 
    534         if (state.tlvCodeActive) {
    535           // Highlight as TLV.
    536 
    537           var beginStatement = false;
    538           if (tlvTrackStatements) {
    539             // This starts a statement if the position is at the scope level
    540             // and we're not within a statement leading comment.
    541             beginStatement =
    542                    (stream.peek() != " ") &&   // not a space
    543                    (style === undefined) &&    // not a region identifier
    544                    !state.tlvInBlockComment && // not in block comment
    545                    //!stream.match(tlvCommentMatch, false) && // not comment start
    546                    (stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit);  // at scope level
    547             if (beginStatement) {
    548               if (state.statementComment) {
    549                 // statement already started by comment
    550                 beginStatement = false;
    551               }
    552               state.statementComment =
    553                    stream.match(tlvCommentMatch, false); // comment start
    554             }
    555           }
    556 
    557           var match;
    558           if (style !== undefined) {
    559             // Region line.
    560             style += " " + tlvScopeStyle(state, 0, "scope-ident")
    561           } else if (((stream.pos / tlvIndentUnit) < state.tlvIndentationStyle.length) &&
    562                      (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^   /))) {
    563             // Indentation
    564             style = // make this style distinct from the previous one to prevent
    565                     // codemirror from combining spans
    566                     "tlv-indent-" + (((stream.pos % 2) == 0) ? "even" : "odd") +
    567                     // and style it
    568                     " " + tlvScopeStyle(state, stream.pos - tlvIndentUnit, "indent");
    569             // Style the line prefix character.
    570             if (match[0].charAt(0) == "!") {
    571               style += " tlv-alert-line-prefix";
    572             }
    573             // Place a class before a scope identifier.
    574             if (tlvIdentNext(stream)) {
    575               style += " " + tlvScopeStyle(state, stream.pos, "before-scope-ident");
    576             }
    577           } else if (state.tlvInBlockComment) {
    578             // In a block comment.
    579             if (stream.match(/^.*?\*\//)) {
    580               // Exit block comment.
    581               state.tlvInBlockComment = false;
    582               if (tlvTrackStatements && !stream.eol()) {
    583                 // Anything after comment is assumed to be real statement content.
    584                 state.statementComment = false;
    585               }
    586             } else {
    587               stream.skipToEnd();
    588             }
    589             style = "comment";
    590           } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) {
    591             // Start comment.
    592             if (match[0] == "//") {
    593               // Line comment.
    594               stream.skipToEnd();
    595             } else {
    596               // Block comment.
    597               state.tlvInBlockComment = true;
    598             }
    599             style = "comment";
    600           } else if (match = stream.match(tlvIdentMatch)) {
    601             // looks like an identifier (or identifier prefix)
    602             var prefix = match[1];
    603             var mnemonic = match[2];
    604             if (// is identifier prefix
    605                 tlvIdentifierStyle.hasOwnProperty(prefix) &&
    606                 // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet)
    607                 (mnemonic.length > 0 || stream.eol())) {
    608               style = tlvIdentifierStyle[prefix];
    609               if (stream.column() == state.indented) {
    610                 // Begin scope.
    611                 style += " " + tlvScopeStyle(state, stream.column(), "scope-ident")
    612               }
    613             } else {
    614               // Just swallow one character and try again.
    615               // This enables subsequent identifier match with preceding symbol character, which
    616               //   is legal within a statement.  (Eg, !$reset).  It also enables detection of
    617               //   comment start with preceding symbols.
    618               stream.backUp(stream.current().length - 1);
    619               style = "tlv-default";
    620             }
    621           } else if (stream.match(/^\t+/)) {
    622             // Highlight tabs, which are illegal.
    623             style = "tlv-tab";
    624           } else if (stream.match(/^[\[\]{}\(\);\:]+/)) {
    625             // [:], (), {}, ;.
    626             style = "meta";
    627           } else if (match = stream.match(/^[mM]4([\+_])?[\w\d_]*/)) {
    628             // m4 pre proc
    629             style = (match[1] == "+") ? "tlv-m4-plus" : "tlv-m4";
    630           } else if (stream.match(/^ +/)){
    631             // Skip over spaces.
    632             if (stream.eol()) {
    633               // Trailing spaces.
    634               style = "error";
    635             } else {
    636               // Non-trailing spaces.
    637               style = "tlv-default";
    638             }
    639           } else if (stream.match(/^[\w\d_]+/)) {
    640             // alpha-numeric token.
    641             style = "number";
    642           } else {
    643             // Eat the next char w/ no formatting.
    644             stream.next();
    645             style = "tlv-default";
    646           }
    647           if (beginStatement) {
    648             style += " tlv-statement";
    649           }
    650         } else {
    651           if (stream.match(/^[mM]4([\w\d_]*)/)) {
    652             // m4 pre proc
    653             style = "tlv-m4";
    654           }
    655         }
    656         return style;
    657       },
    658 
    659       indent: function(state) {
    660         return (state.tlvCodeActive == true) ? state.tlvNextIndent : -1;
    661       },
    662 
    663       startState: function(state) {
    664         state.tlvIndentationStyle = [];  // Styles to use for each level of indentation.
    665         state.tlvCodeActive = true;  // True when we're in a TLV region (and at beginning of file).
    666         state.tlvNextIndent = -1;    // The number of spaces to autoindent the next line if tlvCodeActive.
    667         state.tlvInBlockComment = false;  // True inside /**/ comment.
    668         if (tlvTrackStatements) {
    669           state.statementComment = false;  // True inside a statement's header comment.
    670         }
    671       }
    672 
    673     }
    674   });
    675 });