   | \s+(?:#.*)?     # remove whitespace and comments
 ///g
 
-# Token cleaning regexes.
-MULTILINER      = /\n/g
-
-HEREDOC_INDENT  = /\n+([^\n\S]*)/g
-
-HEREDOC_ILLEGAL = /\*\//
-
-LINE_CONTINUER  = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
-
-TRAILING_SPACES = /\s+$/
+REGEX_ILLEGAL = /// ^ ( / | /{3}\s*) (\*) ///
+
+# Other regexes.
+MULTILINER          = /\n/g
+
+HERECOMMENT_ILLEGAL = /\*\//
+
+LINE_CONTINUER      = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
+
+OCTAL_ESCAPE        = /// ^ ((?: \\. | [^\\] )*) (\\ (?: 0[0-7] | [1-7] )) ///
+
+LEADING_BLANK_LINE  = /^[^\n\S]*\n/
+TRAILING_BLANK_LINE = /\n[^\n\S]*$/
+
+TRAILING_SPACES     = /\s+$/
 
 # Compound assignment tokens.
 COMPOUND_ASSIGN = [

    @token 'NUMBER', number, 0, lexedLength
    lexedLength

  # Matches strings, including multi-line strings. Ensures that quotation marks
  # are balanced within the string's contents, and within nested interpolations.
  stringToken: ->
    switch quote = @chunk.charAt 0
      when "'" then [string] = SIMPLESTR.exec(@chunk) || []
      when '"' then string = @balancedString @chunk, '"'
    return 0 unless string
    inner = string[1...-1]
    trimmed = @removeNewlines inner
    if quote is '"' and 0 < string.indexOf '#{', 1
      numBreak = pos = 0
      innerLen = inner.length
      numBreak++ while inner.charAt(pos++) is '\n' and pos < innerLen
      @interpolateString trimmed, strOffset: 1 + numBreak, lexedLength: string.length
    else
      @token 'STRING', quote + @escapeLines(trimmed) + quote, 0, string.length
    if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
      @error "octal escape sequences #{string} are not allowed"
    string.length

  # Matches heredocs, adjusting indentation to the correct level, as heredocs
  # preserve whitespace, but ignore indentation to the left.
  heredocToken: ->
    return 0 unless match = HEREDOC.exec @chunk
    heredoc = match[0]
    quote = heredoc.charAt 0
    doc = @sanitizeHeredoc match[2], quote: quote, indent: null
    if quote is '"' and 0 <= doc.indexOf '#{'
      strOffset = if match[2].charAt(0) is '\n' then 4 else 3
      @interpolateString doc, heredoc: yes, strOffset: strOffset, lexedLength: heredoc.length
    else
      @token 'STRING', @makeString(doc, quote, yes), 0, heredoc.length
    heredoc.length

  # Matches and consumes comments.
  commentToken: ->
    return 0 unless match = @chunk.match COMMENT
    [comment, here] = match
    if here
      @token 'HERECOMMENT',
        (@sanitizeHeredoc here,
          herecomment: true, indent: repeat ' ', @indent),
        0, comment.length
    comment.length

  # Matches JavaScript interpolated directly into the source via backticks.

    @token 'JS', (script = match[0])[1...-1], 0, script.length
    script.length

  # Matches regular expression literals, as well as multiline extended ones.
  # Lexing regular expressions is difficult to distinguish from division, so we
  # borrow some basic heuristics from JavaScript and Ruby.
  regexToken: ->
    switch
      when match = REGEX_ILLEGAL.exec @chunk
        @error "regular expressions cannot begin with #{match[2]}", match.index + match[1].length
      when @chunk[...3] is '///'
        {tokens, index} = @matchWithInterpolations @chunk[3..], HEREGEX, '///', 3
      when match = REGEX.exec @chunk
        [regex] = match
        index = regex.length
        prev = last @tokens
        return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
      else
        return 0

    [flags] = REGEX_FLAGS.exec @chunk[index..]
    end = index + flags.length
    switch
      when not VALID_FLAGS.test flags
        @error "invalid regular expression flags #{flags}", index
      when regex
        @token 'REGEX', "#{regex}#{flags}"
      when tokens.length is 1
        re = @formatHeregex(tokens[0][1]).replace(/\//g, '\\/')
        @token 'REGEX', "/#{ re or '(?:)' }/#{flags}"
      else
        @token 'IDENTIFIER', 'RegExp', 0, 0
        @token 'CALL_START', '(', 0, 0
        @mergeInterpolationTokens tokens, {quote: '"', start: 3, end}, (value) =>
          @formatHeregex(value).replace(/\\/g, '\\\\')
        if flags
          @token ',', ',', index, 0
          @token 'STRING', '"' + flags + '"', index, flags.length
        @token ')', ')', end, 0

    end

  # Matches newlines, indents, and outdents, and determines which is which.
  # If we can detect that the current line is continued onto the the next line,
