• ジャンプ … +
    browser.coffee cake.coffee coffeescript.coffee command.coffee grammar.coffee helpers.coffee index.coffee lexer.coffee nodes.coffee optparse.coffee register.coffee repl.coffee rewriter.coffee scope.litcoffee sourcemap.litcoffee
  • helpers.coffee

  • §

    このファイルには、Lexer、Rewriter、およびNodes間で共有したい共通のヘルパー関数が含まれています。オブジェクトのマージ、配列の平坦化、文字数のカウントなどを行います。

  • §

    与えられた文字列の先頭が、あるシーケンスに一致するかどうかを確認します。

    exports.starts = (string, literal, start) ->
      literal is string.substr start, literal.length
  • §

    与えられた文字列の末尾が、あるシーケンスに一致するかどうかを確認します。

    exports.ends = (string, literal, back) ->
      len = literal.length
      literal is string.substr string.length - len - (back or 0), len
  • §

    文字列をn回繰り返します。

    exports.repeat = repeat = (str, n) ->
  • §

    賢いアルゴリズムを使用して、O(log(n))の文字列連結操作を実現します。

      res = ''
      while n > 0
        res += str if n & 1
        n >>>= 1
        str += str
      res
  • §

    配列からすべてのfalsyな値を削除します。

    exports.compact = (array) ->
      item for item in array when item
  • §

    文字列中の特定の文字列の出現回数をカウントします。

    exports.count = (string, substr) ->
      num = pos = 0
      return 1/0 unless substr.length
      num++ while pos = 1 + string.indexOf substr, pos
      num
  • §

    オブジェクトをマージし、両方の属性を持つ新しいコピーを返します。 Base#compileが呼び出されるたびに使用され、オプションハッシュのプロパティが他のブランチを汚染することなくツリーを伝播できるようにします。

    exports.merge = (options, overrides) ->
      extend (extend {}, options), overrides
  • §

    別のオブジェクトのプロパティでソースオブジェクトを拡張します(浅いコピー)。

    extend = exports.extend = (object, properties) ->
      for key, val of properties
        object[key] = val
      object
  • §

    配列の平坦化されたバージョンを返します。ノードからchildrenのリストを取得するのに便利です。

    exports.flatten = flatten = (array) ->
      array.flat(Infinity)
  • §

    オブジェクトからキーを削除し、値を返します。ノードがオプションハッシュで特定のメソッドを探している場合に便利です。

    exports.del = (obj, key) ->
      val =  obj[key]
      delete obj[key]
      val
  • §

    典型的なArray::some

    exports.some = Array::some ? (fn) ->
      return true for e in this when fn e
      false
  • §

    リテラートCoffeeScriptから、すべての非コードブロックを削除してコードを抽出するヘルパー関数。コンパイル可能なCoffeeScriptコードの文字列を生成します。

    exports.invertLiterate = (code) ->
      out = []
      blankLine = /^\s*$/
      indented = /^[\t ]/
      listItemStart = /// ^
        (?:\t?|\ {0,3})   # Up to one tab, or up to three spaces, or neither;
        (?:
          [\*\-\+] |      # followed by `*`, `-` or `+`;
          [0-9]{1,9}\.    # or by an integer up to 9 digits long, followed by a period;
        )
        [\ \t]            # followed by a space or a tab.
      ///
      insideComment = no
      for line in code.split('\n')
        if blankLine.test(line)
          insideComment = no
          out.push line
        else if insideComment or listItemStart.test(line)
          insideComment = yes
          out.push "# #{line}"
        else if not insideComment and indented.test(line)
          out.push line
        else
          insideComment = yes
          out.push "# #{line}"
      out.join '\n'
  • §

    2つのjisonスタイルの位置データオブジェクトをマージします。lastが提供されていない場合、これは単にfirstを返します。

    buildLocationData = (first, last) ->
      if not last
        first
      else
        first_line: first.first_line
        first_column: first.first_column
        last_line: last.last_line
        last_column: last.last_column
        last_line_exclusive: last.last_line_exclusive
        last_column_exclusive: last.last_column_exclusive
        range: [
          first.range[0]
          last.range[1]
        ]
  • §

    トークンに付加されたすべてのコメントのリストを作成します。

    exports.extractAllCommentTokens = (tokens) ->
      allCommentsObj = {}
      for token in tokens when token.comments
        for comment in token.comments
          commentKey = comment.locationData.range[0]
          allCommentsObj[commentKey] = comment
      sortedKeys = Object.keys(allCommentsObj).sort (a, b) -> a - b
      for key in sortedKeys
        allCommentsObj[key]
  • §

    その位置データに基づいて、トークンのルックアップハッシュを取得します。複数のトークンが同じ位置ハッシュを持つ可能性がありますが、排他的な位置データを使用すると、たとえば、ゼロ長の生成されたトークンと実際のソーストークンを区別できます。

    buildLocationHash = (loc) ->
      "#{loc.range[0]}-#{loc.range[1]}"
  • §

    ルックアップハッシュとして使用されるトークンの位置で編成された追加のトークンプロパティのディクショナリを作成します。

    exports.buildTokenDataDictionary = buildTokenDataDictionary = (tokens) ->
      tokenData = {}
      for token in tokens when token.comments
        tokenHash = buildLocationHash token[2]
  • §

    ファイルの先頭または末尾にあるコメントを保持するためにトークンストリームの先頭または末尾に追加された生成されたJSトークンなど、複数のトークンが同じ位置ハッシュを持つ可能性があります。

        tokenData[tokenHash] ?= {}
        if token.comments # `comments` is always an array.
  • §

    「重複」するトークン、つまり同じ位置データを持ち、それゆえtokenHashが一致するトークンについては、たとえ重複するコメントがあったとしても、両方/すべてのトークンのコメントを1つの配列にマージします。これらは後でソートされます。

          (tokenData[tokenHash].comments ?= []).push token.comments...
      tokenData
  • §

    これは、オブジェクトをパラメータとして受け取り、そのオブジェクトがASTノードである場合、そのオブジェクトのlocationDataを更新する関数を返します。オブジェクトはいずれの場合も返されます。

    exports.addDataToNode = (parserState, firstLocationData, firstValue, lastLocationData, lastValue, forceUpdateLocation = yes) ->
      (obj) ->
  • §

    位置データを追加します。

        locationData = buildLocationData(firstValue?.locationData ? firstLocationData, lastValue?.locationData ? lastLocationData)
        if obj?.updateLocationDataIfMissing? and firstLocationData?
          obj.updateLocationDataIfMissing locationData, forceUpdateLocation
        else
          obj.locationData = locationData
  • §

    コメントを追加し、まだ作成されていない場合はトークンデータのディクショナリを作成します。

        parserState.tokenData ?= buildTokenDataDictionary parserState.parser.tokens
        if obj.locationData?
          objHash = buildLocationHash obj.locationData
          if parserState.tokenData[objHash]?.comments?
            attachCommentsToNode parserState.tokenData[objHash].comments, obj
        obj
    
    exports.attachCommentsToNode = attachCommentsToNode = (comments, node) ->
      return if not comments? or comments.length is 0
      node.comments ?= []
      node.comments.push comments...
  • §

    jisonの位置データを文字列に変換します。 objはトークンまたはlocationDataにできます。

    exports.locationDataToString = (obj) ->
      if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
      else if "first_line" of obj then locationData = obj
    
      if locationData
        "#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
        "#{locationData.last_line + 1}:#{locationData.last_column + 1}"
      else
        "No location data"
  • §

    任意の数の匿名スクリプトのソースマップキャッシュエントリを区別できるように、一意の匿名ファイル名を生成します。

    exports.anonymousFileName = do ->
      n = 0
      ->
        "<anonymous-#{n++}>"
  • §

    拡張子なしでファイルを返すbasenameの.coffee.md互換バージョンです。

    exports.baseFileName = (file, stripExt = no, useWinPathSep = no) ->
      pathSep = if useWinPathSep then /\\|\// else /\//
      parts = file.split(pathSep)
      file = parts[parts.length - 1]
      return file unless stripExt and file.indexOf('.') >= 0
      parts = file.split('.')
      parts.pop()
      parts.pop() if parts[parts.length - 1] is 'coffee' and parts.length > 1
      parts.join('.')
  • §

    ファイル名がCoffeeScriptファイルを表すかどうかを判断します。

    exports.isCoffee = (file) -> /\.((lit)?coffee|coffee\.md)$/.test file
  • §

    ファイル名がリテラートCoffeeScriptファイルを表すかどうかを判断します。

    exports.isLiterate = (file) -> /\.(litcoffee|coffee\.md)$/.test file
  • §

    指定された位置からSyntaxErrorをスローします。エラーのtoStringは、「標準」形式<ファイル名>:<行>:<列>: <メッセージ>に、エラーのある行とエラーの位置を示すマーカーが続くエラーメッセージを返します。

    exports.throwSyntaxError = (message, location) ->
      error = new SyntaxError message
      error.location = location
      error.toString = syntaxErrorToString
  • §

    コンパイラのスタックトレースを表示する代わりに、カスタムエラーメッセージを表示します(これは、たとえば、CoffeeScriptをコンパイルするNode.jsアプリケーションでエラーが浮上する場合に役立ちます)。

      error.stack = error.toString()
    
      throw error
  • §

    まだソースコード情報がない場合、コンパイラのSyntaxErrorをソースコード情報で更新します。

    exports.updateSyntaxError = (error, code, filename) ->
  • §

    他のエラー(つまり、考えられるバグ)のstackプロパティを混乱させないようにします。

      if error.toString is syntaxErrorToString
        error.code or= code
        error.filename or= filename
        error.stack = error.toString()
      error
    
    syntaxErrorToString = ->
      return Error::toString.call @ unless @code and @location
    
      {first_line, first_column, last_line, last_column} = @location
      last_line ?= first_line
      last_column ?= first_column
    
      if @filename?.startsWith '<anonymous'
        filename = '[stdin]'
      else
        filename = @filename or '[stdin]'
    
      codeLine = @code.split('\n')[first_line]
      start    = first_column
  • §

    複数行のエラーで最初の行のみを表示します。

      end      = if first_line is last_line then last_column + 1 else codeLine.length
      marker   = codeLine[...start].replace(/[^\s]/g, ' ') + repeat('^', end - start)
  • §

    カラー対応のTTYで実行しているかどうかを確認します。

      if process?
        colorsEnabled = process.stdout?.isTTY and not process.env?.NODE_DISABLE_COLORS
    
      if @colorful ? colorsEnabled
        colorize = (str) -> "\x1B[1;31m#{str}\x1B[0m"
        codeLine = codeLine[...start] + colorize(codeLine[start...end]) + codeLine[end..]
        marker   = colorize marker
    
      """
        #{filename}:#{first_line + 1}:#{first_column + 1}: error: #{@message}
        #{codeLine}
        #{marker}
      """
    
    exports.nameWhitespaceCharacter = (string) ->
      switch string
        when ' ' then 'space'
        when '\n' then 'newline'
        when '\r' then 'carriage return'
        when '\t' then 'tab'
        else string
    
    exports.parseNumber = (string) ->
      return NaN unless string?
    
      base = switch string.charAt 1
        when 'b' then 2
        when 'o' then 8
        when 'x' then 16
        else null
    
      if base?
        parseInt string[2..].replace(/_/g, ''), base
      else
        parseFloat string.replace(/_/g, '')
    
    exports.isFunction = (obj) -> Object::toString.call(obj) is '[object Function]'
    exports.isNumber = isNumber = (obj) -> Object::toString.call(obj) is '[object Number]'
    exports.isString = isString = (obj) -> Object::toString.call(obj) is '[object String]'
    exports.isBoolean = isBoolean = (obj) -> obj is yes or obj is no or Object::toString.call(obj) is '[object Boolean]'
    exports.isPlainObject = (obj) -> typeof obj is 'object' and !!obj and not Array.isArray(obj) and not isNumber(obj) and not isString(obj) and not isBoolean(obj)
    
    unicodeCodePointToUnicodeEscapes = (codePoint) ->
      toUnicodeEscape = (val) ->
        str = val.toString 16
        "\\u#{repeat '0', 4 - str.length}#{str}"
      return toUnicodeEscape(codePoint) if codePoint < 0x10000
  • §

    サロゲートペア

      high = Math.floor((codePoint - 0x10000) / 0x400) + 0xD800
      low = (codePoint - 0x10000) % 0x400 + 0xDC00
      "#{toUnicodeEscape(high)}#{toUnicodeEscape(low)}"
  • §

    uフラグなしの正規表現で、\u{...}を\uxxxx[\uxxxx]に置き換えます。

    exports.replaceUnicodeCodePointEscapes = (str, {flags, error, delimiter = ''} = {}) ->
      shouldReplace = flags? and 'u' not in flags
      str.replace UNICODE_CODE_POINT_ESCAPE, (match, escapedBackslash, codePointHex, offset) ->
        return escapedBackslash if escapedBackslash
    
        codePointDecimal = parseInt codePointHex, 16
        if codePointDecimal > 0x10ffff
          error "unicode code point escapes greater than \\u{10ffff} are not allowed",
            offset: offset + delimiter.length
            length: codePointHex.length + 4
        return match unless shouldReplace
    
        unicodeCodePointToUnicodeEscapes codePointDecimal
    
    UNICODE_CODE_POINT_ESCAPE = ///
      ( \\\\ )        # Make sure the escape isn’t escaped.
      |
      \\u\{ ( [\da-fA-F]+ ) \}
    ///g