Skip to content

Commit

Permalink
Merge pull request #165 from maxtaco/zapu-fix_iced_loops
Browse files Browse the repository at this point in the history
Zapu fix iced loops
  • Loading branch information
maxtaco committed Oct 9, 2015
2 parents 0a28c88 + 0b7345f commit c4ca0bb
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 25 deletions.
67 changes: 53 additions & 14 deletions lib/coffee-script/nodes.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 63 additions & 11 deletions src/nodes.coffee
Expand Up @@ -3023,13 +3023,23 @@ exports.For = class For extends While
# - Issue #99 as fixed by @davidbond

# Create variables
begin = new Value new Literal "_begin"
end = new Value new Literal "_end"
positive = new Value new Literal "_positive"
begin = new Value new Literal o.scope.freeVariable "begin"
end = new Value new Literal o.scope.freeVariable "end"
positive = new Value new Literal o.scope.freeVariable "positive"
stepVal = new Value new Literal o.scope.freeVariable "step"

# Calculate stepping
stepVal = @step or new Literal 1
step = new If positive, new Op("+=", @name, stepVal)
step.addElse new Op("-=", @name, stepVal)
if @step
a_stepVal = new Assign stepVal, @step
a_positive = new Assign positive, (new Op ">", stepVal, new Literal 0), null, { icedlocal : true }
else
is_positive = (new Op ">", end, begin)
a_stepVal = new If is_positive, new Assign stepVal, new Literal 1
a_stepVal.addElse new Assign stepVal, new Literal -1
a_positive = new Assign positive, is_positive, null, { icedlocal : true }

step = new Op '+=', @name, stepVal

# Calculate break condition
excl = if @source.base.exclusive then "=" else ''
pos = new Op "&&", new Op("===", positive, new Literal true), new Op(">#{excl}", @name, @source.base.to)
Expand All @@ -3041,7 +3051,8 @@ exports.For = class For extends While
new Assign(@name, @source.base.from)
new Assign(begin, @source.base.from, null, { icedlocal : true })
new Assign(end, @source.base.to, null, { icedlocal : true })
new Assign(positive, (new Op ">", end, begin), null, { icedlocal : true } )
a_stepVal,
a_positive,
]

# Handle the case of 'for i,blah in arr'
Expand All @@ -3054,10 +3065,51 @@ exports.For = class For extends While
a1 = new Assign ref_val, @source
len_rhs = ref_val.copy().add new Access new Value new Literal "length"
a2 = new Assign len_val, len_rhs
a3 = new Assign kval, new Value new Literal 0
init = [ a1, a2, a3 ]
condition = new Op '<', kval, len_val
step = new Op '++', kval

# Handle non-trival stepVars (negative strides etc.)
# - Issue #162

init = [ a1, a2 ]

if not @step
# No step provided, it's 1 by default
a3 = new Assign kval, new Literal 0

step = new Op '++', kval
condition = new Op '<', kval, len_val

