on master help sign in
Create a Regex library
commite411990a87d058af59ca1bee28ef2850daa3f58f authorEvan Czaplicki <info@elm-lang.org> dateFri 1 Nov 2013, 23:51 (13 years ago) parentd1cee23
2 files changed+240
libraries/Native/Regex.js+112 libraries/Regex.elm+128
libraries/Native/Regex.jsnew file+112
@@ -0,0 +1,112 @@
1+Elm.Native.Regex = {};
2+Elm.Native.Regex.make = function(elm) {
3+ elm.Native = elm.Native || {};
4+ elm.Native.Regex = elm.Native.Regex || {};
5+ if (elm.Native.Regex.values) return elm.Native.Regex.values;
6+ if ('values' in Elm.Native.Regex)
7+ return elm.Native.Regex.values = Elm.Native.Regex.values;
8+
9+ var Maybe = Elm.Maybe.make(elm);
10+ var JS = Elm.JavaScript.make(elm);
11+
12+ function escape(str) {
13+ return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
14+ }
15+ function caseInsensitive(re) {
16+ return new RegExp(re.source, 'gi');
17+ }
18+ function pattern(raw) {
19+ return new RegExp(raw, 'g');
20+ }
21+
22+ function contains(re, string) {
23+ return re.test(JS.fromString(string));
24+ }
25+
26+ function find(re, string) {
27+ return findN(Infinity, re, string);
28+ }
29+ function findN(n, re, str) {
30+ var out = [];
31+ var number = 0;
32+ var string = JS.fromString(str);
33+ var result;
34+ while (number++ < n && (result = re.exec(string))) {
35+ var i = result.length - 1;
36+ var subs = new Array(i);
37+ while (i > 0) {
38+ var submatch = result[i];
39+ subs[--i] = submatch === undefined
40+ ? Maybe.Nothing
41+ : Maybe.Just(JS.toString(submatch));
42+ }
43+ out.push({
44+ _:{},
45+ match: JS.toString(result[0]),
46+ submatches: JS.toList(subs),
47+ index: result.index,
48+ number: number,
49+ });
50+ }
51+ return JS.toList(out);
52+ }
53+
54+ function replace(re, replacer, string) {
55+ return replaceN(Infinity, re, replacer, string);
56+ }
57+ function replaceN(n, re, replacer, string) {
58+ var count = 0;
59+ function jsReplacer(match) {
60+ if (count++ > n) return match;
61+ var i = arguments.length-3;
62+ var submatches = new Array(i);
63+ while (i > 0) {
64+ var submatch = arguments[i];
65+ submatches[--i] = submatch === undefined
66+ ? Maybe.Nothing
67+ : Maybe.Just(JS.toString(submatch));
68+ }
69+ return JS.fromString(replacer({
70+ _:{},
71+ match:match,
72+ submatches:JS.toList(submatches),
73+ index:arguments[i-1],
74+ number:count
75+ }));
76+ }
77+ return string.replace(re, jsReplacer);
78+ }
79+
80+ function split(re, string) {
81+ return JS.toList(JS.fromString(string).split(re));
82+ }
83+ function splitN(n, re, str) {
84+ var string = JS.fromString(str);
85+ var result;
86+ var out = [];
87+ var start = re.lastIndex;
88+ while (n--) {
89+ if (!(result = re.exec(string))) break;
90+ out.push(string.slice(start, result.index));
91+ start = re.lastIndex;
92+ }
93+ out.push(string.slice(start));
94+ return JS.toList(out);
95+ }
96+
97+ return Elm.Native.Regex.values = {
98+ pattern: pattern,
99+ caseInsensitive: caseInsensitive,
100+ escape: escape,
101+
102+ contains: F2(contains),
103+ find: F2(find),
104+ findN: F3(findN),
105+
106+ replace: F3(replace),
107+ replaceN: F4(replaceN),
108+
109+ split: F2(split),
110+ splitN: F3(splitN),
111+ };
112+};
libraries/Regex.elmnew file+128
@@ -0,0 +1,128 @@
1+module Regex where
2+{-| A library for working with regular expressions. It uses [the
3+same kind of regular expressions accepted by JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions).
4+
5+# Create
6+@docs pattern, caseInsensitive, escape
7+
8+# Match
9+@docs Match
10+
11+### Find
12+@docs contains, find, findN
13+
14+### Replace
15+@docs replace, replaceN
16+
17+### Split
18+@docs split, splitN
19+
20+-}
21+
22+import Maybe (Maybe)
23+import Native.Regex
24+
25+data Regex = Regex
26+
27+{-| Escape all special characters. So `pattern (escape "$$$")`
28+will match exactly `"$$$"` even though `$` is a special character.
29+-}
30+escape : String -> String
31+escape = Native.Regex.escape
32+
33+{-| 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).
34+Be careful to escape backslashes properly!
35+-}
36+pattern : String -> Regex
37+pattern = Native.Regex.pattern
38+
39+
40+{-| Make a pattern case insensitive -}
41+caseInsensitive : Regex -> Regex
42+caseInsensitive = Native.Regex.caseInsensitive
43+
44+{-| Check to see if a Regex is contained in a string.
45+
46+ contains (pattern "123") "12345" == True
47+ contains (pattern "b+") "aabbcc" == True
48+
49+ contains (pattern "789") "12345" == False
50+ contains (pattern "z+") "aabbcc" == False
51+-}
52+contains : Regex -> String -> Bool
53+contains = Native.Regex.contains
54+
55+{-| A `Match` represents all of the details about a particular match in a string.
56+Here are details on each field:
57+
58+ * `match` &mdash; the full string of the match.
59+ * `submatches` &mdash; a pattern might have [subpatterns, surrounded by
60+ parentheses](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Parenthesized_Substring_Matches).
61+ If there are N subpatterns, there will be N elements in the `submatches` list.
62+ Each submatch in this list is a `Maybe` because not all subpatterns may trigger.
63+ For example, `(pattern "(a+)|(b+)")` will either match many `a`&rsquo;s or
64+ many `b`&rsquo;s, but never both.
65+ * `index` &mdash; the index of the match in the original string.
66+ * `number` &mdash; if you find many matches, you can think of each one
67+ as being labeled with a `number` starting at one. So the first time you
68+ find a match, that is match `number` one. Second time is match `number` two.
69+ This is useful when paired with `replace` if replacement is dependent on how
70+ many times a pattern has appeared before.
71+-}
72+type Match = { match : String, submatches : [Maybe String], index : Int, number : Int }
73+
74+{-| Find all of the matches in a string:
75+
76+ words = find (pattern "\\w+") "hello world"
77+
78+ map .match words == ["hello","world"]
79+ map .index words == [0,6]
80+
81+ places = find (pattern "[oi]n a (\\w+)") "I am on a boat in a lake."
82+
83+ map .match places == ["on a boat", "in a lake"]
84+ map .submatches places == [ [Just "boat"], [Just "lake"] ]
85+-}
86+find : Regex -> String -> [Match]
87+find = Native.Regex.find
88+
89+{-| Same as `find`, but `findN` will quit searching after the Nth match.
90+That means the resulting list has maximum length N, but *it can be shorter*
91+if there are not that many matches in the given string.
92+-}
93+findN : Int -> Regex -> String -> [Match]
94+findN = Native.Regex.findN
95+
96+{-| Replace all matches. The function from `Match` to `String` lets
97+you use the details of a specific match when making replacements.
98+
99+ vowels = caseInsensitive (pattern "[aeiou]")
100+ devowel = replace vowels (\_ -> "")
101+
102+ devowel "The quick brown fox" == "Th qck brwn fx"
103+
104+ word = pattern "\\w+"
105+ reverseWords = replace word (\{match} -> String.reverse match)
106+
107+ reverseWords "deliver mined parts" == "reviled denim strap"
108+-}
109+replace : Regex -> (Match -> String) -> String -> String
110+replace = Native.Regex.replace
111+
112+{-| Same as `replace`, but `replaceN` will quit after the Nth match.-}
113+replaceN : Int -> Regex -> (Match -> String) -> String -> String
114+replaceN = Native.Regex.replaceN
115+
116+{-| Split a string, using the regex as the separator.
117+
118+ split (pattern ", ?") "a,b, c, d" == ["a","b","c","d"]
119+-}
120+split : Regex -> String -> [String]
121+split = Native.Regex.split
122+
123+{-| Same as `split` but stops after the Nth match.
124+
125+ splitN 1 (pattern ", ?") "a,b,c,d" == ["a","b,c,d"]
126+-}
127+splitN : Int -> Regex -> String -> [String]
128+splitN = Native.Regex.splitN