mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Adding initial implementation of here-comments (block comments) Issue #368
This commit is contained in:
parent
7e3c71ed19
commit
8aceef20e1
8 changed files with 252 additions and 212 deletions
|
@ -158,6 +158,8 @@
|
|||
Comment: [
|
||||
o("COMMENT", function() {
|
||||
return new CommentNode($1);
|
||||
}), o("HERECOMMENT", function() {
|
||||
return new CommentNode($1, 'herecomment');
|
||||
})
|
||||
],
|
||||
// [The existential operator](http://jashkenas.github.com/coffee-script/#existence).
|
||||
|
|
65
lib/lexer.js
65
lib/lexer.js
|
@ -187,12 +187,40 @@
|
|||
return false;
|
||||
}
|
||||
quote = match[1].substr(0, 1);
|
||||
doc = this.sanitize_heredoc(match[2] || match[4], quote);
|
||||
doc = this.sanitize_heredoc(match[2] || match[4], {
|
||||
quote: quote
|
||||
});
|
||||
this.interpolate_string(("" + quote + doc + quote));
|
||||
this.line += count(match[1], "\n");
|
||||
this.i += match[1].length;
|
||||
return true;
|
||||
};
|
||||
// Matches and conumes comments. We pass through comments into JavaScript,
|
||||
// so they're treated as real tokens, like any other part of the language.
|
||||
Lexer.prototype.comment_token = function comment_token() {
|
||||
var comment, i, lines, match;
|
||||
if (!(match = this.chunk.match(COMMENT))) {
|
||||
return false;
|
||||
}
|
||||
if (match[3]) {
|
||||
comment = this.sanitize_heredoc(match[3], {
|
||||
herecomment: true
|
||||
});
|
||||
this.token('HERECOMMENT', compact(comment.split(MULTILINER)));
|
||||
} else {
|
||||
lines = compact(match[1].replace(COMMENT_CLEANER, '').split(MULTILINER));
|
||||
i = this.tokens.length - 1;
|
||||
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.line += count(match[1], "\n");
|
||||
this.i += match[1].length;
|
||||
return true;
|
||||
};
|
||||
// Matches JavaScript interpolated directly into the source via backticks.
|
||||
Lexer.prototype.js_token = function js_token() {
|
||||
var script;
|
||||
|
@ -244,25 +272,6 @@
|
|||
delimited = __slice.call(arguments, 0, _d - 0);
|
||||
return balanced_string(this.chunk, delimited);
|
||||
};
|
||||
// Matches and conumes comments. We pass through comments into JavaScript,
|
||||
// so they're treated as real tokens, like any other part of the language.
|
||||
Lexer.prototype.comment_token = function comment_token() {
|
||||
var comment, i, lines;
|
||||
if (!(comment = this.match(COMMENT, 1))) {
|
||||
return false;
|
||||
}
|
||||
this.line += (comment.match(MULTILINER) || []).length;
|
||||
lines = compact(comment.replace(COMMENT_CLEANER, '').split(MULTILINER));
|
||||
i = this.tokens.length - 1;
|
||||
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;
|
||||
return true;
|
||||
};
|
||||
// Matches newlines, indents, and outdents, and determines which is which.
|
||||
// If we can detect that the current line is continued onto the the next line,
|
||||
// then the newline is suppressed:
|
||||
|
@ -276,7 +285,7 @@
|
|||
if (!(indent = this.match(MULTI_DENT, 1))) {
|
||||
return false;
|
||||
}
|
||||
this.line += indent.match(MULTILINER).length;
|
||||
this.line += count(indent, "\n");
|
||||
this.i += indent.length;
|
||||
prev = this.prev(2);
|
||||
size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length;
|
||||
|
@ -405,12 +414,16 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
// Sanitize a heredoc by escaping internal double quotes and erasing all
|
||||
// external indentation on the left-hand side.
|
||||
Lexer.prototype.sanitize_heredoc = function sanitize_heredoc(doc, quote) {
|
||||
// Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||
// erasing all external indentation on the left-hand side.
|
||||
Lexer.prototype.sanitize_heredoc = function sanitize_heredoc(doc, options) {
|
||||
var indent;
|
||||
indent = (doc.match(HEREDOC_INDENT) || ['']).sort()[0];
|
||||
return doc.replace(new RegExp("^" + indent, 'gm'), '').replace(MULTILINER, "\\n").replace(new RegExp(quote, 'g'), '\\"');
|
||||
doc = doc.replace(new RegExp("^" + indent, 'gm'), '');
|
||||
if (options.herecomment) {
|
||||
return doc;
|
||||
}
|
||||
return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), '\\"');
|
||||
};
|
||||
// Tag a half assignment.
|
||||
Lexer.prototype.tag_half_assignment = function tag_half_assignment(tag) {
|
||||
|
@ -636,7 +649,7 @@
|
|||
INTERPOLATION = /^\$([a-zA-Z_@]\w*(\.\w+)*)/;
|
||||
OPERATOR = /^([+\*&|\/\-%=<>:!?]+)([ \t]*)/;
|
||||
WHITESPACE = /^([ \t]+)/;
|
||||
COMMENT = /^(((\n?[ \t]*)?#[^\n]*)+)/;
|
||||
COMMENT = /^((\n?[ \t]*)?#{3}(?!#)\n*([\s\S]*?)\n*([ \t]*)#{3}|((\n?[ \t]*)?#[^\n]*)+)/;
|
||||
CODE = /^((-|=)>)/;
|
||||
MULTI_DENT = /^((\n([ \t]*))+)(\.)?/;
|
||||
LAST_DENTS = /\n([ \t]*)/g;
|
||||
|
|
11
lib/nodes.js
11
lib/nodes.js
|
@ -510,8 +510,9 @@
|
|||
// CoffeeScript passes through comments as JavaScript comments at the
|
||||
// same position.
|
||||
exports.CommentNode = (function() {
|
||||
CommentNode = function CommentNode(lines) {
|
||||
CommentNode = function CommentNode(lines, type) {
|
||||
this.lines = lines;
|
||||
this.type = type;
|
||||
this;
|
||||
return this;
|
||||
};
|
||||
|
@ -520,7 +521,13 @@
|
|||
return this;
|
||||
};
|
||||
CommentNode.prototype.compile_node = function compile_node(o) {
|
||||
return ("" + this.tab + "//") + this.lines.join(("\n" + this.tab + "//"));
|
||||
var sep;
|
||||
if (this.type === 'herecomment') {
|
||||
sep = '\n' + this.idt(1);
|
||||
return "" + this.tab + "/*" + sep + (this.lines.join(sep)) + "\n" + this.tab + "*/";
|
||||
} else {
|
||||
return ("" + this.tab + "//") + this.lines.join(("\n" + this.tab + "//"));
|
||||
}
|
||||
};
|
||||
return CommentNode;
|
||||
})();
|
||||
|
|
322
lib/parser.js
322
lib/parser.js
File diff suppressed because one or more lines are too long
|
@ -165,6 +165,7 @@ grammar: {
|
|||
# positions in which they can occur in the grammar.
|
||||
Comment: [
|
||||
o "COMMENT", -> new CommentNode $1
|
||||
o "HERECOMMENT", -> new CommentNode $1, 'herecomment'
|
||||
]
|
||||
|
||||
# [The existential operator](http://jashkenas.github.com/coffee-script/#existence).
|
||||
|
|
|
@ -126,12 +126,29 @@ exports.Lexer: class Lexer
|
|||
heredoc_token: ->
|
||||
return false unless match: @chunk.match(HEREDOC)
|
||||
quote: match[1].substr 0, 1
|
||||
doc: @sanitize_heredoc match[2] or match[4], quote
|
||||
doc: @sanitize_heredoc match[2] or match[4], {quote}
|
||||
@interpolate_string "$quote$doc$quote"
|
||||
@line: + count match[1], "\n"
|
||||
@i: + match[1].length
|
||||
true
|
||||
|
||||
# Matches and conumes comments. We pass through comments into JavaScript,
|
||||
# so they're treated as real tokens, like any other part of the language.
|
||||
comment_token: ->
|
||||
return false unless match: @chunk.match(COMMENT)
|
||||
if match[3]
|
||||
comment: @sanitize_heredoc match[3], {herecomment: true}
|
||||
@token 'HERECOMMENT', compact comment.split MULTILINER
|
||||
else
|
||||
lines: compact match[1].replace(COMMENT_CLEANER, '').split MULTILINER
|
||||
i: @tokens.length - 1
|
||||
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])
|
||||
@line: + count match[1], "\n"
|
||||
@i: + match[1].length
|
||||
true
|
||||
|
||||
# Matches JavaScript interpolated directly into the source via backticks.
|
||||
js_token: ->
|
||||
return false unless starts @chunk, '`'
|
||||
|
@ -165,19 +182,6 @@ exports.Lexer: class Lexer
|
|||
balanced_token: (delimited...) ->
|
||||
balanced_string @chunk, delimited
|
||||
|
||||
# Matches and conumes comments. We pass through comments into JavaScript,
|
||||
# so they're treated as real tokens, like any other part of the language.
|
||||
comment_token: ->
|
||||
return false unless comment: @match COMMENT, 1
|
||||
@line: + (comment.match(MULTILINER) or []).length
|
||||
lines: compact comment.replace(COMMENT_CLEANER, '').split MULTILINER
|
||||
i: @tokens.length - 1
|
||||
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
|
||||
true
|
||||
|
||||
# Matches newlines, indents, and outdents, and determines which is which.
|
||||
# If we can detect that the current line is continued onto the the next line,
|
||||
# then the newline is suppressed:
|
||||
|
@ -190,7 +194,7 @@ exports.Lexer: class Lexer
|
|||
# can close multiple indents, so we need to know how far in we happen to be.
|
||||
line_token: ->
|
||||
return false unless indent: @match MULTI_DENT, 1
|
||||
@line: + indent.match(MULTILINER).length
|
||||
@line: + count indent, "\n"
|
||||
@i : + indent.length
|
||||
prev: @prev(2)
|
||||
size: indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
|
||||
|
@ -286,13 +290,14 @@ exports.Lexer: class Lexer
|
|||
else
|
||||
@tag 1, 'PROPERTY_ACCESS'
|
||||
|
||||
# Sanitize a heredoc by escaping internal double quotes and erasing all
|
||||
# external indentation on the left-hand side.
|
||||
sanitize_heredoc: (doc, quote) ->
|
||||
# Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||
# erasing all external indentation on the left-hand side.
|
||||
sanitize_heredoc: (doc, options) ->
|
||||
indent: (doc.match(HEREDOC_INDENT) or ['']).sort()[0]
|
||||
doc.replace(new RegExp("^" +indent, 'gm'), '')
|
||||
.replace(MULTILINER, "\\n")
|
||||
.replace(new RegExp(quote, 'g'), '\\"')
|
||||
doc: doc.replace(new RegExp("^" +indent, 'gm'), '')
|
||||
return doc if options.herecomment
|
||||
doc.replace(MULTILINER, "\\n")
|
||||
.replace(new RegExp(options.quote, 'g'), '\\"')
|
||||
|
||||
# Tag a half assignment.
|
||||
tag_half_assignment: (tag) ->
|
||||
|
@ -477,7 +482,7 @@ HEREDOC : /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\
|
|||
INTERPOLATION : /^\$([a-zA-Z_@]\w*(\.\w+)*)/
|
||||
OPERATOR : /^([+\*&|\/\-%=<>:!?]+)([ \t]*)/
|
||||
WHITESPACE : /^([ \t]+)/
|
||||
COMMENT : /^(((\n?[ \t]*)?#[^\n]*)+)/
|
||||
COMMENT : /^((\n?[ \t]*)?#{3}(?!#)\n*([\s\S]*?)\n*([ \t]*)#{3}|((\n?[ \t]*)?#[^\n]*)+)/
|
||||
CODE : /^((-|=)>)/
|
||||
MULTI_DENT : /^((\n([ \t]*))+)(\.)?/
|
||||
LAST_DENTS : /\n([ \t]*)/g
|
||||
|
|
|
@ -358,15 +358,20 @@ children ValueNode, 'base', 'properties'
|
|||
# same position.
|
||||
exports.CommentNode: class CommentNode extends BaseNode
|
||||
|
||||
constructor: (lines) ->
|
||||
constructor: (lines, type) ->
|
||||
@lines: lines
|
||||
@type: type
|
||||
this
|
||||
|
||||
make_return: ->
|
||||
this
|
||||
|
||||
compile_node: (o) ->
|
||||
"$@tab//" + @lines.join("\n$@tab//")
|
||||
if @type is 'herecomment'
|
||||
sep: '\n' + @idt(1)
|
||||
"$@tab/*$sep${ @lines.join(sep) }\n$@tab*/"
|
||||
else
|
||||
"$@tab//" + @lines.join("\n$@tab//")
|
||||
|
||||
statement CommentNode
|
||||
|
||||
|
|
|
@ -54,3 +54,8 @@ test:
|
|||
'test'
|
||||
|
||||
ok test is 'test test test'
|
||||
|
||||
###
|
||||
This is a here-comment.
|
||||
Kind of like a heredoc.
|
||||
###
|
||||
|
|
Loading…
Add table
Reference in a new issue