Skip to content

Commit

Permalink
allow multiple value assignment "a,b,c = x" with any iterable
Browse files Browse the repository at this point in the history
this required some ugly manual hacks to ensure performance

fix a parser bug in "function (x::T)"
improve lowering of "a,b = c,d"
  • Loading branch information
JeffBezanson committed Oct 29, 2012
1 parent 4cc87c7 commit 2f046bd
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 49 deletions.
38 changes: 27 additions & 11 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ t_func[convert_tuple] =
end;
isType(t) ? static_convert(t.parameters[1],x) :
Any))
typeof_tfunc = function (t)
const typeof_tfunc = function (t)
if isType(t)
t = t.parameters[1]
if isa(t,TypeVar)
Expand Down Expand Up @@ -223,7 +223,7 @@ t_func[typeassert] =
tintersect(v,map(t->t.parameters[1],t)) :
Any))

tupleref_tfunc = function (A, t, i)
const tupleref_tfunc = function (A, t, i)
if is(t,())
return None
end
Expand Down Expand Up @@ -262,7 +262,7 @@ tupleref_tfunc = function (A, t, i)
end
t_func[tupleref] = (2, 2, tupleref_tfunc)

getfield_tfunc = function (A, s, name)
const getfield_tfunc = function (A, s, name)
if isType(s)
s = typeof(s.parameters[1])
if s === TypeVar
Expand Down Expand Up @@ -293,7 +293,7 @@ getfield_tfunc = function (A, s, name)
end
t_func[getfield] = (2, 2, getfield_tfunc)
t_func[setfield] = (3, 3, (o, f, v)->v)
fieldtype_tfunc = function (A, s, name)
const fieldtype_tfunc = function (A, s, name)
if !isa(s,CompositeKind)
return Type
end
Expand All @@ -308,7 +308,7 @@ t_func[Expr] = (3, 3, (a,b,c)->Expr)
t_func[Box] = (1, 1, (a,)->Box)

# TODO: handle e.g. apply_type(T, R::Union(Type{Int32},Type{Float64}))
apply_type_tfunc = function (A, args...)
const apply_type_tfunc = function (A, args...)
if !isType(args[1])
return Any
end
Expand Down Expand Up @@ -431,24 +431,24 @@ const isconstantref = isconstantfunc

isvatuple(t::Tuple) = (n = length(t); n > 0 && isseqtype(t[n]))

limit_tuple_depth(t) = limit_tuple_depth(t,0)
const limit_tuple_depth = t->limit_tuple_depth_(t,0)

function limit_tuple_depth(t,d)
const limit_tuple_depth_ = function (t,d::Int)
if isa(t,UnionKind)
# also limit within Union types.
# may have to recur into other stuff in the future too.
return Union(limit_tuple_depth(t.types,d)...)
return Union(limit_tuple_depth_(t.types,d)...)
end
if !isa(t,Tuple)
return t
end
if d > MAX_TUPLE_DEPTH
return Tuple
end
map(x->limit_tuple_depth(x,d+1), t)
map(x->limit_tuple_depth_(x,d+1), t)
end

function limit_tuple_type(t)
const limit_tuple_type = function (t::Tuple)
n = length(t)
if n > MAX_TUPLETYPE_LEN
last = t[n]
Expand All @@ -463,6 +463,20 @@ function limit_tuple_type(t)
end

function abstract_call_gf(f, fargs, argtypes, e)
if length(argtypes)>1 && isa(argtypes[1],Tuple) && argtypes[2]===Int
# allow tuple indexing functions to take advantage of constant
# index arguments.
if f === Main.Base.ref
e.head = :call1
return tupleref_tfunc(fargs, argtypes[1], argtypes[2])
elseif f === Main.Base.next
e.head = :call1
return (tupleref_tfunc(fargs, argtypes[1], argtypes[2]), Int)
elseif f === Main.Base.indexed_next
e.head = :call1
return (tupleref_tfunc(fargs, argtypes[1], argtypes[2]), Int)
end
end
# don't consider more than N methods. this trades off between
# compiler performance and generated code performance.
# typically, considering many methods means spending lots of time
Expand Down Expand Up @@ -1724,7 +1738,9 @@ function add_variable(ast, name, typ)
end

const some_names = {:_var0, :_var1, :_var2, :_var3, :_var4, :_var5, :_var6,
:_var7, :_var8, :_var9, :_var10, :_var11, :_var12}
:_var7, :_var8, :_var9, :_var10, :_var11, :_var12,
:_var13, :_var14, :_var15, :_var16, :_var17, :_var18,
:_var19, :_var20, :_var21, :_var22, :_var23, :_var24}

function unique_name(ast)
locllist = ast.args[2][1]::Array{Any,1}
Expand Down
9 changes: 7 additions & 2 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ ref(t::Tuple, r::Ranges) = tuple([t[ri] for ri in r]...)
## iterating ##

