// Generated by CoffeeScript 2.3.0
(function() {
  // 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.
  var attachCommentsToNode, buildLocationData, buildLocationHash, extend, flatten, ref, repeat, syntaxErrorToString;

  exports.starts = function(string, literal, start) {
    return literal === string.substr(start, literal.length);
  };

  // Peek at the end of a given string to see if it matches a sequence.
  exports.ends = function(string, literal, back) {
    var len;
    len = literal.length;
    return literal === string.substr(string.length - len - (back || 0), len);
  };

  // Repeat a string `n` times.
  exports.repeat = repeat = function(str, n) {
    var res;
    // Use clever algorithm to have O(log(n)) string concatenation operations.
    res = '';
    while (n > 0) {
      if (n & 1) {
        res += str;
      }
      n >>>= 1;
      str += str;
    }
    return res;
  };

  // Trim out all falsy values from an array.
  exports.compact = function(array) {
    var i, item, len1, results;
    results = [];
    for (i = 0, len1 = array.length; i < len1; i++) {
      item = array[i];
      if (item) {
        results.push(item);
      }
    }
    return results;
  };

  // Count the number of occurrences of a string in a string.
  exports.count = function(string, substr) {
    var num, pos;
    num = pos = 0;
    if (!substr.length) {
      return 1 / 0;
    }
    while (pos = 1 + string.indexOf(substr, pos)) {
      num++;
    }
    return 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 = function(options, overrides) {
    return extend(extend({}, options), overrides);
  };

  // Extend a source object with the properties of another object (shallow copy).
  extend = exports.extend = function(object, properties) {
    var key, val;
    for (key in properties) {
      val = properties[key];
      object[key] = val;
    }
    return object;
  };

  // Return a flattened version of an array.
  // Handy for getting a list of `children` from the nodes.
  exports.flatten = flatten = function(array) {
    var element, flattened, i, len1;
    flattened = [];
    for (i = 0, len1 = array.length; i < len1; i++) {
      element = array[i];
      if ('[object Array]' === Object.prototype.toString.call(element)) {
        flattened = flattened.concat(flatten(element));
      } else {
        flattened.push(element);
      }
    }
    return 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 = function(obj, key) {
    var val;
    val = obj[key];
    delete obj[key];
    return val;
  };

  // Typical Array::some
  exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
    var e, i, len1, ref1;
    ref1 = this;
    for (i = 0, len1 = ref1.length; i < len1; i++) {
      e = ref1[i];
      if (fn(e)) {
        return true;
      }
    }
    return false;
  };

  // Helper function for extracting code from Literate CoffeeScript by stripping
  // out all non-code blocks, producing a string of CoffeeScript code that can
  // be compiled “normally.”
  exports.invertLiterate = function(code) {
    var blankLine, i, indented, insideComment, len1, line, listItemStart, out, ref1;
    out = [];
    blankLine = /^\s*$/;
    indented = /^[\t ]/;
    listItemStart = /^(?:\t?| {0,3})(?:[\*\-\+]|[0-9]{1,9}\.)[ \t]/; // Up to one tab, or up to three spaces, or neither;
    // followed by `*`, `-` or `+`;
    // or by an integer up to 9 digits long, followed by a period;
    // followed by a space or a tab.
    insideComment = false;
    ref1 = code.split('\n');
    for (i = 0, len1 = ref1.length; i < len1; i++) {
      line = ref1[i];
      if (blankLine.test(line)) {
        insideComment = false;
        out.push(line);
      } else if (insideComment || listItemStart.test(line)) {
        insideComment = true;
        out.push(`# ${line}`);
      } else if (!insideComment && indented.test(line)) {
        out.push(line);
      } else {
        insideComment = true;
        out.push(`# ${line}`);
      }
    }
    return out.join('\n');
  };

  // Merge two jison-style location data objects together.
  // If `last` is not provided, this will simply return `first`.
  buildLocationData = function(first, last) {
    if (!last) {
      return first;
    } else {
      return {
        first_line: first.first_line,
        first_column: first.first_column,
        last_line: last.last_line,
        last_column: last.last_column
      };
    }
  };

  buildLocationHash = function(loc) {
    return `${loc.first_line}x${loc.first_column}-${loc.last_line}x${loc.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.addDataToNode = function(parserState, first, last) {
    return function(obj) {
      var i, len1, objHash, ref1, token, tokenHash;
      // Add location data
      if (((obj != null ? obj.updateLocationDataIfMissing : void 0) != null) && (first != null)) {
        obj.updateLocationDataIfMissing(buildLocationData(first, last));
      }
      // Add comments data
      if (!parserState.tokenComments) {
        parserState.tokenComments = {};
        ref1 = parserState.parser.tokens;
        for (i = 0, len1 = ref1.length; i < len1; i++) {
          token = ref1[i];
          if (!token.comments) {
            continue;
          }
          tokenHash = buildLocationHash(token[2]);
          if (parserState.tokenComments[tokenHash] == null) {
            parserState.tokenComments[tokenHash] = token.comments;
          } else {
            parserState.tokenComments[tokenHash].push(...token.comments);
          }
        }
      }
      if (obj.locationData != null) {
        objHash = buildLocationHash(obj.locationData);
        if (parserState.tokenComments[objHash] != null) {
          attachCommentsToNode(parserState.tokenComments[objHash], obj);
        }
      }
      return obj;
    };
  };

  exports.attachCommentsToNode = attachCommentsToNode = function(comments, node) {
    if ((comments == null) || comments.length === 0) {
      return;
    }
    if (node.comments == null) {
      node.comments = [];
    }
    return node.comments.push(...comments);
  };

  // Convert jison location data to a string.
  // `obj` can be a token, or a locationData.
  exports.locationDataToString = function(obj) {
    var locationData;
    if (("2" in obj) && ("first_line" in obj[2])) {
      locationData = obj[2];
    } else if ("first_line" in obj) {
      locationData = obj;
    }
    if (locationData) {
      return `${locationData.first_line + 1}:${locationData.first_column + 1}-` + `${locationData.last_line + 1}:${locationData.last_column + 1}`;
    } else {
      return "No location data";
    }
  };

  // A `.coffee.md` compatible version of `basename`, that returns the file sans-extension.
  exports.baseFileName = function(file, stripExt = false, useWinPathSep = false) {
    var parts, pathSep;
    pathSep = useWinPathSep ? /\\|\// : /\//;
    parts = file.split(pathSep);
    file = parts[parts.length - 1];
    if (!(stripExt && file.indexOf('.') >= 0)) {
      return file;
    }
    parts = file.split('.');
    parts.pop();
    if (parts[parts.length - 1] === 'coffee' && parts.length > 1) {
      parts.pop();
    }
    return parts.join('.');
  };

  // Determine if a filename represents a CoffeeScript file.
  exports.isCoffee = function(file) {
    return /\.((lit)?coffee|coffee\.md)$/.test(file);
  };

  // Determine if a filename represents a Literate CoffeeScript file.
  exports.isLiterate = function(file) {
    return /\.(litcoffee|coffee\.md)$/.test(file);
  };

  // Throws a SyntaxError from a given location.
  // The error's `toString` will return an error message following the "standard"
  // format `<filename>:<line>:<col>: <message>` plus the line with the error and a
  // marker showing where the error is.
  exports.throwSyntaxError = function(message, location) {
    var error;
    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 = function(error, code, filename) {
    // Avoid screwing up the `stack` property of other errors (i.e. possible bugs).
    if (error.toString === syntaxErrorToString) {
      error.code || (error.code = code);
      error.filename || (error.filename = filename);
      error.stack = error.toString();
    }
    return error;
  };

  syntaxErrorToString = function() {
    var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, ref1, ref2, ref3, start;
    if (!(this.code && this.location)) {
      return Error.prototype.toString.call(this);
    }
    ({first_line, first_column, last_line, last_column} = this.location);
    if (last_line == null) {
      last_line = first_line;
    }
    if (last_column == null) {
      last_column = first_column;
    }
    filename = this.filename || '[stdin]';
    codeLine = this.code.split('\n')[first_line];
    start = first_column;
    // Show only the first line on multi-line errors.
    end = first_line === last_line ? last_column + 1 : codeLine.length;
    marker = codeLine.slice(0, start).replace(/[^\s]/g, ' ') + repeat('^', end - start);
    // Check to see if we're running on a color-enabled TTY.
    if (typeof process !== "undefined" && process !== null) {
      colorsEnabled = ((ref1 = process.stdout) != null ? ref1.isTTY : void 0) && !((ref2 = process.env) != null ? ref2.NODE_DISABLE_COLORS : void 0);
    }
    if ((ref3 = this.colorful) != null ? ref3 : colorsEnabled) {
      colorize = function(str) {
        return `\x1B[1;31m${str}\x1B[0m`;
      };
      codeLine = codeLine.slice(0, start) + colorize(codeLine.slice(start, end)) + codeLine.slice(end);
      marker = colorize(marker);
    }
    return `${filename}:${first_line + 1}:${first_column + 1}: error: ${this.message}\n${codeLine}\n${marker}`;
  };

  exports.nameWhitespaceCharacter = function(string) {
    switch (string) {
      case ' ':
        return 'space';
      case '\n':
        return 'newline';
      case '\r':
        return 'carriage return';
      case '\t':
        return 'tab';
      default:
        return string;
    }
  };

}).call(this);
