on master help sign in
Merge branch 'master' into debugger
commit163f96d4758107491cd136c6fb631b82880904e7 authorEvan Czaplicki <info@elm-lang.org> dateFri 4 Apr 2014, 00:38 (13 years ago) parent42c0018a4b591b
42 files changed+167−728
.gitignore+3 .gitmodules+9 Elm-Test+1 Elm.cabal+13−3 IO+1 automaton+1 libraries/Array.elm−191 libraries/JavaScript/Experimental.elm+2−1 libraries/Native/Array.js−488 libraries/Native/Graphics/Input.js−24 libraries/Native/Text.js+1 libraries/Native/Utils.js+13−8 libraries/Signal.elm+8−10 libraries/String.elm+1−1 runtime/Render/Element.js+5−2 tests/elm/Test.elm+27 tests/elm/Test/String.elm+26 tests/elm/Test/Trampoline.elm+29 tests/Main.hs → tests/hs/CompilerTest.hs tests/hs/LibTest.hs+27 tests/Tests/Compiler.hs → tests/hs/Tests/Compiler.hs tests/Tests/Property.hs → tests/hs/Tests/Property.hs tests/Tests/Property/Arbitrary.hs → tests/hs/Tests/Property/Arbitrary.hs tests/test-files/bad/BBTArgs → tests/hs/test-files/bad/BBTArgs tests/test-files/bad/InfiniteType.elm → tests/hs/test-files/bad/InfiniteType.elm tests/test-files/bad/NonElementMain.elm → tests/hs/test-files/bad/NonElementMain.elm tests/test-files/bad/Strings/ExtraClose.elm → tests/hs/test-files/bad/Strings/ExtraClose.elm tests/test-files/good/AliasSubstitution.elm → tests/hs/test-files/good/AliasSubstitution.elm tests/test-files/good/NoExpressions.elm → tests/hs/test-files/good/NoExpressions.elm tests/test-files/good/Otherwise.elm → tests/hs/test-files/good/Otherwise.elm tests/test-files/good/Ports.elm → tests/hs/test-files/good/Ports.elm tests/test-files/good/QuotesAndComments.elm → tests/hs/test-files/good/QuotesAndComments.elm tests/test-files/good/Soundness/Apply.elm → tests/hs/test-files/good/Soundness/Apply.elm tests/test-files/good/Soundness/ApplyAnnotated.elm → tests/hs/test-files/good/Soundness/ApplyAnnotated.elm tests/test-files/good/Soundness/Id.elm → tests/hs/test-files/good/Soundness/Id.elm tests/test-files/good/Soundness/IdAnnotated.elm → tests/hs/test-files/good/Soundness/IdAnnotated.elm tests/test-files/good/Soundness/TrickyId.elm → tests/hs/test-files/good/Soundness/TrickyId.elm tests/test-files/good/Soundness/TrickyIdAnnotated.elm → tests/hs/test-files/good/Soundness/TrickyIdAnnotated.elm tests/test-files/good/Strings/Multiline.elm → tests/hs/test-files/good/Strings/Multiline.elm tests/test-files/good/Strings/MultilineNormal.elm → tests/hs/test-files/good/Strings/MultilineNormal.elm tests/test-files/good/Unify/LockedVars.elm → tests/hs/test-files/good/Unify/LockedVars.elm tests/test-files/good/Unify/NonHomogeneousRecords.elm → tests/hs/test-files/good/Unify/NonHomogeneousRecords.elm
.gitignore+3
@@ -11,3 +11,6 @@ data
1111*/ElmFiles/*
1212.DS_Store
1313*~
14+tests/elm/build/
15+tests/elm/cache/
16+tests/elm/exe.js
.gitmodulesnew file+9
@@ -0,0 +1,9 @@
1+[submodule "IO"]
2+ path = IO
3+ url = https://github.com/maxsnew/IO.git
4+[submodule "automaton"]
5+ path = automaton
6+ url = https://github.com/evancz/automaton.git
7+[submodule "Elm-Test"]
8+ path = Elm-Test
9+ url = https://github.com/deadfoxygrandpa/Elm-Test.git
Elm-Testnew file+1

Submodule changed.

Elm.cabal+13−3
@@ -236,10 +236,10 @@ Executable elm-doc
236236 text,
237237 union-find
238238
239−Test-Suite test-elm
239+Test-Suite compiler-tests
240240 Type: exitcode-stdio-1.0
241− Hs-Source-Dirs: tests, compiler
242− Main-is: Main.hs
241+ Hs-Source-Dirs: tests/hs, compiler
242+ Main-is: CompilerTest.hs
243243 other-modules: Tests.Compiler
244244 Tests.Property
245245 Tests.Property.Arbitrary
@@ -276,3 +276,13 @@ Test-Suite test-elm
276276 transformers >= 0.2,
277277 union-find,
278278 unordered-containers
279+
280+Test-Suite lib-tests
281+ Type: exitcode-stdio-1.0
282+ Hs-Source-Dirs: tests/hs
283+ Main-is: LibTest.hs
284+ build-depends: base,
285+ directory,
286+ Elm,
287+ filepath,
288+ process >= 1.2
IOnew file+1

Submodule changed.

automatonnew file+1

Submodule changed.

libraries/Array.elmdeleted−191
@@ -1,191 +0,0 @@
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, safeGet, getWithDefault
12−
13−# Putting Arrays Together
14−@docs fill, fromList, concat, set, updates, push
15−
16−# Taking Arrays Apart
17−@docs elements, indices, assocs, slice
18−
19−# Mapping, folding, zipping
20−@docs map, assocMap, 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−{-| Create an array from a list. -}
38−fromList : [a] -> Array a
39−fromList = List.foldl (Native.Array.push) Native.Array.empty
40−
41−{-| Create a list of elements from an array.
42−
43− elements (fromList [3,5,8]) == [3,5,8]
44−-}
45−elements : Array a -> [a]
46−elements = Native.Array.toList
47−
48−{-| Create a list of the possible indices of an array.
49−
50− indices (fromList [3,5,8] == [0,1,2])
51−-}
52−indices : Array a -> [Int]
53−indices a = [0..Native.Array.length a - 1]
54−
55−{-| Create a list of tuples (Index, Element) from an array.
56−
57− assocs (fromList [3,5,8]) == [(0,3), (1,5), (2,8)]
58−-}
59−assocs : Array a -> [(Int, a)]
60−assocs a = List.zip (indices a) (Native.Array.elements a)
61−
62−{-| Apply a function on every element in an array.
63−
64− map (\x -> x*x) (fromList [1,2,3]) == fromList [1,4,9]
65−-}
66−map : (a -> b) -> Array a -> Array b
67−map = Native.Array.map
68−
69−{-| Apply a function with an index on every element in an array.
70−
71− assocMap (*) (fromList [5,5,5]) == fromList [0,5,10]
72−-}
73−assocMap : (Int -> a -> b) -> Array a -> Array b
74−assocMap = Native.Array.assocMap
75−
76−{-| Reduce an array from the left.
77−
78− foldl (+) 0 (fill 3 5) == 15
79−-}
80−foldl : (a -> b -> b) -> b -> Array a -> b
81−foldl = Native.Array.foldl
82−
83−{-| Reduce an array from the right.
84−
85− foldr (::) [] (fromList [1,2,3]) == [3,2,1]
86−-}
87−foldr : (a -> b -> b) -> b -> Array a -> b
88−foldr = Native.Array.foldr
89−
90−{-| Zip the elements of two arrays via a tuple into a new array. If one array is
91−longer, the extra elements are dropped.
92−
93− zip (fromList [1,2,3,4]) (fromList [0,0,0]) = fromList [(1,0), (2,0), (3,0)]
94−-}
95−zip : Array a -> Array b -> Array (a,b)
96−zip = zipWith (,)
97−
98−{-| Zips the elements of two arrays via a function into a new array. If one array
99−is longer, the extra elements are dropped.
100−
101− zipWith (+) (fill 3 5) (fill 5 8) == fill 3 13
102−-}
103−zipWith : (a -> b -> c) -> Array a -> Array b -> Array c
104−zipWith f a b =
105− fromList <| List.zipWith f (Native.Array.elements a) (Native.Array.elements b)
106−
107−{-| Return an empty array.
108−
109− length empty == 0
110−-}
111−empty : Array a
112−empty = Native.Array.empty
113−
114−{-| Push an element to the end of an array.
115−
116− push 3 (fromList [1,2]) == fromList [1,2,3]
117−-}
118−push : a -> Array a -> Array a
119−push = Native.Array.push
120−
121−{-| Return the element at the index. Breaks, if index is out of range. If the
122−array length is unkown, use safeGet oder getWithDefault.
123−
124− get 2 (A.fromList [3,2,1]) == 1
125−-}
126−get : Int -> Array a -> a
127−get = Native.Array.get
128−
129−{-| Return Just the element at the index or Nothing.
130−
131− safeGet 2 (A.fromList [3,2,1]) == Just 2
132− safeGet 5 (A.fromList [3,2,1]) == Nothing
133−-}
134−safeGet : Int -> Array a -> Maybe a
135−safeGet i array =
136− if i < Native.Array.length array
137− then Just (Native.Array.get i array)
138− else Nothing
139−
140−{-| Get the element at the index. If the index is out of range, the given default
141−elementis returned.
142−
143− getWithDefault 0 2 (A.fromList [3,2,1]) == 1
144− getWithDefault 0 5 (A.fromList [3,2,1]) == 0
145−-}
146−getWithDefault : a -> Int -> Array a -> a
147−getWithDefault default i array =
148− if i < Native.Array.length array
149− then Native.Array.get i array
150− else default
151−
152−{-| Set the element at the index. Returns the updated array, or if the index is
153−out of range, the unaltered array.
154−
155− set 1 7 (fromList [1,2,3]) == fromList [1,7,3]
156−-}
157−set : Int -> a -> Array a -> Array a
158−set = Native.Array.set
159−
160−{-| Update an array with a a list tuples, wherein the first Int is the index.
161−
162− updates [(1,7),(2,8)] (fill 3 1) == fromList [1,7,8]
163−-}
164−updates : [(Int, a)] -> Array a -> Array a
165−updates assocs array = List.foldl (uncurry Native.Array.set) array assocs
166−
167−{-| Slice an array given a range. The selection is inclusive, so the last
168−element in the selection will also be in the new array. This may change in the
169−future.
170−You can select from the end by giving a negative Int.
171−
172− slice 1 2 (fromList [0,1,2,3,4]) == fromList [1,2]
173− slice 1 -2 (fromList [0,1,2,3,4]) == fromList [1,2,3]
174− slice -3 -2 (fromList [0,1,2,3,4]) == fromList [2,3]
175−-}
176−slice : Int -> Int -> Array a -> Array a
177−slice = Native.Array.slice
178−
179−{-| Return the length of an array.
180−
181− length (fromList [1,2,3]) == 3
182−-}
183−length : Array a -> Int
184−length = Native.Array.length
185−
186−{-| Concat two arrays to a new one.
187−
188− concat (fill 3 1) (fill 4 2) == fromList [1,1,1,2,2,2,2]
189−-}
190−concat : Array a -> Array a -> Array a
191−concat = Native.Array.concat
libraries/JavaScript/Experimental.elm+2−1
@@ -9,6 +9,7 @@ improve as we find its key failings.
99-}
1010
1111import Native.JavaScript
12+import Native.Json
1213import Json
1314
1415data RawObject = RawObject
@@ -52,4 +53,4 @@ toJson = Native.Json.fromJS
5253JavaScript object.
5354-}
5455fromJson : Json.Value -> RawObject
55−fromJson = Native.Json.toJS
56+fromJson = Native.Json.toJS
libraries/Native/Array.jsdeleted−488
@@ -1,488 +0,0 @@
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− // An RRB-Tree has two distinct data types. A leaf which contains data as
12− // an array in _1, and a height in _0, that is always 0. A node has in
13− // addition its size table in _2, while _1 contains an array of nodes or
14− // leaves.
15−
16− // M is the maximal node size. 32 seems fast. E is the allowed increase
17− // of search steps when concatting to find an index. Lower values will
18− // decrease balancing, but will increase search steps.
19− var M = 32;
20− var E = 2;
21−
22− // An empty array.
23− var empty = { ctor:"_Array", _0:0, _1:new Array() };
24−
25− // Gets the value at index i recursively.
26− function get(i, a) {
27− if (a._0 == 0) {
28− if (i < a._1.length) {
29− return a._1[i];
30− } else {
31− throw new Error("Index "+ i +" on the array is out of range. Check the length first or you safeGet.");
32− }
33− }
34−
35− var slot = getSlot(i, a);
36− var sub = slot > 0 ? a._2[slot-1] : 0;
37− return get(i - sub, a._1[slot]);
38− }
39−
40− // Sets the value at the index i. Only the nodes leading to i will get
41− // copied and updated.
42− function set(i, item, a) {
43− if (length(a) <= i) {
44− return a;
45− }
46− var newA = nodeCopy(a);
47− newA._1 = a._1.slice();
48−
49− if (a._0 == 0) {
50− newA._1[i] = item;
51− } else {
52− var slot = getSlot(i, a);
53− var sub = slot > 0 ? a._2[slot-1] : 0;
54− newA._1[slot] = set(i - sub, item, a._1[slot]);
55− }
56− return newA;
57− }
58−
59− // Pushes an item via push_ to the bottom right of a tree.
60− function push(item, a) {
61− var pushed = push_(item, a);
62− if (pushed !== null) {
63− return pushed;
64− }
65−
66− newTree = create(item, a._0);
67− return siblise(a, newTree);
68− }
69−
70− // Recursively tries to push an item to the bottom-right most
71− // tree possible. If there is no space left for the item,
72− // null will be returned.
73− function push_(item, a) {
74− // Handle resursion stop at leaf level.
75− if (a._0 == 0) {
76− if (a._1.length < M) {
77− var newA = { ctor:"_Array", _0:0, _1:a._1.slice() };
78− newA._1.push(item);
79− return newA;
80− } else {
81− return null;
82− }
83− }
84−
85− // Recursively push
86− var pushed = push_(item, botRight(a));
87−
88− // There was space in the bottom right tree, so the slot will
89− // be updated.
90− if (pushed != null) {
91− var newA = nodeCopy(a);
92− newA._1[newA._1.length - 1] = pushed;
93− newA._2[newA._2.length - 1]++;
94− return newA
95− }
96−
97− // When there was no space left, check if there is space left
98− // for a new slot with a tree which contains only the item
99− // at the bottom.
100− if (a._1.length < M) {
101− var newSlot = create(item, a._0 - 1);
102− var newA = nodeCopy(a);
103− newA._1.push(newSlot);
104− newA._2.push(newA._2[newA._2.length - 1] + length(newSlot));
105− return newA
106− } else {
107− return null;
108− }
109− }
110−
111− // Converts an array into a list of elements.
112− function elements(a) {
113− return elements_(List.Nil, a);
114− }
115−
116− function elements_(list, a) {
117− for (var i = a._1.length - 1; i >= 0; i--) {
118− list = a._0 == 0 ? List.Cons(a._1[i], list) : elements_(list, a._1[i]);
119− }
120− return list;
121− }
122−
123− // Maps a function over an array.
124− function map(f, a) {
125− var newA = { ctor:"_Array", _0:a._0, _1:new Array(a._1) };
126− if (a._0 > 0) { newA._2 = a._2; }
127− for (var i = 0; i < a._1.length; i++) {
128− newA._1[i] = a._0 == 0 ? f(a._1[i]) : map(f, a._1[i]);
129− }
130− return newA;
131− }
132−
133− function assocMap(f, a) {
134− var newA = { ctor:"_Array", _0:a._0, _1:new Array(a._1) };
135− if (a._0 > 0) { newA._2 = a._2; }
136− for (var i = 0; i < a._1.length; i++) {
137− newA._1[i] = a._0 == 0 ? A2(f, i, a._1[i]) : assocMap(f, a._1[i]);
138− }
139− return newA;
140− }
141−
142− function foldl(f, b, a) {
143− for (var i = a._1.length - 1; i >= 0; i--) {
144− b = A2(f, a._0 == 0 ? a._1[i] : foldl(f, b, a._1[i]), b);
145− }
146− return b;
147− }
148−
149− function foldr(f, b, a) {
150− for (var i = 0; i < a._1.length; i++) {
151− b = A2(f, a._0 == 0 ? a._1[i] : foldr(f, b, a._1[i]), b);
152− }
153− return b;
154− }
155−
156− // Returns a sliced tree. The to is inclusive, but this may change,
157− // when I understand, why e.g. JS does not handle it this way. :-)
158− // If from or to is negative, they will select from the end on.
159− // TODO: currently, it slices the right, then the left. This can be
160− // optimized.
161− function slice(from, to, a) {
162− if (from < 0) { from += length(a); }
163− if (to < 0) { to += length(a); }
164− return sliceLeft(from, sliceRight(to, a));
165− }
166−
167− function sliceRight(to, a) {
168− if (to == length(a)) {
169− return a;
170− }
171−
172− // Handle leaf level.
173− if (a._0 == 0) {
174− var newA = { ctor:"_Array", _0:0 };
175− newA._1 = a._1.slice(0, to + 1);
176− return newA;
177− }
178−
179− // Slice the right recursively.
180− var right = getSlot(to, a);
181− var sliced = sliceRight(to - (right > 0 ? a._2[right - 1] : 0), a._1[right]);
182−
183− // Maybe the a node is not even needed, as sliced contains the whole slice.
184− if (right == 0) {
185− return sliced;
186− }
187−
188− // Create new node.
189− var newA = { ctor:"_Array", _0:a._0
190− , _1:a._1.slice(0, right + 1)
191− , _2:a._2.slice(0, right + 1) };
192− newA._1[right] = sliced;
193− newA._2[right] = length(sliced) + (right > 0 ? newA._2[right - 1] : 0);
194− return newA;
195− }
196−
197− function sliceLeft(from, a) {
198− if (from == 0) {
199− return a;
200− }
201−
202− // Handle leaf level.
203− if (a._0 == 0) {
204− var newA = { ctor:"_Array", _0:0 };
205− newA._1 = a._1.slice(from, a._1.length + 1);
206− return newA;
207− }
208−
209− // Slice the left recursively.
210− var left = getSlot(from, a);
211− var sliced = sliceLeft(from - (left > 0 ? a._2[left - 1] : 0), a._1[left]);
212−
213− // Maybe the a node is not even needed, as sliced contains the whole slice.
214− if (left == a._1.length - 1) {
215− return sliced;
216− }
217−
218− // Create new node.
219− var newA = { ctor:"_Array", _0:a._0
220− , _1:a._1.slice(left, a._1.length + 1)
221− , _2:new Array(a._1.length - left) };
222− newA._1[left] = sliced;
223− var len = 0;
224− for (var i = 0; i < newA._1.length; i++) {
225− len += length(newA._1[i]);
226− newA._2[i] = len;
227− }
228−
229− return newA;
230− }
231−
232− // Concats two trees.
233− // TODO: Add support for concatting trees of different sizes. Current
234− // behavior will just rise the lower tree and then concat them.
235− function concat(a, b) {
236− if (b._0 > a._0) { return concat(parentise(a, b._0), b); }
237− if (a._0 > b._0) { return concat(a, parentise(b, a._0)); }
238− if (a._0 == 0) { return concat(parentise(a, 1), parentise(b, 1)); }
239−
240− var c = concat_(a, b);
241− if (c[1]._1.length > 0) {
242− return siblise(c[0], c[1]);
243− } else {
244− return c[0];
245− }
246− }
247−
248− // Returns an array of two nodes. The second node _may_ be empty. This case
249− // needs to be handled by the function, that called concat_. May be only
250− // called for trees with an minimal height of 1.
251− function concat_(a, b) {
252− if (a._0 == 1) {
253− // Check if balancing is needed and return based on that.
254− var toRemove = calcToRemove(a, b);
255− if (toRemove <= E) {
256− return [a,b];
257− }
258−
259− return shuffle(a, b, toRemove);
260− }
261−
262− var concated = concat_(botRight(a), botLeft(b));
263− a = nodeCopy(a), b = nodeCopy(b);
264−
265− // Adjust the bottom right side of the new tree.
266− a._1[a._1.length - 1] = concated[0];
267− a._2[a._2.length - 1] = length(concated[0])
268− a._2[a._2.length - 1] += a._2.length > 1 ? a._2[a._2.length - 2] : 0;
269−
270− // Adjust the bottom left side of the new tree.
271− if (concated[1]._1.length > 0) {
272− b._1[0] = concated[1];
273− b._2[0] = length(concated[1]);
274− for (var i = 1, len = length(b._1[0]); i < b._2.length; i++) {
275− len += length(b._1[i]);
276− b._2[i] = len;
277− }
278− } else {
279− b._1.shift();
280− for (var i = 1; i < b._2.length; i++) {
281− b._2[i] = b._2[i] - b._2[0];
282− }
283− b._2.shift();
284− }
285−
286− // Check if balancing is needed and return based on that.
287− var toRemove = calcToRemove(a, b);
288− if (toRemove <= E || b._1.length == 0) {
289− return [a,b];
290− }
291−
292− return shuffle(a, b, toRemove);
293− }
294−
295− // Returns the extra search steps for E. Refer to the paper.
296− function calcToRemove(a, b) {
297− var subLengths = 0;
298− for (var i = 0; i < a._1.length; i++) {
299− subLengths += a._1[i]._1.length;
300− }
301− for (var i = 0; i < b._1.length; i++) {
302− subLengths += b._1[i]._1.length;
303− }
304−
305− var toRemove = a._1.length + b._1.length
306− return toRemove - (Math.floor((subLengths - 1) / M) + 1);
307− }
308−
309− // get2 and set2 are helpers for accessing over two arrays.
310− function get2(a, b, index) {
311− return index < a.length ? a[index] : b[index - a.length];
312− }
313−
314− function set2(a, b, index, value) {
315− if (index < a.length) {
316− a[index] = value;
317− } else {
318− b[index - a.length] = value;
319− }
320− }
321−
322− // Creates a node or leaf with a given length at their arrays for perfomance.
323− // Is only used by shuffle.
324− function createNode(height, length) {
325− if (length < 0) { length = 0; }
326− var a = { ctor:"_Array", _0:height, _1:new Array(length) };
327− if (height > 0) {
328− a._2 = new Array(length);
329− }
330− return a;
331− }
332−
333− function saveSlot(a, b, index, slot) {
334− set2(a._1, b._1, index, slot);
335−
336− var l = (index == 0 || index == a._2.length) ?
337− 0 : get2(a._2, a._2, index - 1);
338− set2(a._2, b._2, index, l + length(slot));
339− }
340−
341− // Returns an array of two balanced nodes.
342− function shuffle(a, b, toRemove) {
343− var newA = createNode(a._0, Math.min(M, a._1.length + b._1.length - toRemove));
344− var newB = createNode(a._0, newA._1.length - (a._1.length + b._1.length - toRemove));
345−
346− // Skip the slots with size M. More precise: copy the slot references
347− // to the new node
348− var read = 0;
349− while (get2(a._1, b._1, read)._1.length % M == 0) {
350− set2(newA._1, newB._1, read, get2(a._1, b._1, read));
351− set2(newA._2, newB._2, read, get2(a._2, b._2, read));
352− read++;
353− }
354−
355− // Pulling items from left to right, caching in a slot before writing
356− // it into the new nodes.
357− var write = read;
358− var slot = new createNode(a._0 - 1, 0);
359− var from = 0;
360−
361− // If the current slot is still containing data, then there will be at
362− // least one more write, so we do not break this loop yet.
363− while (read - write - (slot._1.length > 0 ? 1 : 0) < toRemove) {
364− // Find out the max possible items for copying.
365− var source = get2(a._1, b._1, read);
366− var to = Math.min(M - slot._1.length, source._1.length)
367−
368− // Copy and adjust size table.
369− slot._1 = slot._1.concat(source._1.slice(from, to));
370− if (slot._0 > 0) {
371− var len = slot._2.length;
372− for (var i = len; i < len + to - from; i++) {
373− slot._2[i] = length(slot._1[i]);
374− slot._2[i] += (i > 0 ? slot._2[i - 1] : 0);
375− }
376− }
377−
378− from += to;
379−
380− // Only proceed to next slots[i] if the current one was
381− // fully copied.
382− if (source._1.length <= to) {
383− read++; from = 0;
384− }
385−
386− // Only create a new slot if the current one is filled up.
387− if (slot._1.length == M) {
388− saveSlot(newA, newB, write, slot);
389− slot = createNode(a._0 - 1,0);
390− write++;
391− }
392− }
393−
394− // Cleanup after the loop. Copy the last slot into the new nodes.
395− if (slot._1.length > 0) {
396− saveSlot(newA, newB, write, slot);
397− write++;
398− }
399−
400− // Shift the untouched slots to the left
401− while (read < a._1.length + b._1.length ) {
402− saveSlot(newA, newB, write, get2(a._1, b._1, read));
403− read++; write++;
404− }
405−
406− return [newA, newB];
407− }
408−
409− // Helper functions
410− function botRight(a) { return a._1[a._1.length - 1]; }
411− function botLeft(a) { return a._1[0]; }
412−
413− // Copies a node for updating. Note that you should not use
414− // this if only updating one of _1 and _2 for performance reasons.
415− function nodeCopy(a) {
416− var newA = { ctor:"_Array", _0:a._0
417− , _1:a._1.slice() };
418− if (a._0 > 0) { newA._2 = a._2.slice(); }
419− return newA;
420− }
421−
422− // Returns how many items are in the tree.
423− function length(a) {
424− if (a._0 == 0) {
425− return a._1.length;
426− } else {
427− return a._2[a._2.length - 1];
428− }
429− }
430−
431− // Calculates in which slot the item probably is, then
432− // find the exact slot in the size table ._2. Returns the index.
433− function getSlot(i, a) {
434− var slot = Math.floor(i / (Math.pow(M, a._0)));
435− while (a._2[slot] <= i) {
436− slot++
437− }
438− return slot;
439− }
440−
441− // Recursively creates a tree with a given height containing
442− // only the given item.
443− function create(item, height) {
444− if (height == 0) {
445− return { ctor:"_Array", _0:0
446− , _1:[item] };
447− } else {
448− return { ctor:"_Array", _0:height
449− , _1:[create(item, height - 1)]
450− , _2:[1] };
451− }
452− }
453−
454− // Recursively creates a tree that contains the given tree.
455− function parentise(tree, height) {
456− if (height == tree._0) {
457− return tree;
458− } else {
459− return { ctor:"_Array", _0:height
460− , _1:[parentise(tree, height - 1)]
461− , _2:[length(tree)] };
462− }
463− }
464−
465− // Emphasizes blood brotherhood beneath two trees.
466− function siblise(a, b) {
467− return { ctor:"_Array", _0:a._0 + 1
468− , _1:[a, b]
469− , _2:[length(a), length(a) + length(b)] };
470− }
471−
472− Elm.Native.Array.values = {
473− empty:empty,
474− elements:elements,
475− concat:F2(concat),
476− push:F2(push),
477− slice:F3(slice),
478− get:F2(get),
479− set:F3(set),
480− map:F2(map),
481− assocMap:F2(assocMap),
482− foldl:F3(foldl),
483− foldr:F3(foldr),
484− length:length
485− };
486−
487− return elm.Native.Array.values = Elm.Native.Array.values;
488−}
libraries/Native/Graphics/Input.js−24
@@ -137,8 +137,6 @@ Elm.Native.Graphics.Input.make = function(elm) {
137137 var clicker = newNode('div');
138138 clicker.style.width = btn.elm_up.style.width;
139139 clicker.style.height = btn.elm_up.style.height;
140− clicker.style.position = 'absolute';
141− clicker.style.top = 0;
142140 btn.appendChild(clicker);
143141
144142 return btn;
@@ -297,29 +295,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
297295 }));
298296 }
299297
300− function mouseUpdate(event) {
301− var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
302− elm.notify(field.elm_signal.id, field.elm_handler({
303− _:{},
304− string: field.value,
305− selection: {
306− _:{},
307− start: field.selectionStart,
308− end: field.selectionEnd,
309− direction: { ctor: direction }
310− },
311− }));
312− }
313− function mousedown(event) {
314− mouseUpdate(event);
315− elm.node.addEventListener('mouseup', mouseup);
316− }
317− function mouseup(event) {
318− mouseUpdate(event);
319− elm.node.removeEventListener('mouseup', mouseup)
320− }
321298 field.addEventListener('input', inputUpdate);
322− field.addEventListener('mousedown', mousedown);
323299 field.addEventListener('focus', function() {
324300 field.elm_hasFocus = true;
325301 });
libraries/Native/Text.js+1
@@ -153,6 +153,7 @@ Elm.Native.Text.make = function(elm) {
153153 typeface : F2(typeface),
154154 color : F2(color),
155155 link : F2(link),
156+ style : F2(style),
156157
157158 leftAligned : block('left'),
158159 rightAligned : block('right'),
libraries/Native/Utils.js+13−8
@@ -30,11 +30,15 @@ Elm.Native.Utils.make = function(elm) {
3030 function compare(x,y) { return { ctor: ord[cmp(x,y)+1] } }
3131 function cmp(x,y) {
3232 var ord;
33− if (typeof x !== 'object' || x instanceof String){
33+ if (typeof x !== 'object'){
3434 return x === y ? EQ : x < y ? LT : GT;
3535 }
36−
37− if (x.ctor === "::" || x.ctor === "[]") {
36+ else if (x.isChar){
37+ var a = x.toString();
38+ var b = y.toString();
39+ return a === b ? EQ : a < b ? LT : GT;
40+ }
41+ else if (x.ctor === "::" || x.ctor === "[]") {
3842 while (true) {
3943 if (x.ctor === "[]" && y.ctor === "[]") return EQ;
4044 if (x.ctor !== y.ctor) return x.ctor === '[]' ? LT : GT;
@@ -44,8 +48,7 @@ Elm.Native.Utils.make = function(elm) {
4448 y = y._1;
4549 }
4650 }
47−
48− if (x.ctor.slice(0,6) === '_Tuple') {
51+ else if (x.ctor.slice(0,6) === '_Tuple') {
4952 var n = x.ctor.slice(6) - 0;
5053 var err = 'cannot compare tuples with more than 6 elements.';
5154 if (n === 0) return EQ;
@@ -58,9 +61,11 @@ Elm.Native.Utils.make = function(elm) {
5861 if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } }
5962 return EQ;
6063 }
61− throw new Error('Comparison error: comparison is only defined on ints, ' +
62− 'floats, times, chars, strings, lists of comparable values, ' +
63− 'and tuples of comparable values.')
64+ else {
65+ throw new Error('Comparison error: comparison is only defined on ints, ' +
66+ 'floats, times, chars, strings, lists of comparable values, ' +
67+ 'and tuples of comparable values.');
68+ }
6469 }
6570
6671
libraries/Signal.elm+8−10
@@ -120,20 +120,18 @@ initially. -}
120120dropIf : (a -> Bool) -> a -> Signal a -> Signal a
121121dropIf = Native.Signal.dropIf
122122
123−{-| Keep events only when the first signal is true. When the first signal
124−becomes true, the most recent value of the second signal will be propagated.
125−Until the first signal becomes false again, all events will be propagated. Elm
126−does not allow undefined signals, so a base case must be provided in case the
127−first signal is not true initially. -}
123+{-| Keep events only when the first signal is true. Elm does not allow undefined
124+signals, so a base case must be provided in case the first signal is not true
125+initially.
126+-}
128127keepWhen : Signal Bool -> a -> Signal a -> Signal a
129128keepWhen bs def sig =
130129 snd <~ (keepIf fst (False, def) ((,) <~ (sampleOn sig bs) ~ sig))
131130
132−{-| Drop events when the first signal is true. When the first signal becomes
133−false, the most recent value of the second signal will be propagated. Until the
134−first signal becomes true again, all events will be propagated. Elm does not
135−allow undefined signals, s oa base case must be provided in case the first
136−signal is true initially. -}
131+{-| Drop events when the first signal is true. Elm does not allow undefined
132+signals, so a base case must be provided in case the first signal is true
133+initially.
134+-}
137135dropWhen : Signal Bool -> a -> Signal a -> Signal a
138136dropWhen bs = keepWhen (not <~ bs)
139137
libraries/String.elm+1−1
@@ -114,7 +114,7 @@ foldr = Native.String.foldr
114114{-| Split a string using a given separator.
115115
116116 split "," "cat,dog,cow" == ["cat","dog","cow"]
117− split "/" "home/evan/Desktop/" == ["home","evan","Desktop"]
117+ split "/" "home/evan/Desktop/" == ["home","evan","Desktop", ""]
118118
119119Use `Regex.split` if you need something more flexible.
120120-}
runtime/Render/Element.js+5−2
@@ -45,8 +45,9 @@ function setProps(elem, e) {
4545function addClick(e, handler) {
4646 e.style.pointerEvents = 'auto';
4747 e.elm_click_handler = handler;
48− function trigger() {
48+ function trigger(ev) {
4949 e.elm_click_handler(Utils.Tuple0);
50+ ev.stopPropagation();
5051 }
5152 e.elm_click_trigger = trigger;
5253 e.addEventListener('click', trigger);
@@ -63,14 +64,16 @@ function addHover(e, handler) {
6364 e.elm_hover_handler = handler;
6465 e.elm_hover_count = 0;
6566
66− function over() {
67+ function over(evt) {
6768 if (e.elm_hover_count++ > 0) return;
6869 e.elm_hover_handler(true);
70+ evt.stopPropagation();
6971 }
7072 function out(evt) {
7173 if (e.contains(evt.toElement || evt.relatedTarget)) return;
7274 e.elm_hover_count = 0;
7375 e.elm_hover_handler(false);
76+ evt.stopPropagation();
7477 }
7578 e.elm_hover_over = over;
7679 e.elm_hover_out = out;
tests/elm/Test.elmnew file+27
@@ -0,0 +1,27 @@
1+module Main where
2+
3+import ElmTest.Assertion as A
4+import ElmTest.Run as R
5+import ElmTest.Runner.Console (runDisplay)
6+import ElmTest.Test (..)
7+
8+import IO.IO (..)
9+import IO.Runner (Request, Response)
10+import IO.Runner as Run
11+
12+import Test.String as String
13+import Test.Trampoline as Trampoline
14+
15+tests : [Test]
16+tests = String.tests ++ Trampoline.tests
17+
18+console : IO ()
19+console = runDisplay tests
20+
21+port requests : Signal [{ mPut : Maybe String
22+ , mExit : Maybe Int
23+ , mGet : Bool
24+ }]
25+port requests = Run.run responses console
26+
27+port responses : Signal (Maybe String)
tests/elm/Test/String.elmnew file+26
@@ -0,0 +1,26 @@
1+module Test.String (tests) where
2+
3+import List
4+import String (..)
5+
6+import ElmTest.Assertion (..)
7+import ElmTest.Test (..)
8+
9+tests : [Test]
10+tests =
11+ let isEmptyTests = [test "empty is empty" <| assert <| isEmpty "",
12+ test "not empty" <| assert <| not . isEmpty <| "the world"]
13+ lengthTest = test "length of innumerable" <| assertEqual 11 (length "innumerable")
14+ reverseTest = test "reverse test" <| assertEqual "desserts" (reverse "stressed")
15+ repeatTest = test "repeat test" <| assertEqual "hahaha" (repeat 3 "ha")
16+ unconsTests = [test "nonempty uncons" <| assertEqual (Just ('a',"bc")) (uncons "abc"),
17+ test "empty uncons" <| assertEqual Nothing (uncons "")]
18+ appendTest = test "append test" <| assertEqual "butterfly" (append "butter" "fly")
19+ concatTest = test "concat test" <| assertEqual "nevertheless" (concat ["never","the","less"])
20+ splitTests = [test "split commas" <| assertEqual ["cat","dog","cow"] (split "," "cat,dog,cow"),
21+ test "split slashes"<| assertEqual ["home","evan","Desktop", ""] (split "/" "home/evan/Desktop/")]
22+ joinTests = [test "join spaces" <| assertEqual "cat dog cow" (join " " ["cat","dog","cow"]),
23+ test "join slashes" <| assertEqual "home/evan/Desktop" (join "/" ["home","evan","Desktop"])]
24+
25+ endsWithTest = test "endsWith Check" (assert <| endsWith "h" "th")
26+ in List.concat [isEmptyTests, [lengthTest, reverseTest, repeatTest], unconsTests, [appendTest, concatTest], splitTests, joinTests, [endsWithTest]]
tests/elm/Test/Trampoline.elmnew file+29
@@ -0,0 +1,29 @@
1+module Test.Trampoline (tests) where
2+
3+import Trampoline (..)
4+
5+import ElmTest.Assertion (..)
6+import ElmTest.Test (..)
7+
8+badSum : Int -> Int
9+badSum n =
10+ let loop x acc =
11+ case x of
12+ 0 -> acc
13+ _ -> loop (x-1) (acc+x)
14+ in loop n 0
15+
16+goodSum : Int -> Int
17+goodSum n =
18+ let sumT x acc =
19+ case x of
20+ 0 -> Done acc
21+ _ -> Continue (\_ -> sumT (x-1) (acc+x))
22+ in trampoline <| sumT n 0
23+
24+tests : [Test]
25+tests =
26+ let mkLoopCheck n = test ("Equivalent Loop Check #"++ show n) (assertEqual (badSum n) (goodSum n))
27+ loopChecks = map mkLoopCheck [0..25]
28+ noStackOverflow = test "Trampoline Deep Recursion Test" (assertEqual 500000500000 (goodSum 1000000))
29+ in noStackOverflow :: loopChecks
tests/Main.hs → tests/hs/CompilerTest.hsrenamed

The content is the same.

tests/hs/LibTest.hsnew file+27
@@ -0,0 +1,27 @@
1+module Main where
2+
3+import System.Directory
4+import System.Exit
5+import System.FilePath
6+import System.Process
7+
8+import Elm.Internal.Paths as Elm
9+
10+main :: IO ()
11+main = do
12+ top <- getCurrentDirectory
13+ let ioScript s = top</>"IO"</>s
14+ runCmd "git submodule init"
15+ runCmd "npm install jsdom"
16+ setCurrentDirectory $ top</>"tests"</>"elm"
17+ runCmd $ concat [top</>"dist"</>"build"</>"elm"</>"elm --make --only-js --src-dir=" , top</>"automaton", " --src-dir=", top</>"IO", " --src-dir=", top</>"Elm-Test", " Test.elm"]
18+ runCmd $ unwords ["cat ", ioScript "prescript.js", Elm.runtime, "build"</>"Test.js", ioScript "handler.js", "> exe.js"]
19+ exitWith =<< waitForProcess =<< (runCommand "node exe.js")
20+ where runCmd cmd = do
21+ putStrLn cmd
22+ exitCode <- waitForProcess =<< runCommand cmd
23+ case exitCode of
24+ ExitSuccess -> return ()
25+ ExitFailure _ -> error "something went wrong"
26+
27+
tests/Tests/Compiler.hs → tests/hs/Tests/Compiler.hsrenamed

The content is the same.

tests/Tests/Property.hs → tests/hs/Tests/Property.hsrenamed

The content is the same.

tests/Tests/Property/Arbitrary.hs → tests/hs/Tests/Property/Arbitrary.hsrenamed

The content is the same.

tests/test-files/bad/BBTArgs → tests/hs/test-files/bad/BBTArgsrenamed

The content is the same.

tests/test-files/bad/InfiniteType.elm → tests/hs/test-files/bad/InfiniteType.elmrenamed

The content is the same.

tests/test-files/bad/NonElementMain.elm → tests/hs/test-files/bad/NonElementMain.elmrenamed

The content is the same.

tests/test-files/bad/Strings/ExtraClose.elm → tests/hs/test-files/bad/Strings/ExtraClose.elmrenamed

The content is the same.

tests/test-files/good/AliasSubstitution.elm → tests/hs/test-files/good/AliasSubstitution.elmrenamed

The content is the same.

tests/test-files/good/NoExpressions.elm → tests/hs/test-files/good/NoExpressions.elmrenamed

The content is the same.

tests/test-files/good/Otherwise.elm → tests/hs/test-files/good/Otherwise.elmrenamed

The content is the same.

tests/test-files/good/Ports.elm → tests/hs/test-files/good/Ports.elmrenamed

The content is the same.

tests/test-files/good/QuotesAndComments.elm → tests/hs/test-files/good/QuotesAndComments.elmrenamed

The content is the same.

tests/test-files/good/Soundness/Apply.elm → tests/hs/test-files/good/Soundness/Apply.elmrenamed

The content is the same.

tests/test-files/good/Soundness/ApplyAnnotated.elm → tests/hs/test-files/good/Soundness/ApplyAnnotated.elmrenamed

The content is the same.

tests/test-files/good/Soundness/Id.elm → tests/hs/test-files/good/Soundness/Id.elmrenamed

The content is the same.

tests/test-files/good/Soundness/IdAnnotated.elm → tests/hs/test-files/good/Soundness/IdAnnotated.elmrenamed

The content is the same.

tests/test-files/good/Soundness/TrickyId.elm → tests/hs/test-files/good/Soundness/TrickyId.elmrenamed

The content is the same.

tests/test-files/good/Soundness/TrickyIdAnnotated.elm → tests/hs/test-files/good/Soundness/TrickyIdAnnotated.elmrenamed

The content is the same.

tests/test-files/good/Strings/Multiline.elm → tests/hs/test-files/good/Strings/Multiline.elmrenamed

The content is the same.

tests/test-files/good/Strings/MultilineNormal.elm → tests/hs/test-files/good/Strings/MultilineNormal.elmrenamed

The content is the same.

tests/test-files/good/Unify/LockedVars.elm → tests/hs/test-files/good/Unify/LockedVars.elmrenamed

The content is the same.

tests/test-files/good/Unify/NonHomogeneousRecords.elm → tests/hs/test-files/good/Unify/NonHomogeneousRecords.elmrenamed

The content is the same.