diff --git a/lib/lexer.js b/lib/lexer.js index 40db12a3..31348a48 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -1,5 +1,5 @@ (function(){ - var ACCESSORS, ASSIGNMENT, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, HALF_ASSIGNMENTS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, LINE_BREAK, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, _a, _b, _c, balanced_string, compact, count, helpers, include, starts; + var ASSIGNMENT, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, HALF_ASSIGNMENTS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, LINE_BREAK, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, _a, _b, _c, balanced_string, compact, count, helpers, include, starts; var __slice = Array.prototype.slice; // The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt // matches against the beginning of the source code. When a match is found, @@ -126,13 +126,11 @@ // referenced as property names here, so you can still do `jQuery.is()` even // though `is` means `===` otherwise. Lexer.prototype.identifier_token = function() { - var accessed, id, operator, spaced, tag; + var accessed, id, operator, tag; if (!(id = this.match(IDENTIFIER, 1))) { return false; } - this.name_access_type(); - spaced = this.prev() && this.prev().spaced; - accessed = include(ACCESSORS, this.tag(0)) && !spaced; + accessed = this.tag_accessor(); tag = 'IDENTIFIER'; if (!accessed && include(KEYWORDS, id)) { tag = id.toUpperCase(); @@ -412,18 +410,24 @@ // Token Manipulators // ------------------ // As we consume a new `IDENTIFIER`, look at the previous token to determine - // if it's a special kind of accessor. - Lexer.prototype.name_access_type = function() { - if (this.value() === '::') { - this.tag(1, 'PROTOTYPE_ACCESS'); + // if it's a special kind of accessor. Return `true` if any type of accessor + // is the previous token. + Lexer.prototype.tag_accessor = function() { + var prev; + if ((!(prev = this.prev())) || (prev && prev.spaced)) { + return false; } - if (this.value() === '.' && !(this.value(2) === '.')) { + if (prev[1] === '::') { + return this.tag(1, 'PROTOTYPE_ACCESS'); + } else if (prev[1] === '.' && !(this.value(2) === '.')) { if (this.tag(2) === '?') { this.tag(1, 'SOAK_ACCESS'); return this.tokens.splice(-2, 1); } else { return this.tag(1, 'PROPERTY_ACCESS'); } + } else { + return prev[0] === '@'; } }; // Sanitize a heredoc or herecomment by escaping internal double quotes and @@ -693,9 +697,6 @@ // parentheses or bracket following these tokens will be recorded as the start // of a function invocation or indexing operation. CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS']; - // Tokens that indicate an access -- keywords immediately following will be - // treated as identifiers. - ACCESSORS = ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']; // Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN` // occurs at the start of a line. We disambiguate these from trailing whens to // avoid an ambiguity in the grammar. diff --git a/src/lexer.coffee b/src/lexer.coffee index c93ee4bf..8cf1204a 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -89,9 +89,7 @@ exports.Lexer: class Lexer # though `is` means `===` otherwise. identifier_token: -> return false unless id: @match IDENTIFIER, 1 - @name_access_type() - spaced: @prev() and @prev().spaced - accessed: include(ACCESSORS, @tag(0)) and not spaced + accessed: @tag_accessor() tag: 'IDENTIFIER' tag: id.toUpperCase() if not accessed and include(KEYWORDS, id) @identifier_error id if include RESERVED, id @@ -286,15 +284,20 @@ exports.Lexer: class Lexer # ------------------ # As we consume a new `IDENTIFIER`, look at the previous token to determine - # if it's a special kind of accessor. - name_access_type: -> - @tag(1, 'PROTOTYPE_ACCESS') if @value() is '::' - if @value() is '.' and not (@value(2) is '.') + # if it's a special kind of accessor. Return `true` if any type of accessor + # is the previous token. + tag_accessor: -> + return false if (not prev: @prev()) or (prev and prev.spaced) + if prev[1] is '::' + @tag 1, 'PROTOTYPE_ACCESS' + else if prev[1] is '.' and not (@value(2) is '.') if @tag(2) is '?' @tag(1, 'SOAK_ACCESS') @tokens.splice(-2, 1) else @tag 1, 'PROPERTY_ACCESS' + else + prev[0] is '@' # Sanitize a heredoc or herecomment by escaping internal double quotes and # erasing all external indentation on the left-hand side. @@ -526,10 +529,6 @@ NOT_REGEX: [ # of a function invocation or indexing operation. CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS'] -# Tokens that indicate an access -- keywords immediately following will be -# treated as identifiers. -ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@'] - # Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN` # occurs at the start of a line. We disambiguate these from trailing whens to # avoid an ambiguity in the grammar.