Merge branch 'master' into 2
# Conflicts: # lib/coffee-script/lexer.js # lib/coffee-script/nodes.js # lib/coffee-script/optparse.js # lib/coffee-script/rewriter.js # lib/coffee-script/scope.js # lib/coffee-script/sourcemap.js # src/nodes.coffee # test/classes.coffee # test/comments.coffee # test/error_messages.coffee
This commit is contained in:
commit
9524159e68
|
@ -581,6 +581,7 @@
|
|||
};
|
||||
}), o('ForStart ForSource', function() {
|
||||
$2.own = $1.own;
|
||||
$2.ownTag = $1.ownTag;
|
||||
$2.name = $1[0];
|
||||
$2.index = $1[1];
|
||||
return $2;
|
||||
|
@ -591,6 +592,7 @@
|
|||
return $2;
|
||||
}), o('FOR OWN ForVariables', function() {
|
||||
$3.own = true;
|
||||
$3.ownTag = LOC(2)(new Literal($2));
|
||||
return $3;
|
||||
})
|
||||
],
|
||||
|
@ -646,6 +648,17 @@
|
|||
step: $4,
|
||||
guard: $6
|
||||
};
|
||||
}), o('FORFROM Expression', function() {
|
||||
return {
|
||||
source: $2,
|
||||
from: true
|
||||
};
|
||||
}), o('FORFROM Expression WHEN Expression', function() {
|
||||
return {
|
||||
source: $2,
|
||||
guard: $4,
|
||||
from: true
|
||||
};
|
||||
})
|
||||
],
|
||||
Switch: [
|
||||
|
@ -764,7 +777,7 @@
|
|||
]
|
||||
};
|
||||
|
||||
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['right', 'AWAIT'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', '&'], ['left', '^'], ['left', '|'], ['left', '&&'], ['left', '||'], ['left', 'BIN?'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', 'YIELD'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT'], ['left', 'POST_IF']];
|
||||
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['right', 'AWAIT'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', '&'], ['left', '^'], ['left', '|'], ['left', '&&'], ['left', '||'], ['left', 'BIN?'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', 'YIELD'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT'], ['left', 'POST_IF']];
|
||||
|
||||
tokens = [];
|
||||
|
||||
|
|
|
@ -128,6 +128,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (tag === 'IDENTIFIER' && this.seenFor && id === 'from') {
|
||||
tag = 'FORFROM';
|
||||
this.seenFor = false;
|
||||
}
|
||||
if (tag === 'IDENTIFIER' && indexOf.call(RESERVED, id) >= 0) {
|
||||
this.error("reserved word '" + id + "'", {
|
||||
|
|
|
@ -3263,13 +3263,20 @@
|
|||
this.body = Block.wrap([body]);
|
||||
this.own = !!source.own;
|
||||
this.object = !!source.object;
|
||||
this.from = !!source.from;
|
||||
if (this.from && this.index) {
|
||||
this.index.error('cannot use index with for-from');
|
||||
}
|
||||
if (this.own && !this.object) {
|
||||
source.ownTag.error("cannot use own with for-" + (this.from ? 'from' : 'in'));
|
||||
}
|
||||
if (this.object) {
|
||||
ref3 = [this.index, this.name], this.name = ref3[0], this.index = ref3[1];
|
||||
}
|
||||
if (this.index instanceof Value) {
|
||||
this.index.error('index cannot be a pattern matching expression');
|
||||
}
|
||||
this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length;
|
||||
this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length && !this.from;
|
||||
this.pattern = this.name instanceof Value;
|
||||
if (this.range && this.index) {
|
||||
this.index.error('indexes do not apply to range loops');
|
||||
|
@ -3277,9 +3284,6 @@
|
|||
if (this.range && this.pattern) {
|
||||
this.name.error('cannot pattern match over range loops');
|
||||
}
|
||||
if (this.own && !this.object) {
|
||||
this.name.error('cannot use own with for-in');
|
||||
}
|
||||
this.returns = false;
|
||||
}
|
||||
|
||||
|
@ -3307,10 +3311,18 @@
|
|||
if (this.returns) {
|
||||
rvar = scope.freeVariable('results');
|
||||
}
|
||||
ivar = (this.object && index) || scope.freeVariable('i', {
|
||||
single: true
|
||||
});
|
||||
kvar = (this.range && name) || index || ivar;
|
||||
if (this.from) {
|
||||
if (this.pattern) {
|
||||
ivar = scope.freeVariable('x', {
|
||||
single: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ivar = (this.object && index) || scope.freeVariable('i', {
|
||||
single: true
|
||||
});
|
||||
}
|
||||
kvar = ((this.range || this.from) && name) || index || ivar;
|
||||
kvarAssign = kvar !== ivar ? kvar + " = " : "";
|
||||
if (this.step && !this.range) {
|
||||
ref4 = this.cacheToCodeFragments(this.step.cache(o, LEVEL_LIST, isComplexOrAssignable)), step = ref4[0], stepVar = ref4[1];
|
||||
|
@ -3338,10 +3350,10 @@
|
|||
defPart += "" + this.tab + (ref = scope.freeVariable('ref')) + " = " + svar + ";\n";
|
||||
svar = ref;
|
||||
}
|
||||
if (name && !this.pattern) {
|
||||
if (name && !this.pattern && !this.from) {
|
||||
namePart = name + " = " + svar + "[" + kvar + "]";
|
||||
}
|
||||
if (!this.object) {
|
||||
if (!this.object && !this.from) {
|
||||
if (step !== stepVar) {
|
||||
defPart += "" + this.tab + step + ";\n";
|
||||
}
|
||||
|
@ -3385,7 +3397,7 @@
|
|||
}
|
||||
}
|
||||
if (this.pattern) {
|
||||
body.expressions.unshift(new Assign(this.name, new Literal(svar + "[" + kvar + "]")));
|
||||
body.expressions.unshift(new Assign(this.name, this.from ? new IdentifierLiteral(kvar) : new Literal(svar + "[" + kvar + "]")));
|
||||
}
|
||||
defPartFragments = [].concat(this.makeCode(defPart), this.pluckDirectCall(o, body));
|
||||
if (namePart) {
|
||||
|
@ -3396,11 +3408,13 @@
|
|||
if (this.own) {
|
||||
guardPart = "\n" + idt1 + "if (!" + (utility('hasProp', o)) + ".call(" + svar + ", " + kvar + ")) continue;";
|
||||
}
|
||||
} else if (this.from) {
|
||||
forPartFragments = [this.makeCode(kvar + " of " + svar)];
|
||||
}
|
||||
bodyFragments = body.compileToFragments(merge(o, {
|
||||
indent: idt1
|
||||
}), LEVEL_TOP);
|
||||
if (bodyFragments && (bodyFragments.length > 0)) {
|
||||
if (bodyFragments && bodyFragments.length > 0) {
|
||||
bodyFragments = [].concat(this.makeCode("\n"), bodyFragments, this.makeCode("\n"));
|
||||
}
|
||||
return [].concat(defPartFragments, this.makeCode("" + (resultPart || '') + this.tab + "for ("), forPartFragments, this.makeCode(") {" + guardPart + varPart), bodyFragments, this.makeCode(this.tab + "}" + (returnResult || '')));
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -371,7 +371,7 @@
|
|||
Rewriter.prototype.fixOutdentLocationData = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var prevLocationData;
|
||||
if (!(token[0] === 'OUTDENT' || (token.generated && token[0] === 'CALL_END'))) {
|
||||
if (!(token[0] === 'OUTDENT' || (token.generated && token[0] === 'CALL_END') || (token.generated && token[0] === '}'))) {
|
||||
return 1;
|
||||
}
|
||||
prevLocationData = tokens[i - 1][2];
|
||||
|
|
|
@ -570,12 +570,12 @@ grammar =
|
|||
ForBody: [
|
||||
o 'FOR Range', -> source: (LOC(2) new Value($2))
|
||||
o 'FOR Range BY Expression', -> source: (LOC(2) new Value($2)), step: $4
|
||||
o 'ForStart ForSource', -> $2.own = $1.own; $2.name = $1[0]; $2.index = $1[1]; $2
|
||||
o 'ForStart ForSource', -> $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2
|
||||
]
|
||||
|
||||
ForStart: [
|
||||
o 'FOR ForVariables', -> $2
|
||||
o 'FOR OWN ForVariables', -> $3.own = yes; $3
|
||||
o 'FOR OWN ForVariables', -> $3.own = yes; $3.ownTag = (LOC(2) new Literal($2)); $3
|
||||
]
|
||||
|
||||
# An array of all accepted values for a variable inside the loop.
|
||||
|
@ -606,6 +606,8 @@ grammar =
|
|||
o 'FORIN Expression BY Expression', -> source: $2, step: $4
|
||||
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
|
||||
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
|
||||
o 'FORFROM Expression', -> source: $2, from: yes
|
||||
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
|
||||
]
|
||||
|
||||
Switch: [
|
||||
|
@ -728,7 +730,7 @@ operators = [
|
|||
['nonassoc', 'INDENT', 'OUTDENT']
|
||||
['right', 'YIELD']
|
||||
['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
|
||||
['right', 'FORIN', 'FOROF', 'BY', 'WHEN']
|
||||
['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN']
|
||||
['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT']
|
||||
['left', 'POST_IF']
|
||||
]
|
||||
|
|
|
@ -43,7 +43,7 @@ exports.Lexer = class Lexer
|
|||
@indentLiteral = '' # The indentation
|
||||
@ends = [] # The stack for pairing up tokens.
|
||||
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
|
||||
@seenFor = no # Used to recognize FORIN and FOROF tokens.
|
||||
@seenFor = no # Used to recognize FORIN, FOROF and FORFROM tokens.
|
||||
@seenImport = no # Used to recognize IMPORT FROM? AS? tokens.
|
||||
@seenExport = no # Used to recognize EXPORT FROM? AS? tokens.
|
||||
@exportSpecifierList = no # Used to identify when in an EXPORT {...} FROM? ...
|
||||
|
@ -166,6 +166,9 @@ exports.Lexer = class Lexer
|
|||
if @value() is '!'
|
||||
poppedToken = @tokens.pop()
|
||||
id = '!' + id
|
||||
else if tag is 'IDENTIFIER' and @seenFor and id is 'from'
|
||||
tag = 'FORFROM'
|
||||
@seenFor = no
|
||||
|
||||
if tag is 'IDENTIFIER' and id in RESERVED
|
||||
@error "reserved word '#{id}'", length: id.length
|
||||
|
|
|
@ -2245,13 +2245,15 @@ exports.For = class For extends While
|
|||
@body = Block.wrap [body]
|
||||
@own = !!source.own
|
||||
@object = !!source.object
|
||||
@from = !!source.from
|
||||
@index.error 'cannot use index with for-from' if @from and @index
|
||||
source.ownTag.error "cannot use own with for-#{if @from then 'from' else 'in'}" if @own and not @object
|
||||
[@name, @index] = [@index, @name] if @object
|
||||
@index.error 'index cannot be a pattern matching expression' if @index instanceof Value
|
||||
@range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length
|
||||
@range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length and not @from
|
||||
@pattern = @name instanceof Value
|
||||
@index.error 'indexes do not apply to range loops' if @range and @index
|
||||
@name.error 'cannot pattern match over range loops' if @range and @pattern
|
||||
@name.error 'cannot use own with for-in' if @own and not @object
|
||||
@returns = false
|
||||
|
||||
children: ['body', 'source', 'guard', 'step']
|
||||
|
@ -2271,8 +2273,11 @@ exports.For = class For extends While
|
|||
scope.find(name) if name and not @pattern
|
||||
scope.find(index) if index
|
||||
rvar = scope.freeVariable 'results' if @returns
|
||||
ivar = (@object and index) or scope.freeVariable 'i', single: true
|
||||
kvar = (@range and name) or index or ivar
|
||||
if @from
|
||||
ivar = scope.freeVariable 'x', single: true if @pattern
|
||||
else
|
||||
ivar = (@object and index) or scope.freeVariable 'i', single: true
|
||||
kvar = ((@range or @from) and name) or index or ivar
|
||||
kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
|
||||
if @step and not @range
|
||||
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST, isComplexOrAssignable
|
||||
|
@ -2290,9 +2295,9 @@ exports.For = class For extends While
|
|||
if (name or @own) and @source.unwrap() not instanceof IdentifierLiteral
|
||||
defPart += "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
|
||||
svar = ref
|
||||
if name and not @pattern
|
||||
if name and not @pattern and not @from
|
||||
namePart = "#{name} = #{svar}[#{kvar}]"
|
||||
if not @object
|
||||
if not @object and not @from
|
||||
defPart += "#{@tab}#{step};\n" if step isnt stepVar
|
||||
down = stepNum < 0
|
||||
lvar = scope.freeVariable 'len' unless @step and stepNum? and down
|
||||
|
@ -2311,7 +2316,7 @@ exports.For = class For extends While
|
|||
increment = "#{ivar} += #{stepVar}"
|
||||
else
|
||||
increment = "#{if kvar isnt ivar then "++#{ivar}" else "#{ivar}++"}"
|
||||
forPartFragments = [@makeCode("#{declare}; #{compare}; #{kvarAssign}#{increment}")]
|
||||
forPartFragments = [@makeCode("#{declare}; #{compare}; #{kvarAssign}#{increment}")]
|
||||
if @returns
|
||||
resultPart = "#{@tab}#{rvar} = [];\n"
|
||||
returnResult = "\n#{@tab}return #{rvar};"
|
||||
|
@ -2322,14 +2327,16 @@ exports.For = class For extends While
|
|||
else
|
||||
body = Block.wrap [new If @guard, body] if @guard
|
||||
if @pattern
|
||||
body.expressions.unshift new Assign @name, new Literal "#{svar}[#{kvar}]"
|
||||
body.expressions.unshift new Assign @name, if @from then new IdentifierLiteral kvar else new Literal "#{svar}[#{kvar}]"
|
||||
defPartFragments = [].concat @makeCode(defPart), @pluckDirectCall(o, body)
|
||||
varPart = "\n#{idt1}#{namePart};" if namePart
|
||||
if @object
|
||||
forPartFragments = [@makeCode("#{kvar} in #{svar}")]
|
||||
forPartFragments = [@makeCode("#{kvar} in #{svar}")]
|
||||
guardPart = "\n#{idt1}if (!#{utility 'hasProp', o}.call(#{svar}, #{kvar})) continue;" if @own
|
||||
else if @from
|
||||
forPartFragments = [@makeCode("#{kvar} of #{svar}")]
|
||||
bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
|
||||
if bodyFragments and (bodyFragments.length > 0)
|
||||
if bodyFragments and bodyFragments.length > 0
|
||||
bodyFragments = [].concat @makeCode("\n"), bodyFragments, @makeCode("\n")
|
||||
[].concat defPartFragments, @makeCode("#{resultPart or ''}#{@tab}for ("),
|
||||
forPartFragments, @makeCode(") {#{guardPart}#{varPart}"), bodyFragments,
|
||||
|
|
|
@ -375,7 +375,8 @@ class exports.Rewriter
|
|||
fixOutdentLocationData: ->
|
||||
@scanTokens (token, i, tokens) ->
|
||||
return 1 unless token[0] is 'OUTDENT' or
|
||||
(token.generated and token[0] is 'CALL_END')
|
||||
(token.generated and token[0] is 'CALL_END') or
|
||||
(token.generated and token[0] is '}')
|
||||
prevLocationData = tokens[i - 1][2]
|
||||
token[2] =
|
||||
first_line: prevLocationData.last_line
|
||||
|
|
|
@ -114,3 +114,42 @@ test "splat extraction from generators", ->
|
|||
yield 2
|
||||
yield 3
|
||||
arrayEq [ gen()... ], [ 1, 2, 3 ]
|
||||
|
||||
test "for-from loops over Array", ->
|
||||
array1 = [50, 30, 70, 20]
|
||||
array2 = []
|
||||
for x from array1
|
||||
array2.push(x)
|
||||
arrayEq array1, array2
|
||||
|
||||
array1 = [[20, 30], [40, 50]]
|
||||
array2 = []
|
||||
for [a, b] from array1
|
||||
array2.push(b)
|
||||
array2.push(a)
|
||||
arrayEq array2, [30, 20, 50, 40]
|
||||
|
||||
array1 = [{a: 10, b: 20, c: 30}, {a: 40, b: 50, c: 60}]
|
||||
array2 = []
|
||||
for {a: a, b, c: d} from array1
|
||||
array2.push([a, b, d])
|
||||
arrayEq array2, [[10, 20, 30], [40, 50, 60]]
|
||||
|
||||
array1 = [[10, 20, 30, 40, 50]]
|
||||
for [a, b..., c] from array1
|
||||
eq 10, a
|
||||
arrayEq [20, 30, 40], b
|
||||
eq 50, c
|
||||
|
||||
test "for-from comprehensions over Array", ->
|
||||
array1 = (x + 10 for x from [10, 20, 30])
|
||||
ok array1.join(' ') is '20 30 40'
|
||||
|
||||
array2 = (x for x from [30, 41, 57] when x %% 3 is 0)
|
||||
ok array2.join(' ') is '30 57'
|
||||
|
||||
array1 = (b + 5 for [a, b] from [[20, 30], [40, 50]])
|
||||
ok array1.join(' ') is '35 55'
|
||||
|
||||
array2 = (a + b for [a, b] from [[10, 20], [30, 40], [50, 60]] when a + b >= 70)
|
||||
ok array2.join(' ') is '70 110'
|
||||
|
|
|
@ -12,7 +12,7 @@ test "ensure that carriage returns don't break compilation on Windows", ->
|
|||
test "#3089 - don't mutate passed in options to compile", ->
|
||||
opts = {}
|
||||
CoffeeScript.compile '1 + 1', opts
|
||||
ok !opts.scope
|
||||
ok !opts.scope
|
||||
|
||||
test "--bare", ->
|
||||
eq -1, CoffeeScript.compile('x = y', bare: on).indexOf 'function'
|
||||
|
|
|
@ -1192,7 +1192,7 @@ test "function cannot contain both `await` and `yield from`", ->
|
|||
^^^^^^^
|
||||
'''
|
||||
|
||||
test "Cannnot have `await` outside a function", ->
|
||||
test "cannot have `await` outside a function", ->
|
||||
assertErrorFormat '''
|
||||
await 1
|
||||
''', '''
|
||||
|
@ -1200,3 +1200,17 @@ test "Cannnot have `await` outside a function", ->
|
|||
await 1
|
||||
^^^^^^^
|
||||
'''
|
||||
|
||||
test "indexes are not supported in for-from loops", ->
|
||||
assertErrorFormat "x for x, i from [1, 2, 3]", '''
|
||||
[stdin]:1:10: error: cannot use index with for-from
|
||||
x for x, i from [1, 2, 3]
|
||||
^
|
||||
'''
|
||||
|
||||
test "own is not supported in for-from loops", ->
|
||||
assertErrorFormat "x for own x from [1, 2, 3]", '''
|
||||
[stdin]:1:7: error: cannot use own with for-from
|
||||
x for own x from [1, 2, 3]
|
||||
^^^
|
||||
'''
|
||||
|
|
|
@ -232,3 +232,41 @@ test "yield handles 'this' correctly", ->
|
|||
ok z.done is false
|
||||
|
||||
throws -> y.next new Error "boom"
|
||||
|
||||
test "for-from loops over generators", ->
|
||||
array1 = [50, 30, 70, 20]
|
||||
gen = -> yield from array1
|
||||
|
||||
array2 = []
|
||||
array3 = []
|
||||
array4 = []
|
||||
|
||||
iterator = gen()
|
||||
for x from iterator
|
||||
array2.push(x)
|
||||
break if x is 30
|
||||
|
||||
for x from iterator
|
||||
array3.push(x)
|
||||
|
||||
for x from iterator
|
||||
array4.push(x)
|
||||
|
||||
arrayEq array2, [50, 30]
|
||||
# Different JS engines have different opinions on the value of array3:
|
||||
# https://github.com/jashkenas/coffeescript/pull/4306#issuecomment-257066877
|
||||
# As a temporary measure, either result is accepted.
|
||||
ok array3.length is 0 or array3.join(',') is '70,20'
|
||||
arrayEq array4, []
|
||||
|
||||
|
||||
test "for-from comprehensions over generators", ->
|
||||
gen = ->
|
||||
yield from [30, 41, 51, 60]
|
||||
|
||||
iterator = gen()
|
||||
array1 = (x for x from iterator when x %% 2 is 1)
|
||||
array2 = (x for x from iterator)
|
||||
|
||||
ok array1.join(' ') is '41 51'
|
||||
ok array2.length is 0
|
||||
|
|
|
@ -515,6 +515,27 @@ test "Verify OUTDENT and CALL_END tokens are located at the end of the previous
|
|||
eq token[0], 'CALL_END'
|
||||
assertAtCloseCurly(token)
|
||||
|
||||
test "Verify generated } tokens are located at the end of the previous token", ->
|
||||
source = '''
|
||||
a(b, ->
|
||||
c: () ->
|
||||
if d
|
||||
e
|
||||
)
|
||||
'''
|
||||
tokens = CoffeeScript.tokens source
|
||||
[..., identifier, outdent1, outdent2, closeCurly, outdent3, callEnd,
|
||||
terminator] = tokens
|
||||
eq identifier[0], 'IDENTIFIER'
|
||||
assertAtIdentifier = (token) ->
|
||||
eq token[2].first_line, identifier[2].last_line
|
||||
eq token[2].first_column, identifier[2].last_column
|
||||
eq token[2].last_line, identifier[2].last_line
|
||||
eq token[2].last_column, identifier[2].last_column
|
||||
|
||||
for token in [outdent1, outdent2, closeCurly, outdent3]
|
||||
assertAtIdentifier(token)
|
||||
|
||||
test "Verify real CALL_END tokens have the right position", ->
|
||||
source = '''
|
||||
a()
|
||||
|
|
|
@ -75,6 +75,20 @@ test "large ranges are generated with looping constructs", ->
|
|||
eq 100, (len = up.length)
|
||||
eq 99, up[len - 1]
|
||||
|
||||
test "for-from loops over ranges", ->
|
||||
array1 = []
|
||||
for x from [20..30]
|
||||
array1.push(x)
|
||||
break if x is 25
|
||||
arrayEq array1, [20, 21, 22, 23, 24, 25]
|
||||
|
||||
test "for-from comprehensions over ranges", ->
|
||||
array1 = (x + 10 for x from [20..25])
|
||||
ok array1.join(' ') is '30 31 32 33 34 35'
|
||||
|
||||
array2 = (x for x from [20..30] when x %% 2 == 0)
|
||||
ok array2.join(' ') is '20 22 24 26 28 30'
|
||||
|
||||
test "#1012 slices with arguments object", ->
|
||||
expected = [0..9]
|
||||
argsAtStart = (-> [arguments[0]..9]) 0
|
||||
|
|
|
@ -41,6 +41,10 @@ test "loop variable should be accessible after for-in loop", ->
|
|||
d = (x for x in [1,2])
|
||||
eq x, 2
|
||||
|
||||
test "loop variable should be accessible after for-from loop", ->
|
||||
d = (x for x from [1,2])
|
||||
eq x, 2
|
||||
|
||||
class Array then slice: fail # needs to be global
|
||||
class Object then hasOwnProperty: fail
|
||||
test "#1973: redefining Array/Object constructors shouldn't confuse __X helpers", ->
|
||||
|
|
Loading…
Reference in New Issue