diff --git a/lib/lexer.js b/lib/lexer.js index bf07e146d..7f506d26a 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -1227,7 +1227,7 @@ function addImplicitBraces(tokens){ } } function expandLiterals(tokens){ - var i, token, sig, lno, from, char, to, tochar, by, ts, n, word, that, __ref, __step, __i, __len; + var i, token, sig, lno, from, char, to, tochar, by, byp, ts, n, word, that, __ref, __i, __len; i = 0; while (token = tokens[++i]) { switch (token[0]) { @@ -1244,16 +1244,22 @@ function expandLiterals(tokens){ __ref = decode(token[1], lno = token[2]), from = __ref[0], char = __ref[1]; __ref = decode(tokens[i + 1][1], lno), to = __ref[0], tochar = __ref[1]; if (char ^ tochar) { - carp('bad "to" in range'); + carp('bad "to" in range', lno); } - if (by = ((__ref = tokens[i + 2]) != null ? __ref[0] : void 8) === 'RANGE_BY') { - if (isNaN(by = (__ref = tokens[i + 3]) != null ? __ref[1] : void 8)) { - carp('bad "by" in range'); + by = 1; + if (byp = ((__ref = tokens[i + 2]) != null ? __ref[0] : void 8) === 'RANGE_BY') { + if (!(by = +((__ref = tokens[i + 3]) != null ? __ref[1] : void 8))) { + carp('bad "by" in range', tokens[i + 2][2]); + } + } + if (token.op === 'til') { + to -= by / 2; + if (from > to ^ by < 0) { + to = from; } } ts = []; - to -= token.op === 'til' && 1e-15; - for (n = from, __step = +by || 1; __step < 0 ? n >= to : n <= to; n += __step) { + for (n = from; by < 0 ? n >= to : n <= to; n += by) { if (0x10000 < ts.push([ 'STRNUM', char ? character(n) @@ -1263,7 +1269,7 @@ function expandLiterals(tokens){ } } ts.pop() || carp('empty range', lno); - tokens.splice.apply(tokens, [i, by ? 4 : 2].concat(__slice.call(ts))); + tokens.splice.apply(tokens, [i, 2 + 2 * byp].concat(__slice.call(ts))); i += ts.length - 1; break; case 'WORDS': diff --git a/src/lexer.co b/src/lexer.co index b593e0588..65cae2d25 100644 --- a/src/lexer.co +++ b/src/lexer.co @@ -827,17 +827,21 @@ character = if JSON!? then uxxxx else -> tokens.splice i++ 0 [\+- sig, token.2] continue if token.callable case \RANGE - [from, char] = decode token.1, lno = token.2 + [from, char] = decode token. 1, lno = token.2 [to, tochar] = decode tokens[i+1]1, lno - carp 'bad "to" in range' if char ^ tochar - if by = tokens[i+2]?0 is \RANGE_BY - carp 'bad "by" in range' if isNaN by = tokens[i+3]?1 - ts = []; to -= token.op is \til and 1e-15 - for n from from to to by +by or 1 + carp 'bad "to" in range' lno if char ^ tochar + by = 1 + if byp = tokens[i+2]?0 is \RANGE_BY + carp 'bad "by" in range' tokens[i+2]2 unless by = +tokens[i+3]?1 + if token.op is \til + to -= by / 2 + to = from if from > to ^ by < 0 + ts = [] + for n from from to to by by carp 'range limit exceeded' lno if 0x10000 < ts.push \ [\STRNUM, if char then character n else "#n", lno] [\, \, lno] ts.pop! or carp 'empty range' lno - tokens.splice i, if by then 4 else 2, ...ts + tokens.splice i, 2 + 2 * byp, ...ts i += ts.length - 1 case \WORDS ts = [[\[ \[ lno = token.2]] diff --git a/test/literal.co b/test/literal.co index d23ea7ee8..0e60adb99 100644 --- a/test/literal.co +++ b/test/literal.co @@ -20,16 +20,6 @@ eq 36, 0XF + 36RT eq 100m2, 10m ** 2 eq 3000c, 30$ * 100 -# [#31](https://github.com/satyr/coco/issues/31): Numeric Ranges -eq '1,2,3' String [1 to +3] -eq '1,0,-1' String Array 1 to -1 by -1 - -til = String -eq 2, [Number]0 til 2 - -throws 'range limit exceeded on line 1' -> Coco.tokens '0 to 1 by 1e-5' -throws 'empty range on line 3' -> Coco.tokens '\n\n 1 to 0' - # [coffee#764](https://github.com/jashkenas/coffee-script/issues/764) # Boolean/Number should be indexable. ok 42['toString'] @@ -419,5 +409,33 @@ eq o.3, 3 eq o.5, 5 +### Numeric/Character Ranges +show = -> @@ * ' ' + +eq '-1 0 1 2' show -1 to +2 +eq '1 0 -1' show +1 to -1 by -1 + +eq '999 1000' show 999 til 1001 +eq '1e-9' show 1e-9 til 1e-8 +eq '9999999' show 9999999 til 1e7 +eq '10000000' show 1e7 til 9999999 by -1 + +eq '0.5 0.75 1' show 0.5 til 1.2 by 0.25 + +til = String +eq 2, [Number]0 til 2 + +eq 'A F K P U Z' show \A to \Z by 5 +eq 'y u q m i e' show \y til \a by -4 + +ok \\u2028 to \\u2029 + +throws 'range limit exceeded on line 2' -> Coco.tokens '\n0 to 1 by 1e-5' +throws 'empty range on line 2' -> Coco.tokens '\n1 to 0' +throws 'bad "to" in range on line 2' -> Coco.tokens '\n0 to "q"' +throws 'bad "by" in range on line 2' -> Coco.tokens '\n0 to 9 by "2"' +throws 'bad string in range on line 2' -> Coco.tokens '\n"a" to "bc"' + + ### Misc throws \unimplemented -> ... diff --git a/test/string.co b/test/string.co index cbb450152..4f746633d 100644 --- a/test/string.co +++ b/test/string.co @@ -205,14 +205,6 @@ eq('+', \+) eq '\\', [\\\].0 eq '$', {\$}.\$ -eq 'AFKPUZ' [\A to \Z by 5]*'' - -throws 'bad "to" in range on line 1' -> Coco.tokens '0 to "q"' -throws 'bad "by" in range on line 1' -> Coco.tokens '0 to 9 by "2"' -throws 'bad string in range on line 1' -> Coco.tokens '"a" to "bc"' - -ok \\u2028 to \\u2029 - # [coffee#923](https://github.com/jashkenas/coffee-script/issues/923) eq "#{ "{" }", "{"