on master help sign in
Introduce the HowMany type to simplify API
f0f84cbEvan Czaplicki
libraries/Native/Regex.js+10−19
@@ -15,7 +15,7 @@ Elm.Native.Regex.make = function(elm) {
15 function caseInsensitive(re) {
15 function caseInsensitive(re) {
16 return new RegExp(re.source, 'gi');
16 return new RegExp(re.source, 'gi');
17 }
17 }
18− function pattern(raw) {
18+ function regex(raw) {
19 return new RegExp(raw, 'g');
19 return new RegExp(raw, 'g');
20 }
20 }
21
21
@@ -23,10 +23,8 @@ Elm.Native.Regex.make = function(elm) {
23 return re.test(JS.fromString(string));
23 return re.test(JS.fromString(string));
24 }
24 }
25
25
26− function findAll(re, string) {
27− return find(Infinity, re, string);
28− }
29 function find(n, re, str) {
26 function find(n, re, str) {
27+ n = n.ctor === "All" ? Infinity : n._0;
30 var out = [];
28 var out = [];
31 var number = 0;
29 var number = 0;
32 var string = JS.fromString(str);
30 var string = JS.fromString(str);
@@ -51,10 +49,8 @@ Elm.Native.Regex.make = function(elm) {
51 return JS.toList(out);
49 return JS.toList(out);
52 }
50 }
53
51
54− function replaceAll(re, replacer, string) {
55− return replace(Infinity, re, replacer, string);
56− }
57 function replace(n, re, replacer, string) {
52 function replace(n, re, replacer, string) {
53+ n = n.ctor === "All" ? Infinity : n._0;
58 var count = 0;
54 var count = 0;
59 function jsReplacer(match) {
55 function jsReplacer(match) {
60 if (count++ > n) return match;
56 if (count++ > n) return match;
@@ -77,10 +73,10 @@ Elm.Native.Regex.make = function(elm) {
77 return string.replace(re, jsReplacer);
73 return string.replace(re, jsReplacer);
78 }
74 }
79
75
80− function split(re, string) {
76+ function split(n, re, str) {
77+ if (n === Infinity) {
81− return JS.toList(JS.fromString(string).split(re));
78+ return JS.toList(JS.fromString(string).split(re));
82− }
79+ }
83− function splitN(n, re, str) {
84 var string = JS.fromString(str);
80 var string = JS.fromString(str);
85 var result;
81 var result;
86 var out = [];
82 var out = [];
@@ -95,18 +91,13 @@ Elm.Native.Regex.make = function(elm) {
95 }
91 }
96
92
97 return Elm.Native.Regex.values = {
93 return Elm.Native.Regex.values = {
98− pattern: pattern,
94+ regex: regex,
99 caseInsensitive: caseInsensitive,
95 caseInsensitive: caseInsensitive,
100 escape: escape,
96 escape: escape,
101
97
102 contains: F2(contains),
98 contains: F2(contains),
103− findAll: F2(findAll),
104 find: F3(find),
99 find: F3(find),
105−
106− replaceAll: F3(replaceAll),
107 replace: F4(replace),
100 replace: F4(replace),
108−
109− split: F2(split),
101+ split: F3(split),
110− splitN: F3(splitN),
111 };
102 };
112−};
103+};
libraries/Regex.elm+49−56
@@ -3,16 +3,17 @@ module Regex where
3same kind of regular expressions accepted by JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions).
3same kind of regular expressions accepted by JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions).
4
4
5# Create
5# Create
6−@docs pattern, caseInsensitive, escape
6+@docs regex, escape, caseInsensitive
7
7
8−# Match
8+# Helpful Data Structures
9−@docs Match
10
9
11−# Find and Replace
10+These data structures are needed to help define functions like [`find`](#find)
12−@docs contains, find, findAll, replace, replaceAll
11+and [`replace`](#replace).
13
12
14−# Split
13+@docs HowMany, Match
14+
15+# Use
15−@docs split, splitN
16+@docs contains, find, replace, split
16
17
17-}
18-}
18
19
@@ -21,31 +22,35 @@ import Native.Regex
21
22
22data Regex = Regex
23data Regex = Regex
23
24
24−{-| Escape all special characters. So `pattern (escape "$$$")`
25+{-| Escape strings to be regular expressions, making all special characters
25−will match exactly `"$$$"` even though `$` is a special character.
26+safe. So `regex (escape "^a+")` will match exactly `"^a+"` instead of a series
27+of `a`’s that start at the beginning of the line.
26-}
28-}
27escape : String -> String
29escape : String -> String
28escape = Native.Regex.escape
30escape = Native.Regex.escape
29
31
30{-| Create a Regex that matches patterns [as specified in JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Writing_a_Regular_Expression_Pattern).
32{-| Create a Regex that matches patterns [as specified in JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Writing_a_Regular_Expression_Pattern).
33+
31−Be careful to escape backslashes properly!
34+Be careful to escape backslashes properly! For example, `"\w"` is escaping the
35+letter `w` which is probably not what you want. You probably want `"\\w"`
36+instead, which escapes the backslash.
32-}
37-}
33−pattern : String -> Regex
38+regex : String -> Regex
34−pattern = Native.Regex.pattern
39+regex = Native.Regex.regex
35
40
36
41
37−{-| Make a pattern case insensitive -}
42+{-| Make a regex case insensitive -}
38caseInsensitive : Regex -> Regex
43caseInsensitive : Regex -> Regex
39caseInsensitive = Native.Regex.caseInsensitive
44caseInsensitive = Native.Regex.caseInsensitive
40
45
41{-| Check to see if a Regex is contained in a string.
46{-| Check to see if a Regex is contained in a string.
42
47
43```haskell
48```haskell
44− contains (pattern "123") "12345" == True
49+ contains (regex "123") "12345" == True
45− contains (pattern "b+") "aabbcc" == True
50+ contains (regex "b+") "aabbcc" == True
46
51
47− contains (pattern "789") "12345" == False
52+ contains (regex "789") "12345" == False
48− contains (pattern "z+") "aabbcc" == False
53+ contains (regex "z+") "aabbcc" == False
49```
54```
50-}
55-}
51contains : Regex -> String -> Bool
56contains : Regex -> String -> Bool
@@ -55,79 +60,67 @@ contains = Native.Regex.contains
55Here are details on each field:
60Here are details on each field:
56
61
57 * `match` — the full string of the match.
62 * `match` — the full string of the match.
58− * `submatches` — a pattern might have [subpatterns, surrounded by
63+ * `submatches` — a regex might have [subpatterns, surrounded by
59 parentheses](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Parenthesized_Substring_Matches).
64 parentheses](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Parenthesized_Substring_Matches).
60 If there are N subpatterns, there will be N elements in the `submatches` list.
65 If there are N subpatterns, there will be N elements in the `submatches` list.
61 Each submatch in this list is a `Maybe` because not all subpatterns may trigger.
66 Each submatch in this list is a `Maybe` because not all subpatterns may trigger.
62− For example, `(pattern "(a+)|(b+)")` will either match many `a`’s or
67+ For example, `(regex "(a+)|(b+)")` will either match many `a`’s or
63 many `b`’s, but never both.
68 many `b`’s, but never both.
64 * `index` — the index of the match in the original string.
69 * `index` — the index of the match in the original string.
65 * `number` — if you find many matches, you can think of each one
70 * `number` — if you find many matches, you can think of each one
66 as being labeled with a `number` starting at one. So the first time you
71 as being labeled with a `number` starting at one. So the first time you
67 find a match, that is match `number` one. Second time is match `number` two.
72 find a match, that is match `number` one. Second time is match `number` two.
68− This is useful when paired with `replaceAll` if replacement is dependent on how
73+ This is useful when paired with `replace All` if replacement is dependent on how
69 many times a pattern has appeared before.
74 many times a pattern has appeared before.
70-}
75-}
71type Match = { match : String, submatches : [Maybe String], index : Int, number : Int }
76type Match = { match : String, submatches : [Maybe String], index : Int, number : Int }
72
77
78+{-| `HowMany` is used to specify how many matches you want to make. So
79+`replace All` would replace every match, but `replace (AtMost 2)` would
80+replace at most two matches (i.e. zero, one, two, but never three or more).
81+-}
82+data HowMany = All | AtMost Int
83+
73−{-| Find all of the matches in a string:
84+{-| Find matches in a string:
74
85
75```haskell
86```haskell
76− words = findAll (pattern "\\w+") "hello world"
87+ findTwoCommas = find (AtMost 2) (regex ",")
77
88
78− map .match words == ["hello","world"]
89+ -- map .index (findTwoCommas "a,b,c,d,e") == [1,3]
79− map .index words == [0,6]
90+ -- map .index (findTwoCommas "a b c d e") == []
80
91
81− places = findAll (pattern "[oi]n a (\\w+)") "I am on a boat in a lake."
92+ places = find All (regex "[oi]n a (\\w+)") "I am on a boat in a lake."
82
93
83− map .match places== ["on a boat", "in a lake"]
94+ -- map .match places == ["on a boat", "in a lake"]
84− map .submatches places == [ [Just "boat"], [Just "lake"] ]
95+ -- map .submatches places == [ [Just "boat"], [Just "lake"] ]
85```
96```
86-}
97-}
87−findAll : Regex -> String -> [Match]
88−findAll = Native.Regex.findAll
89−
90−{-| Same as `findAll`, but `find` will quit searching after the *n<sup>th</sup>* match.
91−That means the resulting list has maximum length N, but *it can be shorter*
92−if there are not that many matches in the given string.
93−-}
94−find : Int -> Regex -> String -> [Match]
98+find : HowMany -> Regex -> String -> [Match]
95find = Native.Regex.find
99find = Native.Regex.find
96
100
97−{-| Replace all matches. The function from `Match` to `String` lets
101+{-| Replace matches. The function from `Match` to `String` lets
98you use the details of a specific match when making replacements.
102you use the details of a specific match when making replacements.
99
103
100```haskell
104```haskell
101− devowel = replaceAll (pattern "[aeiou]") (\_ -> "")
105+ devowel = replace All (regex "[aeiou]") (\_ -> "")
102
106
103− devowel "The quick brown fox" == "Th qck brwn fx"
107+ -- devowel "The quick brown fox" == "Th qck brwn fx"
104
108
105− reverseWords = replaceAll (pattern "\\w+") (\{match} -> String.reverse match)
109+ reverseWords = replace All (regex "\\w+") (\{match} -> String.reverse match)
106
110
107− reverseWords "deliver mined parts" == "reviled denim strap"
111+ -- reverseWords "deliver mined parts" == "reviled denim strap"
108```
112```
109-}
113-}
110−replaceAll : Regex -> (Match -> String) -> String -> String
111−replaceAll = Native.Regex.replaceAll
112−
113−{-| Same as `replaceAll`, but `replace` will quit after the *n<sup>th</sup>* match.-}
114−replace : Int -> Regex -> (Match -> String) -> String -> String
114+replace : HowMany -> Regex -> (Match -> String) -> String -> String
115replace = Native.Regex.replace
115replace = Native.Regex.replace
116
116
117{-| Split a string, using the regex as the separator.
117{-| Split a string, using the regex as the separator.
118
118
119```haskell
119```haskell
120− split (pattern " *, *") "a ,b, c,d" == ["a","b","c","d"]
120+ split (AtMost 1) (regex ",") "tom,99,90,85" == ["tom","99,90,85"]
121−```
122−-}
123−split : Regex -> String -> [String]
124−split = Native.Regex.split
125
121
126−{-| Same as `split` but stops after the *n<sup>th</sup>* match.
122+ split All (regex ",") "a,b,c,d" == ["a","b","c","d"]
127−
128−```haskell
129− splitN 1 (pattern ": *") "tom: 99,90,85" == ["tom","99,90,85"]
130```
123```
131-}
124-}
132−splitN : Int -> Regex -> String -> [String]
125+split : HowMany -> Regex -> String -> [String]
133−splitN = Native.Regex.splitN
126+split = Native.Regex.split