// Generated by CoffeeScript 2.3.2 (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, range: [first.range[0], last.range[1]] }; } }; buildLocationHash = function(loc) { return `${loc.first_line}x${loc.first_column}-${loc.last_line}x${loc.last_column}`; }; // Build a dictionary of extra token properties organized by tokens’ locations // used as lookup hashes. buildTokenDataDictionary = function(parserState) { var base, i, len1, ref1, token, tokenData, tokenHash; tokenData = {}; ref1 = parserState.parser.tokens; for (i = 0, len1 = ref1.length; i < len1; i++) { token = ref1[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. ((base = tokenData[tokenHash]).comments != null ? base.comments : base.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, first, last, forceUpdateLocation = true) { return function(obj) { var objHash, ref1; // Add location data. if (((obj != null ? obj.updateLocationDataIfMissing : void 0) != null) && (first != null)) { obj.updateLocationDataIfMissing(buildLocationData(first, last), forceUpdateLocation); } // Add comments, building the dictionary of token data if it hasn’t been // built yet. if (parserState.tokenData == null) { parserState.tokenData = buildTokenDataDictionary(parserState); } if (obj.locationData != null) { objHash = buildLocationHash(obj.locationData); if (((ref1 = parserState.tokenData[objHash]) != null ? ref1.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"; } }; // 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 `::: ` 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; } }; 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);