Skip to content

Commit

Permalink
allow vararg in lhs of assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
simeonschaub committed Oct 4, 2020
1 parent d2856a3 commit 490515d
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 29 deletions.
19 changes: 19 additions & 0 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,25 @@ iterate_and_index(x) = (destruct_iterate, select_first)
# to make inference's life easier.
iterate_and_index(::Nothing) = throw(MethodError(iterate, (nothing,)))

function _rest(itr, state, _)
@_inline_meta
state === () && return rest(itr)
return rest(itr, state[1][2])
end
_rest(x::Union{Array,Tuple,NamedTuple,Pair}, _, i) = (@_inline_meta; rest(x, i))

rest(t::Tuple) = t
rest(t::NTuple{N}, i::Int) where {N} = ntuple(x -> getfield(t, x+i-1), N-i+1)
rest(a::Array, i::Int=1) = a[i:end]
# The semantics of `collect` are weird. Better to write our own
function rest(a::AbstractArray{T}, state...) where {T}
v = Vector{T}(undef, 0)
# assume only very few items are taken from the front
sizehint!(v, length(a))
return foldl(push!, Iterators.rest(a, state...), init=v)
end
rest(itr, state...) = Iterators.rest(itr, state...)

# Use dispatch to avoid a branch in first
first(::Tuple{}) = throw(ArgumentError("tuple must be non-empty"))
first(t::Tuple) = t[1]
Expand Down
95 changes: 66 additions & 29 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,8 @@
,@(reverse after)
(unnecessary (tuple ,@(reverse elts))))
(let ((L (car lhss))
(R (car rhss)))
;; rhss can be null iff L is a vararg
(R (if (null? rhss) '() (car rhss))))
(cond ((and (symbol-like? L)
(or (not (pair? R)) (quoted? R) (equal? R '(null)))
;; overwrite var immediately if it doesn't occur elsewhere
Expand All @@ -1434,6 +1435,16 @@
(cons (make-assignment L R) stmts)
after
(cons R elts)))
((vararg? L)
(if (null? (cdr lhss))
(let ((temp (make-ssavalue)))
`(block ,@(reverse stmts)
(= ,temp (tuple ,@rhss))
,@(reverse after)
(= ,(cadr L) ,temp)
(unnecessary (tuple ,@(reverse elts) (... ,temp)))))
(error (string "invalid \"...\" on non-final assignment location \""
(cadr L) "\""))))
((vararg? R)
(let ((temp (make-ssavalue)))
`(block ,@(reverse stmts)
Expand Down Expand Up @@ -2035,6 +2046,7 @@
(define (sides-match? l r)
;; l and r either have equal lengths, or r has a trailing ...
(cond ((null? l) (null? r))
((vararg? (car l)) #t)
((null? r) #f)
((vararg? (car r)) (null? (cdr r)))
(else (sides-match? (cdr l) (cdr r)))))
Expand All @@ -2044,34 +2056,59 @@
(expand-forms
(tuple-to-assignments lhss x))
;; (a, b, ...) = other
(let* ((xx (if (or (and (symbol? x) (not (memq x lhss)))
(ssavalue? x))
x (make-ssavalue)))
(ini (if (eq? x xx) '() (list (sink-assignment xx (expand-forms x)))))
(n (length lhss))
(funcs (make-ssavalue))
(iterate (make-ssavalue))
(index (make-ssavalue))
(st (gensy)))
`(block
,@ini
,(lower-tuple-assignment
(list iterate index)
`(call (top iterate_and_index) ,xx))
,.(map (lambda (i lhs)
(expand-forms
`(block
(= ,st (call ,iterate
,xx ,.(if (eq? i 0) '() `(,st))))
,(if (eventually-call? lhs)
(let ((val (gensy)))
`(block
(= ,val (call ,index ,st ,(+ i 1)))
(= ,lhs ,val)))
`(= ,lhs (call ,index ,st ,(+ i 1)))))))
(iota n)
lhss)
(unnecessary ,xx))))))
(begin
;; like memq, but if last element of lhss is (... sym),
;; check against sym instead
(define (in-lhs? x lhss)
(if (null? lhss)
#f
(let ((l (car lhss)))
(cond ((and (pair? l) (eq? (car l) '|...|))
(if (null? (cdr lhss))
(eq? (cadr l) x)
(error (string "invalid \"...\" on non-final assignment location \""
(cadr l) "\""))))
((eq? l x) #t)
(else (in-lhs? x (cdr lhss)))))))
(define (gensymified-assignment lhs rhs)
(if (eventually-call? lhs)
(let ((val (gensy)))
`(block
(= ,val ,rhs)
(= ,lhs ,val)))
`(= ,lhs ,rhs)))
(let* ((xx (if (or (and (not (in-lhs? x lhss)) (symbol? x))
(ssavalue? x))
x (make-ssavalue)))
(ini (if (eq? x xx) '() (list (sink-assignment xx (expand-forms x)))))
(n (length lhss))
(funcs (make-ssavalue))
(iterate (make-ssavalue))
(index (make-ssavalue))
(st (gensy)))
`(block
,@ini
,(lower-tuple-assignment
(list iterate index)
`(call (top iterate_and_index) ,xx))
,.(map (lambda (i lhs)
(expand-forms
(if (and (pair? lhs) (eq? (car lhs) '|...|))
(gensymified-assignment
(cadr lhs)
`(call (top _rest)
,xx
,(if (eq? i 0) '(tuple) `(tuple ,st))
,(+ i 1)))
`(block
(= ,st (call ,iterate
,xx ,.(if (eq? i 0) '() `(,st))))
,(gensymified-assignment
lhs
`(call ,index ,st ,(+ i 1)))))))
(iota n)
lhss)
(unnecessary ,xx)))))))
((typed_hcat)
(error "invalid spacing in left side of indexed assignment"))
((typed_vcat)
Expand Down
51 changes: 51 additions & 0 deletions test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2306,3 +2306,54 @@ end
@test_throws ParseError("invalid operator \".<---\"") Meta.parse("1 .<--- 2")
@test_throws ParseError("invalid operator \"--\"") Meta.parse("a---b")
@test_throws ParseError("invalid operator \".--\"") Meta.parse("a.---b")

@testset "slurp in assignments" begin
res = begin x, y, z... = 1:7 end
@test res == 1:7
@test x == 1 && y == 2
@test z == Vector(3:7)

res = begin x, y, z... = [1, 2] end
@test res == [1, 2]
@test x == 1 && y == 2
@test z == Int[]

x = 1
res = begin x..., = x end
@test res == 1
@test x == 1

x, y, z... = 1:7
res = begin y, z, x... = z..., x, y end
@test res == ((3:7)..., 1, 2)
@test y == 3
@test z == 4
@test x == ((5:7)..., 1, 2)

res = begin x, _, y... = 1, 2 end
@test res == (1, 2)
@test x == 1
@test y == ()

res = begin x, y... = 1 end
@test res == 1
@test x == 1
@test y == Iterators.rest(1, nothing)

res = begin x, y, z... = 1, 2, 3:5 end
@test res == (1, 2, 3:5)
@test x == 1 && y == 2
@test z == (3:5,)

@test Meta.isexpr(Meta.@lower(begin a, b..., c = 1:3 end), :error)
@test Meta.isexpr(Meta.@lower(begin a, b..., c = 1, 2, 3 end), :error)
@test Meta.isexpr(Meta.@lower(begin a, b..., c... = 1, 2, 3 end), :error)

@test_throws BoundsError begin x, y, z... = 1:1 end
@test_throws BoundsError begin x, y, _, z... = 1, 2 end

car((a, d...)) = a
cdr((a, d...)) = d
@test car(1:3) == 1
@test cdr(1:3) == [2, 3]
end

0 comments on commit 490515d

Please sign in to comment.