start(t::Tuple) = 1
done(t::Tuple, i) = (length(t) < i)
next(t::Tuple, i) = (t[i], i+1)
done(t::Tuple, i::Int) = (length(t) < i)
next(t::Tuple, i::Int) = (t[i], i+1)

# this allows partial evaluation of bounded sequences of next() calls on tuples,
# while reducing to plain next() for arbitrary iterables.
indexed_next(t::Tuple, i::Int, state) = (t[i], i+1)
indexed_next(I, i, state) = next(I, state)

## mapping ##

Expand Down
4 changes: 3 additions & 1 deletion src/julia-parser.scm
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,9 @@
((function macro)
(let* ((paren (eqv? (require-token s) #\())
(sig (parse-call s))
(def (if (symbol? sig)
(def (if (or (symbol? sig)
(and (pair? sig) (eq? (car sig) '|::|)
(symbol? (cadr sig))))
(if paren
;; in "function (x)" the (x) is a tuple
`(tuple ,sig)
Expand Down
83 changes: 48 additions & 35 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -674,11 +674,31 @@

;; convert (lhss...) = (tuple ...) to assignments, eliminating the tuple
(define (tuple-to-assignments lhss x)
(let ((temps (map (lambda (x) (gensy)) (cdr x))))
`(block
,@(map make-assignment temps (cdr x))
,@(map make-assignment lhss temps)
(unnecessary-tuple (tuple ,@temps)))))
(let loop ((lhss lhss)
(rhss (cdr x))
(stmts '())
(after '())
(elts '()))
(if (null? lhss)
`(block ,@(reverse stmts)
,@(reverse after)
(unnecessary-tuple (tuple ,@(reverse elts))))
(let ((L (car lhss))
(R (car rhss)))
(if (and (symbol? L)
;; overwrite var immediately if it doesn't occur elsewhere
(not (contains (lambda (e) (eq? e L)) (cdr rhss))))
(loop (cdr lhss)
(cdr rhss)
(cons (make-assignment L R) stmts)
after
(cons L elts))
(let ((temp (gensy)))
(loop (cdr lhss)
(cdr rhss)
(cons (make-assignment temp R) stmts)
(cons (make-assignment L temp) after)
(cons temp elts))))))))

;; convert (lhss...) = x to tuple indexing, handling the general case
(define (lower-tuple-assignment lhss x)
Expand Down Expand Up @@ -736,35 +756,27 @@

(pattern-lambda (comparison . chain) (expand-compare-chain chain))

;; multiple value assignment a,b = x...
(pattern-lambda (= (tuple . lhss) (... x))
(let* ((xx (if (symbol? x) x (gensy)))
(ini (if (eq? x xx) '() `((= ,xx ,x))))
(st (gensy)))
(if
(and (pair? x) (eq? (car x) 'tuple))
`(= (tuple ,@lhss) ,x)
`(block
,@ini
(= ,st (call (top start) ,xx))
,.(apply append
(map (lambda (lhs)
`((if (call (top done) ,xx ,st)
(call (top throw)
(call (top BoundsError))))
(= (tuple ,lhs ,st)
(call (top next) ,xx ,st))))
lhss))
,xx))))

;; multiple value assignment
(pattern-lambda (= (tuple . lhss) x)
(if (and (pair? x) (pair? lhss) (eq? (car x) 'tuple)
(length= lhss (length (cdr x))))
;; (a, b, ...) = (x, y, ...)
(tuple-to-assignments lhss x)
;; (a, b, ...) = other
(lower-tuple-assignment lhss x)))
;; multiple value assignment a,b = x
(pattern-lambda
(= (tuple . lhss) x)
(if (and (pair? x) (pair? lhss) (eq? (car x) 'tuple)
(length= lhss (length (cdr x))))
;; (a, b, ...) = (x, y, ...)
(tuple-to-assignments lhss x)
;; (a, b, ...) = other
(let* ((xx (if (symbol? x) x (gensy)))
(ini (if (eq? x xx) '() `((= ,xx ,x))))
(st (gensy)))
`(block
,@ini
(= ,st (call (top start) ,xx))
,.(map (lambda (i lhs)
(lower-tuple-assignment
(list lhs st)
`(call (|.| (top Base) (quote indexed_next)) ,xx ,(+ i 1) ,st)))
(iota (length lhss))
lhss)
,xx))))

(pattern-lambda (= (ref a . idxs) rhs)
(let* ((reuse (and (pair? a)
Expand Down Expand Up @@ -960,7 +972,8 @@
(= ,state (call (top start) ,coll))
(while (call (top !) (call (top done) ,coll ,state))
(block
(= (tuple ,lhs ,state) (call (top next) ,coll ,state))
,(lower-tuple-assignment (list lhs state)
`(call (top next) ,coll ,state))
,body))))))

; update operators
Expand Down

0 comments on commit 2f046bd

Please sign in to comment.