init.push a3
else
# Check if step is a literal number
[_, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST
stepNum = stepVar.match NUMBER

if stepNum
step = new Assign kval, new Op '+', kval, @step
down = parseNum(stepNum[0]) < 0
if down
a3 = new Assign kval, new Op '-', len_val, new Literal 1
condition = new Op '>=', kval, new Literal 0
else
a3 = new Assign kval, new Literal 0
condition = new Op '<', kval, len_val

init.push a3
else
step_var = scope.freeVariable 'step'
step_var_val = new Value new Literal step_var
init_step = new Assign step_var_val, new Value @step

is_step_positive = (new Op '>', step_var_val, (new Literal 0))
start_rhs = new If is_step_positive, new Value new Literal 0
start_rhs.addElse new Op '-', len_val, new Literal 1
step = new Assign kval, new Op '+', kval, step_var_val
a3 = new Assign kval, start_rhs

condition = new Op '&&', (new Op '>=', kval, new Literal 0), (new Op '<', kval, len_val)

init.push init_step, a3

ref_val_copy = ref_val.copy()
ref_val_copy.add new Index kval
a4 = new Assign @name, ref_val_copy
Expand Down
167 changes: 167 additions & 0 deletions test/iced.coffee
Expand Up @@ -632,6 +632,173 @@ atest 'funcname with double quotes is safely emitted', (cb) ->

cb(v is 1, {})

atest 'consistent behavior of ranges with and without await', (cb) ->
arr1 = []
arr2 = []
for x in [3..0]
await delay defer()
arr1.push x

for x in [3..0]
arr2.push x

arrayEq arr1, arr2

arr1 = []
arr2 = []
for x in [3..0] by -1
await delay defer()
arr1.push x

for x in [3..0] by -1
arr2.push x

arrayEq arr1, arr2

for x in [3...0] by 1
await delay defer()
throw new Error 'Should never enter this loop'

for x in [3...0] by 1
throw new Error 'Should never enter this loop'

for x in [3..0] by 1
await delay defer()
throw new Error 'Should never enter this loop'

for x in [3..0] by 1
throw new Error 'Should never enter this loop'

for x in [0..3] by -1
throw new Error 'Should never enter this loop'
await delay defer()

for x in [0..3] by -1
throw new Error 'Should never enter this loop'

arr1 = []
arr2 = []
for x in [3..0] by -2
ok x <= 3
await delay defer()
arr1.push x

for x in [3..0] by -2
arr2.push x

arrayEq arr1, arr2

arr1 = []
arr2 = []
for x in [0..3] by 2
await delay defer()
arr1.push x

for x in [0..3] by 2
arr2.push x

arrayEq arr1, arr2

cb true, {}

atest 'loops with defers (Issue #89 via @davidbau)', (cb) ->
arr = []
for x in [0..3] by 2
await delay defer()
arr.push x
arrayEq [0, 2], arr

arr = []
for x in ['a', 'b', 'c']
await delay defer()
arr.push x
arrayEq arr, ['a', 'b', 'c']

arr = []
for x in ['a', 'b', 'c'] by 1
await delay defer()
arr.push x
arrayEq arr, ['a', 'b', 'c']

arr = []
for x in ['d', 'e', 'f', 'g'] by 2
await delay defer()
arr.push x
arrayEq arr, [ 'd', 'f' ]

arr = []
for x in ['a', 'b', 'c'] by -1
await delay defer()
arr.push x
arrayEq arr, ['c', 'b', 'a']

arr = []
step = -2
for x in ['a', 'b', 'c'] by step
await delay defer()
arr.push x
arrayEq arr, ['c', 'a']

cb true, {}

atest "nested loops with negative steps", (cb) ->
v1 = []
for i in [10...0] by -1
for j in [10...i] by -1
await delay defer()
v1.push (i*1000)+j
v2 = []
for i in [10...0] by -1
for j in [10...i] by -1
v2.push (i*1000)+j
arrayEq v1, v2
cb true, {}

atest 'loop with function as step', (cb) ->
makeFunc = ->
calld = false
return ->
if calld
throw Error 'step function called twice'

calld = true
return 1

# Basically, func should be called only once and its result should
# be used as step.

func = makeFunc()
arr = []
for x in [1,2,3] by func()
arr.push x

arrayEq [1,2,3], arr

func = makeFunc()
arr = []
for x in [1,2,3] by func()
await delay defer()
arr.push x

arrayEq [1,2,3], arr

func = makeFunc()
arr = []
for x in [1..3] by func()
arr.push x

arrayEq [1,2,3], arr

func = makeFunc()
arr = []
for x in [1..3] by func()
await delay defer()
arr.push x

arrayEq [1,2,3], arr

cb true, {}

# helper to assert that a string should fail compilation
cantCompile = (code) ->
throws -> CoffeeScript.compile code
Expand Down

0 comments on commit c4ca0bb

Please sign in to comment.