on master help sign in
Merge branch 'master' into webgl
commit069c382dfb6d63046bde781b51916663b01c9cbf authorJohn P Mayer Jr <john.p.mayer.jr@gmail.com> dateMon 31 Mar 2014, 22:28 (13 years ago) parentbd135f3ad5bdaa
6 files changed+716−25
compiler/Generate/Cases.hs+14−11 compiler/Generate/JavaScript.hs+8−5 libraries/Array.elm+190 libraries/Native/Array.js+491 libraries/Native/Graphics/Input.js+9−9 libraries/Native/Show.js+4
compiler/Generate/Cases.hs+14−11
@@ -13,7 +13,6 @@ import qualified SourceSyntax.Pattern as P
1313import qualified SourceSyntax.Variable as V
1414import Transform.Substitute
1515
16−
1716toMatch :: [(P.Pattern, Expr)] -> State Int (String, Match)
1817toMatch patterns = do
1918 v <- newVar
@@ -37,16 +36,19 @@ data Clause =
3736 deriving Show
3837
3938matchSubst :: [(String,String)] -> Match -> Match
40−matchSubst _ Break = Break
41−matchSubst _ Fail = Fail
42−matchSubst pairs (Seq ms) = Seq (map (matchSubst pairs) ms)
43−matchSubst pairs (Other (A a e)) =
44− Other . A a $ foldr ($) e $ map (\(x,y) -> subst x (rawVar y)) pairs
45−matchSubst pairs (Match n cs m) =
46− Match (varSubst n) (map clauseSubst cs) (matchSubst pairs m)
47− where varSubst v = fromMaybe v (lookup v pairs)
48− clauseSubst (Clause c vs m) =
49− Clause c (map varSubst vs) (matchSubst pairs m)
39+matchSubst pairs match =
40+ case match of
41+ Break -> Break
42+ Fail -> Fail
43+ Seq ms -> Seq (map (matchSubst pairs) ms)
44+ Other (A a e) ->
45+ Other . A a $ foldr ($) e $ map (\(x,y) -> subst x (rawVar y)) pairs
46+ Match n cs m ->
47+ Match (varSubst n) (map clauseSubst cs) (matchSubst pairs m)
48+ where
49+ varSubst v = fromMaybe v (lookup v pairs)
50+ clauseSubst (Clause c vs m) =
51+ Clause c (map varSubst vs) (matchSubst pairs m)
5052
5153isCon (p:_, _) =
5254 case p of
@@ -68,6 +70,7 @@ match vs@(v:_) cs def
6870 where
6971 cs' = map (dealias v) cs
7072
73+dealias :: String -> ([P.Pattern],Expr) -> ([P.Pattern],Expr)
7174dealias v c@(p:ps, A a e) =
7275 case p of
7376 P.Alias x pattern -> (pattern:ps, A a $ subst x (rawVar v) e)
compiler/Generate/JavaScript.hs+8−5
@@ -55,6 +55,7 @@ expression :: Expr -> State Int (Expression ())
5555expression (A region expr) =
5656 case expr of
5757 Var (V.Raw x) -> return $ obj x
58+
5859 Literal lit -> return $ literal lit
5960
6061 Range lo hi ->
@@ -218,19 +219,19 @@ definition (Definition pattern expr@(A region _) _) = do
218219 decl x n = varDecl x (dotSep ["$","_" ++ show n])
219220 setup vars
220221 | Help.isTuple name = assign "$" : vars
221− | otherwise = assign "$raw" : safeAssign : vars
222+ | otherwise = assign "_raw" : safeAssign : vars
222223
223− safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
224− if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
224+ safeAssign = varDecl "$" (CondExpr () if' (obj "_raw") exception)
225+ if' = InfixExpr () OpStrictEq (obj "_raw.ctor") (string name)
225226 exception = obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]
226227
227228 _ ->
228229 do defs' <- concat <$> mapM toDef vars
229− return (VarDeclStmt () [assign "$"] : defs')
230+ return (VarDeclStmt () [assign "_"] : defs')
230231 where
231232 vars = P.boundVarList pattern
232233 mkVar = A region . rawVar
233− toDef y = let expr = A region $ Case (mkVar "$") [(pattern, mkVar y)]
234+ toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
234235 in definition $ Definition (P.Var y) expr Nothing
235236
236237match :: Region -> Case.Match -> State Int [Statement ()]
@@ -257,9 +258,11 @@ match region mtch =
257258 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]) ]
258259
259260 Case.Break -> return [BreakStmt () Nothing]
261+
260262 Case.Other e ->
261263 do e' <- expression e
262264 return [ ret e' ]
265+
263266 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
264267 where
265268 dropEnd acc [] = acc
libraries/Array.elmnew file+190
@@ -0,0 +1,190 @@
1+module Array where
2+
3+{-| A library for immutable arrays. The elements in an array must have the same
4+type. The arrays are implemented in Relaxed Radix Balanced-Trees for fast
5+reads, updates, and concatenation.
6+
7+If you use more then one map or zip function on an array, consider turning it
8+into a list before operating on it, and then turning it back into an array.
9+
10+# Basics
11+@docs empty, length, get, getMaybe, getSafe
12+
13+# Putting Arrays Together
14+@docs fill, fromList, concat, set, updates, push
15+
16+# Taking Arrays Apart
17+@docs toList, toIndexedList, indices, slice
18+
19+# Mapping, folding, zipping
20+@docs map, indexedMap, foldl, foldr, zip, zipWith
21+-}
22+
23+import Native.Array
24+import Basics (..)
25+import Maybe (..)
26+import List
27+
28+data Array a = Array
29+
30+{-| Fills an array with a given length and a default element.
31+
32+ fill 3 5 == fromList [5,5,5]
33+-}
34+fill : Int -> a -> Array a
35+fill len e = fromList <| List.map (always e) [1..len]
36+
37+-- TODO: make this a native function.
38+{-| Create an array from a list. -}
39+fromList : [a] -> Array a
40+fromList = List.foldl (Native.Array.push) Native.Array.empty
41+
42+{-| Create a list of elements from an array.
43+
44+ toList (fromList [3,5,8]) == [3,5,8]
45+-}
46+toList : Array a -> [a]
47+toList = Native.Array.toList
48+
49+{-| Create a list of the possible indices of an array.
50+
51+ indices (fromList [3,5,8] == [0,1,2])
52+-}
53+indices : Array a -> [Int]
54+indices a = [0..Native.Array.length a - 1]
55+
56+-- TODO: make this a native function.
57+{-| Create a list of tuples (Index, Element) from an array.
58+
59+ toIndexedList (fromList [3,5,8]) == [(0,3), (1,5), (2,8)]
60+-}
61+toIndexedList : Array a -> [(Int, a)]
62+toIndexedList a = List.zip (indices a) (Native.Array.toList a)
63+
64+{-| Apply a function on every element in an array.
65+
66+ map (\x -> x*x) (fromList [1,2,3]) == fromList [1,4,9]
67+-}
68+map : (a -> b) -> Array a -> Array b
69+map = Native.Array.map
70+
71+{-| Apply a function on every element with its index as first argument.
72+
73+ indexedMap (*) (fromList [5,5,5]) == fromList [0,5,10]
74+-}
75+indexedMap : (Int -> a -> b) -> Array a -> Array b
76+indexedMap = Native.Array.indexedMap
77+
78+{-| Reduce an array from the left.
79+
80+ foldl (+) 0 (fill 3 5) == 15
81+-}
82+foldl : (a -> b -> b) -> b -> Array a -> b
83+foldl = Native.Array.foldl
84+
85+{-| Reduce an array from the right.
86+
87+ foldr (::) [] (fromList [1,2,3]) == [3,2,1]
88+-}
89+foldr : (a -> b -> b) -> b -> Array a -> b
90+foldr = Native.Array.foldr
91+
92+{-| Zip the elements of two arrays via a tuple into a new array. If one array is
93+longer, the extra elements are dropped.
94+
95+ zip (fromList [1,2,3,4]) (fromList [0,0,0]) = fromList [(1,0), (2,0), (3,0)]
96+-}
97+zip : Array a -> Array b -> Array (a,b)
98+zip = zipWith (,)
99+
100+{-| Zips the elements of two arrays via a function into a new array. If one array
101+is longer, the extra elements are dropped.
102+
103+ zipWith (+) (fill 3 5) (fill 5 8) == fill 3 13
104+-}
105+zipWith : (a -> b -> c) -> Array a -> Array b -> Array c
106+zipWith f a b =
107+ fromList <| List.zipWith f (Native.Array.toList a) (Native.Array.toList b)
108+
109+{-| Return an empty array.
110+
111+ length empty == 0
112+-}
113+empty : Array a
114+empty = Native.Array.empty
115+
116+{-| Push an element to the end of an array.
117+
118+ push 3 (fromList [1,2]) == fromList [1,2,3]
119+-}
120+push : a -> Array a -> Array a
121+push = Native.Array.push
122+
123+{-| Return the element at the index. Breaks, if index is out of range. If the
124+array length is unkown, use safeGet oder getWithDefault.
125+
126+ get 2 (A.fromList [3,2,1]) == 1
127+-}
128+get : Int -> Array a -> a
129+get = Native.Array.get
130+
131+{-| Return Just the element at the index or Nothing if the index is out of range.
132+
133+ getMaybe 2 (A.fromList [3,2,1]) == Just 2
134+ getMaybe 5 (A.fromList [3,2,1]) == Nothing
135+-}
136+getMaybe : Int -> Array a -> Maybe a
137+getMaybe i array = if Native.Array.length array > i
138+ then Just (Native.Array.get i array)
139+ else Nothing
140+
141+{-| Get the element at the index. If the index is out of range, the given default
142+element is returned.
143+
144+ getSafe 0 2 (A.fromList [3,2,1]) == 1
145+ getSafe 0 5 (A.fromList [3,2,1]) == 0
146+-}
147+getSafe : a -> Int -> Array a -> a
148+getSafe default i array = if Native.Array.length array > i
149+ then Native.Array.get i array else default
150+
151+{-| Set the element at the index. Returns the updated array, or if the index is
152+out of range, the unaltered array.
153+
154+ set 1 7 (fromList [1,2,3]) == fromList [1,7,3]
155+-}
156+set : Int -> a -> Array a -> Array a
157+set = Native.Array.set
158+
159+{-| Update an array with a a list tuples, wherein the first Int is the index.
160+
161+ updates [(1,7),(2,8)] (fill 3 1) == fromList [1,7,8]
162+-}
163+updates : [(Int, a)] -> Array a -> Array a
164+updates us array = List.foldl (uncurry Native.Array.set) array us
165+
166+{-| Slice an array given a range. The selection is inclusive, so the last
167+element in the selection will also be in the new array. This may change in the
168+future.
169+You can select from the end by giving a negative Int.
170+
171+ slice 1 2 (fromList [0,1,2,3,4]) == fromList [1,2]
172+ slice 1 -2 (fromList [0,1,2,3,4]) == fromList [1,2,3]
173+ slice -3 -2 (fromList [0,1,2,3,4]) == fromList [2,3]
174+-}
175+slice : Int -> Int -> Array a -> Array a
176+slice = Native.Array.slice
177+
178+{-| Return the length of an array.
179+
180+ length (fromList [1,2,3]) == 3
181+-}
182+length : Array a -> Int
183+length = Native.Array.length
184+
185+{-| Concat two arrays to a new one.
186+
187+ concat (fill 3 1) (fill 4 2) == fromList [1,1,1,2,2,2,2]
188+-}
189+concat : Array a -> Array a -> Array a
190+concat = Native.Array.concat
libraries/Native/Array.jsnew file+491
@@ -0,0 +1,491 @@
1+Elm.Native.Array = {};
2+Elm.Native.Array.make = function(elm) {
3+ elm.Native = elm.Native || {};
4+ elm.Native.Array = elm.Native.Array || {};
5+ if (elm.Native.Array.values) return elm.Native.Array.values;
6+ if ('values' in Elm.Native.Array)
7+ return elm.Native.Array.values = Elm.Native.Array.values;
8+
9+ var List = Elm.Native.List.make(elm);
10+
11+ // A RRB-Tree has two distinct data types.
12+ // Leaf -> "height" is always 0
13+ // "table" is an array of elements
14+ // Node -> "height" is always greater than 0
15+ // "table" is an array of child nodes
16+ // "lengths" is an array of accumulated lengths of the child nodes
17+
18+ // M is the maximal table size. 32 seems fast. E is the allowed increase
19+ // of search steps when concatting to find an index. Lower values will
20+ // decrease balancing, but will increase search steps.
21+ var M = 32;
22+ var E = 2;
23+
24+ // An empty array.
25+ var empty = { ctor:"_Array", height:0, table:new Array() };
26+
27+ // Gets the value at index i recursively.
28+ function get(i, a) {
29+ if (a.height == 0) {
30+ if (i < a.table.length) {
31+ return a.table[i];
32+ } else {
33+ throw new Error("Index "+ i +" is out of range. Check the length of your array first or use getSafe or getMaybe.");
34+ }
35+ }
36+
37+ var slot = getSlot(i, a);
38+ var sub = slot > 0 ? a.lengths[slot-1] : 0;
39+ return get(i - sub, a.table[slot]);
40+ }
41+
42+ // Sets the value at the index i. Only the nodes leading to i will get
43+ // copied and updated.
44+ function set(i, item, a) {
45+ if (length(a) <= i) {
46+ return a;
47+ }
48+ var newA = nodeCopy(a);
49+ newA.table = a.table.slice();
50+
51+ if (a.height == 0) {
52+ newA.table[i] = item;
53+ } else {
54+ var slot = getSlot(i, a);
55+ var sub = slot > 0 ? a.lengths[slot-1] : 0;
56+ newA.table[slot] = set(i - sub, item, a.table[slot]);
57+ }
58+ return newA;
59+ }
60+
61+ // Pushes an item via push_ to the bottom right of a tree.
62+ function push(item, a) {
63+ var pushed = push_(item, a);
64+ if (pushed !== null) {
65+ return pushed;
66+ }
67+
68+ newTree = create(item, a.height);
69+ return siblise(a, newTree);
70+ }
71+
72+ // Recursively tries to push an item to the bottom-right most
73+ // tree possible. If there is no space left for the item,
74+ // null will be returned.
75+ function push_(item, a) {
76+ // Handle resursion stop at leaf level.
77+ if (a.height == 0) {
78+ if (a.table.length < M) {
79+ var newA = { ctor:"_Array", height:0, table:a.table.slice() };
80+ newA.table.push(item);
81+ return newA;
82+ } else {
83+ return null;
84+ }
85+ }
86+
87+ // Recursively push
88+ var pushed = push_(item, botRight(a));
89+
90+ // There was space in the bottom right tree, so the slot will
91+ // be updated.
92+ if (pushed != null) {
93+ var newA = nodeCopy(a);
94+ newA.table[newA.table.length - 1] = pushed;
95+ newA.lengths[newA.lengths.length - 1]++;
96+ return newA
97+ }
98+
99+ // When there was no space left, check if there is space left
100+ // for a new slot with a tree which contains only the item
101+ // at the bottom.
102+ if (a.table.length < M) {
103+ var newSlot = create(item, a.height - 1);
104+ var newA = nodeCopy(a);
105+ newA.table.push(newSlot);
106+ newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
107+ return newA
108+ } else {
109+ return null;
110+ }
111+ }
112+
113+ // Converts an array into a list of elements.
114+ function toList(a) {
115+ return toList_(List.Nil, a);
116+ }
117+
118+ function toList_(list, a) {
119+ for (var i = a.table.length - 1; i >= 0; i--) {
120+ list = a.height == 0 ? List.Cons(a.table[i], list) : toList_(list, a.table[i]);
121+ }
122+ return list;
123+ }
124+
125+ // Maps a function over the elements of an array.
126+ function map(f, a) {
127+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
128+ if (a.height > 0) { newA.lengths = a.lengths; }
129+ for (var i = 0; i < a.table.length; i++) {
130+ newA.table[i] = a.height == 0 ? f(a.table[i]) : map(f, a.table[i]);
131+ }
132+ return newA;
133+ }
134+
135+ // Maps a function over the elements with their index as first argument.
136+ function indexedMap(f, a) {
137+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
138+ if (a.height > 0) { newA.lengths = a.lengths; }
139+ for (var i = 0; i < a.table.length; i++) {
140+ newA.table[i] = a.height == 0 ? A2(f, i, a.table[i]) : indexedMap(f, a.table[i]);
141+ }
142+ return newA;
143+ }
144+
145+ function foldl(f, b, a) {
146+ for (var i = a.table.length - 1; i >= 0; i--) {
147+ b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
148+ }
149+ return b;
150+ }
151+
152+ function foldr(f, b, a) {
153+ for (var i = 0; i < a.table.length; i++) {
154+ b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
155+ }
156+ return b;
157+ }
158+
159+ // Returns a sliced tree. "to" is inclusive, but this may change,
160+ // when I understand, why e.g. JS does not handle it this way. :-)
161+ // If "from" or "to" is negative, they will select from the end on.
162+ // TODO: currently, it slices the right, then the left. This can be
163+ // optimized.
164+ function slice(from, to, a) {
165+ if (from < 0) { from += length(a); }
166+ if (to < 0) { to += length(a); }
167+ return sliceLeft(from, sliceRight(to, a));
168+ }
169+
170+ function sliceRight(to, a) {
171+ if (to == length(a)) {
172+ return a;
173+ }
174+
175+ // Handle leaf level.
176+ if (a.height == 0) {
177+ var newA = { ctor:"_Array", height:0 };
178+ newA.table = a.table.slice(0, to + 1);
179+ return newA;
180+ }
181+
182+ // Slice the right recursively.
183+ var right = getSlot(to, a);
184+ var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
185+
186+ // Maybe the a node is not even needed, as sliced contains the whole slice.
187+ if (right == 0) {
188+ return sliced;
189+ }
190+
191+ // Create new node.
192+ var newA = { ctor:"_Array", height:a.height
193+ , table:a.table.slice(0, right + 1)
194+ , lengths:a.lengths.slice(0, right + 1) };
195+ newA.table[right] = sliced;
196+ newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
197+ return newA;
198+ }
199+
200+ function sliceLeft(from, a) {
201+ if (from == 0) {
202+ return a;
203+ }
204+
205+ // Handle leaf level.
206+ if (a.height == 0) {
207+ var newA = { ctor:"_Array", height:0 };
208+ newA.table = a.table.slice(from, a.table.length + 1);
209+ return newA;
210+ }
211+
212+ // Slice the left recursively.
213+ var left = getSlot(from, a);
214+ var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
215+
216+ // Maybe the a node is not even needed, as sliced contains the whole slice.
217+ if (left == a.table.length - 1) {
218+ return sliced;
219+ }
220+
221+ // Create new node.
222+ var newA = { ctor:"_Array", height:a.height
223+ , table:a.table.slice(left, a.table.length + 1)
224+ , lengths:new Array(a.table.length - left) };
225+ newA.table[left] = sliced;
226+ var len = 0;
227+ for (var i = 0; i < newA.table.length; i++) {
228+ len += length(newA.table[i]);
229+ newA.lengths[i] = len;
230+ }
231+
232+ return newA;
233+ }
234+
235+ // Concats two trees.
236+ // TODO: Add support for concatting trees of different sizes. Current
237+ // behavior will just rise the lower tree and then concat them.
238+ function concat(a, b) {
239+ if (b.height > a.height) { return concat(parentise(a, b.height), b); }
240+ if (a.height > b.height) { return concat(a, parentise(b, a.height)); }
241+ if (a.height == 0) { return concat(parentise(a, 1), parentise(b, 1)); }
242+
243+ var c = concat_(a, b);
244+ if (c[1].table.length > 0) {
245+ return siblise(c[0], c[1]);
246+ } else {
247+ return c[0];
248+ }
249+ }
250+
251+ // Returns an array of two nodes. The second node _may_ be empty. This case
252+ // needs to be handled by the function, that called concat_. May be only
253+ // called for trees with an minimal height of 1.
254+ function concat_(a, b) {
255+ if (a.height == 1) {
256+ // Check if balancing is needed and return based on that.
257+ var toRemove = calcToRemove(a, b);
258+ if (toRemove <= E) {
259+ return [a,b];
260+ }
261+
262+ return shuffle(a, b, toRemove);
263+ }
264+
265+ var concated = concat_(botRight(a), botLeft(b));
266+ a = nodeCopy(a), b = nodeCopy(b);
267+
268+ // Adjust the bottom right side of the new tree.
269+ a.table[a.table.length - 1] = concated[0];
270+ a.lengths[a.lengths.length - 1] = length(concated[0])
271+ a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
272+
273+ // Adjust the bottom left side of the new tree.
274+ if (concated[1].table.length > 0) {
275+ b.table[0] = concated[1];
276+ b.lengths[0] = length(concated[1]);
277+ for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
278+ len += length(b.table[i]);
279+ b.lengths[i] = len;
280+ }
281+ } else {
282+ b.table.shift();
283+ for (var i = 1; i < b.lengths.length; i++) {
284+ b.lengths[i] = b.lengths[i] - b.lengths[0];
285+ }
286+ b.lengths.shift();
287+ }
288+
289+ // Check if balancing is needed and return based on that.
290+ var toRemove = calcToRemove(a, b);
291+ if (toRemove <= E || b.table.length == 0) {
292+ return [a,b];
293+ }
294+
295+ return shuffle(a, b, toRemove);
296+ }
297+
298+ // Returns the extra search steps for E. Refer to the paper.
299+ function calcToRemove(a, b) {
300+ var subLengths = 0;
301+ for (var i = 0; i < a.table.length; i++) {
302+ subLengths += a.table[i].table.length;
303+ }
304+ for (var i = 0; i < b.table.length; i++) {
305+ subLengths += b.table[i].table.length;
306+ }
307+
308+ var toRemove = a.table.length + b.table.length
309+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
310+ }
311+
312+ // get2, set2 and saveSlot are helpers for accessing elements over two arrays.
313+ function get2(a, b, index) {
314+ return index < a.length ? a[index] : b[index - a.length];
315+ }
316+
317+ function set2(a, b, index, value) {
318+ if (index < a.length) {
319+ a[index] = value;
320+ } else {
321+ b[index - a.length] = value;
322+ }
323+ }
324+
325+ function saveSlot(a, b, index, slot) {
326+ set2(a.table, b.table, index, slot);
327+
328+ var l = (index == 0 || index == a.lengths.length) ?
329+ 0 : get2(a.lengths, a.lengths, index - 1);
330+ set2(a.lengths, b.lengths, index, l + length(slot));
331+ }
332+
333+ // Creates a node or leaf with a given length at their arrays for perfomance.
334+ // Is only used by shuffle.
335+ function createNode(h, length) {
336+ if (length < 0) { length = 0; }
337+ var a = { ctor:"_Array", height:h, table:new Array(length) };
338+ if (h > 0) {
339+ a.lengths = new Array(length);
340+ }
341+ return a;
342+ }
343+
344+ // Returns an array of two balanced nodes.
345+ function shuffle(a, b, toRemove) {
346+ var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
347+ var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
348+
349+ // Skip the slots with size M. More precise: copy the slot references
350+ // to the new node
351+ var read = 0;
352+ while (get2(a.table, b.table, read).table.length % M == 0) {
353+ set2(newA.table, newB.table, read, get2(a.table, b.table, read));
354+ set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
355+ read++;
356+ }
357+
358+ // Pulling items from left to right, caching in a slot before writing
359+ // it into the new nodes.
360+ var write = read;
361+ var slot = new createNode(a.height - 1, 0);
362+ var from = 0;
363+
364+ // If the current slot is still containing data, then there will be at
365+ // least one more write, so we do not break this loop yet.
366+ while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) {
367+ // Find out the max possible items for copying.
368+ var source = get2(a.table, b.table, read);
369+ var to = Math.min(M - slot.table.length, source.table.length)
370+
371+ // Copy and adjust size table.
372+ slot.table = slot.table.concat(source.table.slice(from, to));
373+ if (slot.height > 0) {
374+ var len = slot.lengths.length;
375+ for (var i = len; i < len + to - from; i++) {
376+ slot.lengths[i] = length(slot.table[i]);
377+ slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
378+ }
379+ }
380+
381+ from += to;
382+
383+ // Only proceed to next slots[i] if the current one was
384+ // fully copied.
385+ if (source.table.length <= to) {
386+ read++; from = 0;
387+ }
388+
389+ // Only create a new slot if the current one is filled up.
390+ if (slot.table.length == M) {
391+ saveSlot(newA, newB, write, slot);
392+ slot = createNode(a.height - 1,0);
393+ write++;
394+ }
395+ }
396+
397+ // Cleanup after the loop. Copy the last slot into the new nodes.
398+ if (slot.table.length > 0) {
399+ saveSlot(newA, newB, write, slot);
400+ write++;
401+ }
402+
403+ // Shift the untouched slots to the left
404+ while (read < a.table.length + b.table.length ) {
405+ saveSlot(newA, newB, write, get2(a.table, b.table, read));
406+ read++; write++;
407+ }
408+
409+ return [newA, newB];
410+ }
411+
412+ // Navigation functions
413+ function botRight(a) { return a.table[a.table.length - 1]; }
414+ function botLeft(a) { return a.table[0]; }
415+
416+ // Copies a node for updating. Note that you should not use this if
417+ // only updating only one of "table" or "lengths" for performance reasons.
418+ function nodeCopy(a) {
419+ var newA = { ctor:"_Array", height:a.height
420+ , table:a.table.slice() };
421+ if (a.height > 0) { newA.lengths = a.lengths.slice(); }
422+ return newA;
423+ }
424+
425+ // Returns how many items are in the tree.
426+ function length(a) {
427+ if (a.height == 0) {
428+ return a.table.length;
429+ } else {
430+ return a.lengths[a.lengths.length - 1];
431+ }
432+ }
433+
434+ // Calculates in which slot of "table" the item probably is, then
435+ // find the exact slot via forward searching in "lengths". Returns the index.
436+ function getSlot(i, a) {
437+ var slot = Math.floor(i / (Math.pow(M, a.height)));
438+ while (a.lengths[slot] <= i) {
439+ slot++
440+ }
441+ return slot;
442+ }
443+
444+ // Recursively creates a tree with a given height containing
445+ // only the given item.
446+ function create(item, h) {
447+ if (h == 0) {
448+ return { ctor:"_Array", height:0
449+ , table:[item] };
450+ } else {
451+ return { ctor:"_Array", height:h
452+ , table:[create(item, h - 1)]
453+ , lengths:[1] };
454+ }
455+ }
456+
457+ // Recursively creates a tree that contains the given tree.
458+ function parentise(tree, h) {
459+ if (h == tree.height) {
460+ return tree;
461+ } else {
462+ return { ctor:"_Array", height:h
463+ , table:[parentise(tree, h - 1)]
464+ , lengths:[length(tree)] };
465+ }
466+ }
467+
468+ // Emphasizes blood brotherhood beneath two trees.
469+ function siblise(a, b) {
470+ return { ctor:"_Array", height:a.height + 1
471+ , table:[a, b]
472+ , lengths:[length(a), length(a) + length(b)] };
473+ }
474+
475+ Elm.Native.Array.values = {
476+ empty:empty,
477+ toList:toList,
478+ concat:F2(concat),
479+ push:F2(push),
480+ slice:F3(slice),
481+ get:F2(get),
482+ set:F3(set),
483+ map:F2(map),
484+ indexedMap:F2(indexedMap),
485+ foldl:F3(foldl),
486+ foldr:F3(foldr),
487+ length:length
488+ };
489+
490+ return elm.Native.Array.values = Elm.Native.Array.values;
491+}
libraries/Native/Graphics/Input.js+9−9
@@ -12,7 +12,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
1212 var Text = Elm.Native.Text.make(elm);
1313 var Signal = Elm.Signal.make(elm);
1414 var newElement = Elm.Graphics.Element.make(elm).newElement;
15− var JS = Elm.Native.JavaScript.make(elm);
15+ var List = Elm.Native.List.make(elm);
1616 var Utils = Elm.Native.Utils.make(elm);
1717 var Tuple2 = Utils.Tuple2;
1818
@@ -23,14 +23,14 @@ Elm.Native.Graphics.Input.make = function(elm) {
2323
2424 function renderDropDown(signal, values) {
2525 return function(_) {
26− var entries = JS.fromList(values);
26+ var entries = List.toArray(values);
2727
2828 var drop = newNode('select');
2929 drop.style.border = '0 solid';
3030 drop.style.pointerEvents = 'auto';
3131 for (var i = 0; i < entries.length; ++i) {
3232 var option = newNode('option');
33− var name = JS.fromString(entries[i]._0);
33+ var name = entries[i]._0;
3434 option.value = name;
3535 option.innerHTML = name;
3636 drop.appendChild(option);
@@ -91,7 +91,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
9191 type: 'Button',
9292 render: renderButton,
9393 update: updateButton,
94− model: { signal:signal, value:value, text:JS.fromString(text) }
94+ model: { signal:signal, value:value, text:text }
9595 });
9696 }
9797
@@ -266,8 +266,8 @@ Elm.Native.Graphics.Input.make = function(elm) {
266266 field.style.pointerEvents = 'auto';
267267
268268 field.type = model.type;
269− field.placeholder = JS.fromString(model.placeHolder);
270− field.value = JS.fromString(model.content.string);
269+ field.placeholder = model.placeHolder;
270+ field.value = model.content.string;
271271
272272 field.elm_signal = model.signal;
273273 field.elm_handler = model.handler;
@@ -287,7 +287,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
287287
288288 elm.notify(field.elm_signal.id, field.elm_handler({
289289 _:{},
290− string: JS.toString(next),
290+ string: next,
291291 selection: {
292292 _:{},
293293 start: start,
@@ -338,8 +338,8 @@ Elm.Native.Graphics.Input.make = function(elm) {
338338 field.elm_handler = newModel.handler;
339339
340340 field.type = newModel.type;
341− field.placeholder = JS.fromString(newModel.placeHolder);
342− var value = JS.fromString(newModel.content.string);
341+ field.placeholder = newModel.placeHolder;
342+ var value = newModel.content.string;
343343 field.value = value;
344344 field.elm_old_value = value;
345345 if (field.elm_hasFocus) {
libraries/Native/Show.js+4
@@ -5,6 +5,7 @@ Elm.Native.Show.make = function(elm) {
55 if (elm.Native.Show.values) return elm.Native.Show.values;
66
77 var NList = Elm.Native.List.make(elm);
8+ var Array = Elm.Array.make(elm);
89 var List = Elm.List.make(elm);
910 var Dict = Elm.Dict.make(elm);
1011 var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;
@@ -43,6 +44,9 @@ Elm.Native.Show.make = function(elm) {
4344 output.push(toString(v[k]));
4445 }
4546 return "(" + output.join(",") + ")";
47+ } else if (v.ctor === "_Array") {
48+ var list = Array.toList(v);
49+ return "Array.fromList " + toString(list);
4650 } else if (v.ctor === "::") {
4751 var output = '[' + toString(v._0);
4852 v = v._1;