• Jump To … +
    browser.coffee cake.coffee coffee-script.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

  • ¶

    Copyright (c) 2009-2013 Jeremy Ashkenas Copyright (c) 2013,2014 Michele Bini

    This file contains the common helper functions that we'd like to share among the Lexer, Rewriter, and the Nodes. Merge objects, flatten arrays, count characters, that sort of thing.

    Peek at the beginning of a given string to see if it matches a sequence.

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

    Peek at the end of a given string to see if it matches a sequence.

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

    Repeat a string n times.

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

    Use clever algorithm to have O(log(n)) string concatenation operations.

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

    Trim out all falsy values from an array.

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

    Count the number of occurrences of a string in a string.

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

    Merge objects, returning a fresh copy with attributes from both sides. Used every time Base#compile is called, to allow properties in the options hash to propagate down the tree without polluting other branches.

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

    Extend a source object with the properties of another object (shallow copy).

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

    Return a flattened version of an array. Handy for getting a list of children from the nodes.

    exports.flatten = flatten = (array) ->
      flattened = []
      for element in array
        if element instanceof Array
          flattened = flattened.concat flatten element
        else
          flattened.push element
      flattened
  • ¶

    Delete a key from an object, returning the value. Useful when a node is looking for a particular method in an options hash.

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

    Gets the last item of an array(-like) object.

    exports.last = last = (array, back) -> array[array.length - (back or 0) - 1]
  • ¶

    Typical Array::some

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

    Simple function for inverting Literate CoffeeScript code by putting the documentation in comments, producing a string of CoffeeScript code that can be compiled "normally".

    exports.invertLiterate = (code) ->
      maybe_code = true
      lines = for line in code.split('\n')
        if maybe_code and /^([ ]{4}|[ ]{0,3}\t)/.test line
          line
        else if maybe_code = /^\s*$/.test line
          line
        else
          '# ' + line
      lines.join '\n'
  • ¶

    Merge two jison-style location data objects together. If last is not provided, this will simply return 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
  • ¶

    This returns a function which takes an object as a parameter, and if that object is an AST node, updates that object's locationData. The object is returned either way.

    exports.addLocationDataFn = (first, last) ->
      (obj) ->
        if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
          obj.updateLocationDataIfMissing buildLocationData(first, last)
    
        return obj
  • ¶

    Convert jison location data to a string. obj can be a token, or a 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.locationDataToSource = (l)->
      lines = exports.code.lines or= exports.code.full.split("\n")
  • ¶

    if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2] else if "first_line" of obj then locationData = obj

      m = lines[l.first_line .. l.last_line]
      m[m.length - 1]  = m[m.length - 1]  .substr(0,  l.last_column+1)
      m[0]             = m[0]             .substr(    l.first_column)
      m.join("\n")
  • ¶

    A .coffee.md compatible version of basename, that returns the file sans-extension.

    exports.baseFileName = (file, stripExt = no, useWinPathSep = no) ->
      isExt = (x)-> x is 'coffee' or x is 'refcoffee'
      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 isExt(parts[parts.length - 1]) and parts.length > 1
      parts.join('.')
  • ¶

    Determine if a filename represents a CoffeeScript file.

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

    Determine if a filename represents a Literate CoffeeScript file.

    exports.isLiterate = (file) -> /\.(lit(ref)?coffee|(ref)?coffee\.md)$/.test file
  • ¶

    Throws a SyntaxError from a given location. The error's toString will return an error message following the "standard" format ::: plus the line with the error and a marker showing where the error is.

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

    Instead of showing the compiler's stacktrace, show our custom error message (this is useful when the error bubbles up in Node.js applications that compile CoffeeScript for example).

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

    Update a compiler SyntaxError with source code information if it didn't have it already.

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

    Avoid screwing up the stack property of other errors (i.e. possible bugs).

      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
    
      filename = @filename or '[stdin]'
      codeLine = @code.split('\n')[first_line]
      start    = first_column
  • ¶

    Show only the first line on multi-line errors.

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

    Check to see if we're running on a color-enabled 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