// Generated by CoffeeScript 2.7.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 UNICODE_CODE_POINT_ESCAPE, attachCommentsToNode, buildLocationData, buildLocationHash, buildTokenDataDictionary, extend, flatten, isBoolean, isNumber, isString, ref, repeat, syntaxErrorToString, unicodeCodePointToUnicodeEscapes,
    indexOf = [].indexOf;

  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,
        last_line_exclusive: last.last_line_exclusive,
        last_column_exclusive: last.last_column_exclusive,
        range: [first.range[0], last.range[1]]
      };
    }
  };

  // Build a list of all comments attached to tokens.
  exports.extractAllCommentTokens = function(tokens) {
    var allCommentsObj, comment, commentKey, i, j, k, key, len1, len2, len3, ref1, results, sortedKeys, token;
    allCommentsObj = {};
    for (i = 0, len1 = tokens.length; i < len1; i++) {
      token = tokens[i];
      if (token.comments) {
        ref1 = token.comments;
        for (j = 0, len2 = ref1.length; j < len2; j++) {
          comment = ref1[j];
          commentKey = comment.locationData.range[0];
          allCommentsObj[commentKey] = comment;
        }
      }
    }
    sortedKeys = Object.keys(allCommentsObj).sort(function(a, b) {
      return a - b;
    });
    results = [];
    for (k = 0, len3 = sortedKeys.length; k < len3; k++) {
      key = sortedKeys[k];
      results.push(allCommentsObj[key]);
    }
    return results;
  };

  // Get a lookup hash for a token based on its location data.
  // Multiple tokens might have the same location hash, but using exclusive
  // location data distinguishes e.g. zero-length generated tokens from
  // actual source tokens.
  buildLocationHash = function(loc) {
    return `${loc.range[0]}-${loc.range[1]}`;
  };

  // Build a dictionary of extra token properties organized by tokens’ locations
  // used as lookup hashes.
  exports.buildTokenDataDictionary = buildTokenDataDictionary = function(tokens) {
    var base1, i, len1, token, tokenData, tokenHash;
    tokenData = {};
    for (i = 0, len1 = tokens.length; i < len1; i++) {
      token = tokens[i];
      if (!token.comments) {
        continue;
      }
      tokenHash = buildLocationHash(token[2]);
      // Multiple tokens might have the same location hash, such as the generated
      // `JS` tokens added at the start or end of the token stream to hold
      // comments that start or end a file.
      if (tokenData[tokenHash] == null) {
        tokenData[tokenHash] = {};
      }
      if (token.comments) { // `comments` is always an array.
        // For “overlapping” tokens, that is tokens with the same location data
        // and therefore matching `tokenHash`es, merge the comments from both/all
        // tokens together into one array, even if there are duplicate comments;
        // they will get sorted out later.
        ((base1 = tokenData[tokenHash]).comments != null ? base1.comments : base1.comments = []).push(...token.comments);
      }
    }
    return tokenData;
  };

  // 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, firstLocationData, firstValue, lastLocationData, lastValue, forceUpdateLocation = true) {
    return function(obj) {
      var locationData, objHash, ref1, ref2, ref3;
      // Add location data.
      locationData = buildLocationData((ref1 = firstValue != null ? firstValue.locationData : void 0) != null ? ref1 : firstLocationData, (ref2 = lastValue != null ? lastValue.locationData : void 0) != null ? ref2 : lastLocationData);
      if (((obj != null ? obj.updateLocationDataIfMissing : void 0) != null) && (firstLocationData != null)) {
        obj.updateLocationDataIfMissing(locationData, forceUpdateLocation);
      } else {
        obj.locationData = locationData;
      }
      // Add comments, building the dictionary of token data if it hasn’t been
      // built yet.
      if (parserState.tokenData == null) {
        parserState.tokenData = buildTokenDataDictionary(parserState.parser.tokens);
      }
      if (obj.locationData != null) {
        objHash = buildLocationHash(obj.locationData);
        if (((ref3 = parserState.tokenData[objHash]) != null ? ref3.comments : void 0) != null) {
          attachCommentsToNode(parserState.tokenData[objHash].comments, 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";
    }
  };

  // Generate a unique anonymous file name so we can distinguish source map cache
  // entries for any number of anonymous scripts.
  exports.anonymousFileName = (function() {
    var n;
    n = 0;
    return function() {
      return `<anonymous-${n++}>`;
    };
  })();

  // 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, ref4, 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;
    }
    if ((ref1 = this.filename) != null ? ref1.startsWith('<anonymous') : void 0) {
      filename = '[stdin]';
    } else {
      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 = ((ref2 = process.stdout) != null ? ref2.isTTY : void 0) && !((ref3 = process.env) != null ? ref3.NODE_DISABLE_COLORS : void 0);
    }
    if ((ref4 = this.colorful) != null ? ref4 : 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}
${codeLine}
${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;
    }
  };

  exports.parseNumber = function(string) {
    var base;
    if (string == null) {
      return 0/0;
    }
    base = (function() {
      switch (string.charAt(1)) {
        case 'b':
          return 2;
        case 'o':
          return 8;
        case 'x':
          return 16;
        default:
          return null;
      }
    })();
    if (base != null) {
      return parseInt(string.slice(2).replace(/_/g, ''), base);
    } else {
      return parseFloat(string.replace(/_/g, ''));
    }
  };

  exports.isFunction = function(obj) {
    return Object.prototype.toString.call(obj) === '[object Function]';
  };

  exports.isNumber = isNumber = function(obj) {
    return Object.prototype.toString.call(obj) === '[object Number]';
  };

  exports.isString = isString = function(obj) {
    return Object.prototype.toString.call(obj) === '[object String]';
  };

  exports.isBoolean = isBoolean = function(obj) {
    return obj === true || obj === false || Object.prototype.toString.call(obj) === '[object Boolean]';
  };

  exports.isPlainObject = function(obj) {
    return typeof obj === 'object' && !!obj && !Array.isArray(obj) && !isNumber(obj) && !isString(obj) && !isBoolean(obj);
  };

  unicodeCodePointToUnicodeEscapes = function(codePoint) {
    var high, low, toUnicodeEscape;
    toUnicodeEscape = function(val) {
      var str;
      str = val.toString(16);
      return `\\u${repeat('0', 4 - str.length)}${str}`;
    };
    if (codePoint < 0x10000) {
      return toUnicodeEscape(codePoint);
    }
    // surrogate pair
    high = Math.floor((codePoint - 0x10000) / 0x400) + 0xD800;
    low = (codePoint - 0x10000) % 0x400 + 0xDC00;
    return `${toUnicodeEscape(high)}${toUnicodeEscape(low)}`;
  };

  // Replace `\u{...}` with `\uxxxx[\uxxxx]` in regexes without `u` flag
  exports.replaceUnicodeCodePointEscapes = function(str, {flags, error, delimiter = ''} = {}) {
    var shouldReplace;
    shouldReplace = (flags != null) && indexOf.call(flags, 'u') < 0;
    return str.replace(UNICODE_CODE_POINT_ESCAPE, function(match, escapedBackslash, codePointHex, offset) {
      var codePointDecimal;
      if (escapedBackslash) {
        return 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
        });
      }
      if (!shouldReplace) {
        return match;
      }
      return unicodeCodePointToUnicodeEscapes(codePointDecimal);
    });
  };

  UNICODE_CODE_POINT_ESCAPE = /(\\\\)|\\u\{([\da-fA-F]+)\}/g; // Make sure the escape isn’t escaped.

}).call(this);
