mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Better handling of initial indent at file start.
* Detect initial indentation before the first token and enforce it. * Don't add `INDENT` token (or the matching `OUTDENT, TERMINATOR`).
This commit is contained in:
parent
ba7cb3ab69
commit
4fd5e9a3ab
7 changed files with 342 additions and 220 deletions
|
@ -32,7 +32,7 @@
|
||||||
Root: [
|
Root: [
|
||||||
o('', function() {
|
o('', function() {
|
||||||
return new Block;
|
return new Block;
|
||||||
}), o('Body'), o('Block TERMINATOR')
|
}), o('Body')
|
||||||
],
|
],
|
||||||
Body: [
|
Body: [
|
||||||
o('Line', function() {
|
o('Line', function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
}
|
}
|
||||||
this.literate = opts.literate;
|
this.literate = opts.literate;
|
||||||
this.indent = 0;
|
this.indent = 0;
|
||||||
|
this.baseIndent = 0;
|
||||||
this.indebt = 0;
|
this.indebt = 0;
|
||||||
this.outdebt = 0;
|
this.outdebt = 0;
|
||||||
this.indents = [];
|
this.indents = [];
|
||||||
|
@ -346,11 +347,17 @@
|
||||||
this.suppressNewlines();
|
this.suppressNewlines();
|
||||||
return indent.length;
|
return indent.length;
|
||||||
}
|
}
|
||||||
|
if (!this.tokens.length) {
|
||||||
|
this.baseIndent = this.indent = size;
|
||||||
|
return indent.length;
|
||||||
|
}
|
||||||
diff = size - this.indent + this.outdebt;
|
diff = size - this.indent + this.outdebt;
|
||||||
this.token('INDENT', diff, indent.length - size, size);
|
this.token('INDENT', diff, indent.length - size, size);
|
||||||
this.indents.push(diff);
|
this.indents.push(diff);
|
||||||
this.ends.push('OUTDENT');
|
this.ends.push('OUTDENT');
|
||||||
this.outdebt = this.indebt = 0;
|
this.outdebt = this.indebt = 0;
|
||||||
|
} else if (size < this.baseIndent) {
|
||||||
|
this.error('missing indentation', indent.length);
|
||||||
} else {
|
} else {
|
||||||
this.indebt = 0;
|
this.indebt = 0;
|
||||||
this.outdentToken(this.indent - size, noNewlines, indent.length);
|
this.outdentToken(this.indent - size, noNewlines, indent.length);
|
||||||
|
@ -774,10 +781,15 @@
|
||||||
return quote + this.escapeLines(body, heredoc) + quote;
|
return quote + this.escapeLines(body, heredoc) + quote;
|
||||||
};
|
};
|
||||||
|
|
||||||
Lexer.prototype.error = function(message) {
|
Lexer.prototype.error = function(message, offset) {
|
||||||
|
var first_column, first_line, _ref2;
|
||||||
|
if (offset == null) {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
_ref2 = this.getLineAndColumnFromChunk(offset), first_line = _ref2[0], first_column = _ref2[1];
|
||||||
return throwSyntaxError(message, {
|
return throwSyntaxError(message, {
|
||||||
first_line: this.chunkLine,
|
first_line: first_line,
|
||||||
first_column: this.chunkColumn
|
first_column: first_column
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -74,7 +74,6 @@ grammar =
|
||||||
Root: [
|
Root: [
|
||||||
o '', -> new Block
|
o '', -> new Block
|
||||||
o 'Body'
|
o 'Body'
|
||||||
o 'Block TERMINATOR'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Any list of statements and expressions, separated by line breaks or semicolons.
|
# Any list of statements and expressions, separated by line breaks or semicolons.
|
||||||
|
|
|
@ -37,6 +37,7 @@ exports.Lexer = class Lexer
|
||||||
tokenize: (code, opts = {}) ->
|
tokenize: (code, opts = {}) ->
|
||||||
@literate = opts.literate # Are we lexing literate CoffeeScript?
|
@literate = opts.literate # Are we lexing literate CoffeeScript?
|
||||||
@indent = 0 # The current indentation level.
|
@indent = 0 # The current indentation level.
|
||||||
|
@baseIndent = 0 # The overall minimum indentation level
|
||||||
@indebt = 0 # The over-indentation at the current level.
|
@indebt = 0 # The over-indentation at the current level.
|
||||||
@outdebt = 0 # The under-outdentation at the current level.
|
@outdebt = 0 # The under-outdentation at the current level.
|
||||||
@indents = [] # The stack of all current indentation levels.
|
@indents = [] # The stack of all current indentation levels.
|
||||||
|
@ -322,11 +323,16 @@ exports.Lexer = class Lexer
|
||||||
@indebt = size - @indent
|
@indebt = size - @indent
|
||||||
@suppressNewlines()
|
@suppressNewlines()
|
||||||
return indent.length
|
return indent.length
|
||||||
|
unless @tokens.length
|
||||||
|
@baseIndent = @indent = size
|
||||||
|
return indent.length
|
||||||
diff = size - @indent + @outdebt
|
diff = size - @indent + @outdebt
|
||||||
@token 'INDENT', diff, indent.length - size, size
|
@token 'INDENT', diff, indent.length - size, size
|
||||||
@indents.push diff
|
@indents.push diff
|
||||||
@ends.push 'OUTDENT'
|
@ends.push 'OUTDENT'
|
||||||
@outdebt = @indebt = 0
|
@outdebt = @indebt = 0
|
||||||
|
else if size < @baseIndent
|
||||||
|
@error 'missing indentation', indent.length
|
||||||
else
|
else
|
||||||
@indebt = 0
|
@indebt = 0
|
||||||
@outdentToken @indent - size, noNewlines, indent.length
|
@outdentToken @indent - size, noNewlines, indent.length
|
||||||
|
@ -691,10 +697,11 @@ exports.Lexer = class Lexer
|
||||||
quote + @escapeLines(body, heredoc) + quote
|
quote + @escapeLines(body, heredoc) + quote
|
||||||
|
|
||||||
# Throws a compiler error on the current position.
|
# Throws a compiler error on the current position.
|
||||||
error: (message) ->
|
error: (message, offset = 0) ->
|
||||||
# TODO: Are there some cases we could improve the error line number by
|
# TODO: Are there some cases we could improve the error line number by
|
||||||
# passing the offset in the chunk where the error happened?
|
# passing the offset in the chunk where the error happened?
|
||||||
throwSyntaxError message, first_line: @chunkLine, first_column: @chunkColumn
|
[first_line, first_column] = @getLineAndColumnFromChunk offset
|
||||||
|
throwSyntaxError message, {first_line, first_column}
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
# ---------
|
# ---------
|
||||||
|
|
|
@ -144,3 +144,10 @@ test "#1299: Disallow token misnesting", ->
|
||||||
ok no
|
ok no
|
||||||
catch e
|
catch e
|
||||||
eq 'unmatched ]', e.message
|
eq 'unmatched ]', e.message
|
||||||
|
|
||||||
|
test "#2981: Enforce initial indentation", ->
|
||||||
|
try
|
||||||
|
CoffeeScript.compile ' a\nb'
|
||||||
|
ok no
|
||||||
|
catch e
|
||||||
|
eq 'missing indentation', e.message
|
||||||
|
|
|
@ -41,8 +41,8 @@ test "Verify location of generated tokens", ->
|
||||||
test "Verify location of generated tokens (with indented first line)", ->
|
test "Verify location of generated tokens (with indented first line)", ->
|
||||||
tokens = CoffeeScript.tokens " a = 83"
|
tokens = CoffeeScript.tokens " a = 83"
|
||||||
|
|
||||||
eq tokens.length, 6
|
eq tokens.length, 4
|
||||||
[IndentToken, aToken, equalsToken, numberToken] = tokens
|
[aToken, equalsToken, numberToken] = tokens
|
||||||
|
|
||||||
eq aToken[2].first_line, 0
|
eq aToken[2].first_line, 0
|
||||||
eq aToken[2].first_column, 2
|
eq aToken[2].first_column, 2
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue