1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

resolving issues with comments in the middle of unfinished single-line expressions.

This commit is contained in:
Jeremy Ashkenas 2010-03-20 00:58:25 -04:00
parent 29eff23490
commit a2778bf06d
3 changed files with 41 additions and 16 deletions

View file

@ -1,5 +1,5 @@
(function(){ (function(){
var ACCESSORS, ASSIGNMENT, BEFORE_WHEN, CALLABLE, CODE, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_ESCAPE, REGEX_FLAGS, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, balanced_string, compact, count, helpers, include, starts; var ACCESSORS, ASSIGNMENT, CALLABLE, CODE, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, 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_ESCAPE, REGEX_FLAGS, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, balanced_string, compact, count, helpers, include, starts;
// The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt // 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, // matches against the beginning of the source code. When a match is found,
// a token is produced, we consume the match, and start again. Tokens are in the // a token is produced, we consume the match, and start again. Tokens are in the
@ -134,7 +134,7 @@
if (include(RESERVED, id)) { if (include(RESERVED, id)) {
this.identifier_error(id); this.identifier_error(id);
} }
if (tag === 'WHEN' && include(BEFORE_WHEN, this.tag())) { if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
tag = 'LEADING_WHEN'; tag = 'LEADING_WHEN';
} }
this.token(tag, id); this.token(tag, id);
@ -233,14 +233,19 @@
// Matches and conumes comments. We pass through comments into JavaScript, // Matches and conumes comments. We pass through comments into JavaScript,
// so they're treated as real tokens, like any other part of the language. // so they're treated as real tokens, like any other part of the language.
Lexer.prototype.comment_token = function comment_token() { Lexer.prototype.comment_token = function comment_token() {
var comment, lines; var comment, i, lines;
if (!((comment = this.match(COMMENT, 1)))) { if (!((comment = this.match(COMMENT, 1)))) {
return false; return false;
} }
this.line += (comment.match(MULTILINER) || []).length; this.line += (comment.match(MULTILINER) || []).length;
lines = comment.replace(COMMENT_CLEANER, '').split(MULTILINER); lines = compact(comment.replace(COMMENT_CLEANER, '').split(MULTILINER));
this.token('COMMENT', compact(lines)); i = this.tokens.length - 1;
this.token('TERMINATOR', "\n"); if (this.unfinished()) {
while (this.tokens[i] && !include(LINE_BREAK, this.tokens[i][0])) {
i -= 1;
}
}
this.tokens.splice(i + 1, 0, ['COMMENT', lines, this.line], ['TERMINATOR', '\n', this.line]);
this.i += comment.length; this.i += comment.length;
return true; return true;
}; };
@ -262,7 +267,7 @@
prev = this.prev(2); prev = this.prev(2);
size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length; size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length;
next_character = this.chunk.match(MULTI_DENT)[4]; next_character = this.chunk.match(MULTI_DENT)[4];
no_newlines = next_character === '.' || (this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE)); no_newlines = next_character === '.' || this.unfinished();
if (size === this.indent) { if (size === this.indent) {
if (no_newlines) { if (no_newlines) {
return this.suppress_newlines(); return this.suppress_newlines();
@ -551,6 +556,12 @@
} }
return m ? m[index] : false; return m ? m[index] : false;
}; };
// Are we in the midst of an unfinished expression?
Lexer.prototype.unfinished = function unfinished() {
var prev;
prev = this.prev(2);
return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE);
};
return Lexer; return Lexer;
}).call(this); }).call(this);
// There are no exensions to the core lexer by default. // There are no exensions to the core lexer by default.
@ -612,5 +623,5 @@
// Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN` // 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 // occurs at the start of a line. We disambiguate these from trailing whens to
// avoid an ambiguity in the grammar. // avoid an ambiguity in the grammar.
BEFORE_WHEN = ['INDENT', 'OUTDENT', 'TERMINATOR']; LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
})(); })();

View file

@ -98,7 +98,7 @@ exports.Lexer: class Lexer
tag: id.toUpperCase() if include(KEYWORDS, id) and tag: id.toUpperCase() if include(KEYWORDS, id) and
not (include(ACCESSORS, @tag(0)) and not @prev().spaced) not (include(ACCESSORS, @tag(0)) and not @prev().spaced)
@identifier_error id if include RESERVED, id @identifier_error id if include RESERVED, id
tag: 'LEADING_WHEN' if tag is 'WHEN' and include BEFORE_WHEN, @tag() tag: 'LEADING_WHEN' if tag is 'WHEN' and include LINE_BREAK, @tag()
@token(tag, id) @token(tag, id)
@i += id.length @i += id.length
true true
@ -171,9 +171,11 @@ exports.Lexer: class Lexer
comment_token: -> comment_token: ->
return false unless comment: @match COMMENT, 1 return false unless comment: @match COMMENT, 1
@line += (comment.match(MULTILINER) or []).length @line += (comment.match(MULTILINER) or []).length
lines: comment.replace(COMMENT_CLEANER, '').split(MULTILINER) lines: compact comment.replace(COMMENT_CLEANER, '').split(MULTILINER)
@token 'COMMENT', compact lines i: @tokens.length - 1
@token 'TERMINATOR', "\n" if @unfinished()
i -= 1 while @tokens[i] and not include LINE_BREAK, @tokens[i][0]
@tokens.splice(i + 1, 0, ['COMMENT', lines, @line], ['TERMINATOR', '\n', @line])
@i += comment.length @i += comment.length
true true
@ -194,9 +196,7 @@ exports.Lexer: class Lexer
prev: @prev(2) prev: @prev(2)
size: indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length size: indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
next_character: @chunk.match(MULTI_DENT)[4] next_character: @chunk.match(MULTI_DENT)[4]
no_newlines: next_character is '.' or no_newlines: next_character is '.' or @unfinished()
(@value() and @value().match and @value().match(NO_NEWLINE) and
prev and (prev[0] isnt '.') and not @value().match(CODE))
if size is @indent if size is @indent
return @suppress_newlines() if no_newlines return @suppress_newlines() if no_newlines
return @newline_token(indent) return @newline_token(indent)
@ -406,6 +406,12 @@ exports.Lexer: class Lexer
return false unless m: @chunk.match(regex) return false unless m: @chunk.match(regex)
if m then m[index] else false if m then m[index] else false
# Are we in the midst of an unfinished expression?
unfinished: ->
prev: @prev(2)
@value() and @value().match and @value().match(NO_NEWLINE) and
prev and (prev[0] isnt '.') and not @value().match(CODE)
# There are no exensions to the core lexer by default. # There are no exensions to the core lexer by default.
Lexer.extensions: [] Lexer.extensions: []
@ -500,4 +506,4 @@ ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']
# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN` # 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 # occurs at the start of a line. We disambiguate these from trailing whens to
# avoid an ambiguity in the grammar. # avoid an ambiguity in the grammar.
BEFORE_WHEN: ['INDENT', 'OUTDENT', 'TERMINATOR'] LINE_BREAK: ['INDENT', 'OUTDENT', 'TERMINATOR']

View file

@ -45,3 +45,11 @@ else # comment
45 45
ok result is 45 ok result is 45
test:
'test ' +
'test ' + # comment
'test'
ok test is 'test test test'