|
| 1 | +//// This package uses the regular expression engine of the underlying platform. |
| 2 | +//// Regular expressions in Erlang and JavaScript largely share the same syntax, but |
| 3 | +//// there are some differences and have different performance characteristics. Be |
| 4 | +//// sure to thoroughly test your code on all platforms that you support when using |
| 5 | +//// this library. |
| 6 | + |
| 7 | +import gleam/option.{type Option} |
| 8 | + |
| 9 | +pub type Regexp |
| 10 | + |
| 11 | +/// The details about a particular match: |
| 12 | +/// |
| 13 | +pub type Match { |
| 14 | + Match( |
| 15 | + /// The full string of the match. |
| 16 | + content: String, |
| 17 | + /// A `Regexp` can have subpatterns, sup-parts that are in parentheses. |
| 18 | + submatches: List(Option(String)), |
| 19 | + ) |
| 20 | +} |
| 21 | + |
| 22 | +/// When a regular expression fails to compile: |
| 23 | +/// |
| 24 | +pub type CompileError { |
| 25 | + CompileError( |
| 26 | + /// The problem encountered that caused the compilation to fail |
| 27 | + error: String, |
| 28 | + /// The byte index into the string to where the problem was found |
| 29 | + /// This value may not be correct in JavaScript environments. |
| 30 | + byte_index: Int, |
| 31 | + ) |
| 32 | +} |
| 33 | + |
| 34 | +pub type Options { |
| 35 | + Options(case_insensitive: Bool, multi_line: Bool) |
| 36 | +} |
| 37 | + |
| 38 | +/// Creates a `Regexp` with some additional options. |
| 39 | +/// |
| 40 | +/// ## Examples |
| 41 | +/// |
| 42 | +/// ```gleam |
| 43 | +/// let options = Options(case_insensitive: False, multi_line: True) |
| 44 | +/// let assert Ok(re) = compile("^[0-9]", with: options) |
| 45 | +/// check(re, "abc\n123") |
| 46 | +/// // -> True |
| 47 | +/// ``` |
| 48 | +/// |
| 49 | +/// ```gleam |
| 50 | +/// let options = Options(case_insensitive: True, multi_line: False) |
| 51 | +/// let assert Ok(re) = compile("[A-Z]", with: options) |
| 52 | +/// check(re, "abc123") |
| 53 | +/// // -> True |
| 54 | +/// ``` |
| 55 | +/// |
| 56 | +pub fn compile( |
| 57 | + pattern: String, |
| 58 | + with options: Options, |
| 59 | +) -> Result(Regexp, CompileError) { |
| 60 | + do_compile(pattern, options) |
| 61 | +} |
| 62 | + |
| 63 | +@external(erlang, "gleam_regexp_ffi", "compile") |
| 64 | +@external(javascript, "../gleam_regexp_ffi.mjs", "compile") |
| 65 | +fn do_compile( |
| 66 | + pattern: String, |
| 67 | + with with: Options, |
| 68 | +) -> Result(Regexp, CompileError) |
| 69 | + |
| 70 | +/// Creates a new `Regexp`. |
| 71 | +/// |
| 72 | +/// ## Examples |
| 73 | +/// |
| 74 | +/// ```gleam |
| 75 | +/// let assert Ok(re) = from_string("[0-9]") |
| 76 | +/// check(re, "abc123") |
| 77 | +/// // -> True |
| 78 | +/// ``` |
| 79 | +/// |
| 80 | +/// ```gleam |
| 81 | +/// check(re, "abcxyz") |
| 82 | +/// // -> False |
| 83 | +/// ``` |
| 84 | +/// |
| 85 | +/// ```gleam |
| 86 | +/// from_string("[0-9") |
| 87 | +/// // -> Error(CompileError( |
| 88 | +/// // error: "missing terminating ] for character class", |
| 89 | +/// // byte_index: 4 |
| 90 | +/// // )) |
| 91 | +/// ``` |
| 92 | +/// |
| 93 | +pub fn from_string(pattern: String) -> Result(Regexp, CompileError) { |
| 94 | + compile(pattern, Options(case_insensitive: False, multi_line: False)) |
| 95 | +} |
| 96 | + |
| 97 | +/// Returns a boolean indicating whether there was a match or not. |
| 98 | +/// |
| 99 | +/// ## Examples |
| 100 | +/// |
| 101 | +/// ```gleam |
| 102 | +/// let assert Ok(re) = from_string("^f.o.?") |
| 103 | +/// check(with: re, content: "foo") |
| 104 | +/// // -> True |
| 105 | +/// ``` |
| 106 | +/// |
| 107 | +/// ```gleam |
| 108 | +/// check(with: re, content: "boo") |
| 109 | +/// // -> False |
| 110 | +/// ``` |
| 111 | +/// |
| 112 | +pub fn check(with regexp: Regexp, content string: String) -> Bool { |
| 113 | + do_check(regexp, string) |
| 114 | +} |
| 115 | + |
| 116 | +@external(erlang, "gleam_regexp_ffi", "check") |
| 117 | +@external(javascript, "../gleam_regexp_ffi.mjs", "check") |
| 118 | +fn do_check(regexp: Regexp, string: String) -> Bool |
| 119 | + |
| 120 | +/// Splits a string. |
| 121 | +/// |
| 122 | +/// ## Examples |
| 123 | +/// |
| 124 | +/// ```gleam |
| 125 | +/// let assert Ok(re) = from_string(" *, *") |
| 126 | +/// split(with: re, content: "foo,32, 4, 9 ,0") |
| 127 | +/// // -> ["foo", "32", "4", "9", "0"] |
| 128 | +/// ``` |
| 129 | +/// |
| 130 | +pub fn split(with regexp: Regexp, content string: String) -> List(String) { |
| 131 | + do_split(regexp, string) |
| 132 | +} |
| 133 | + |
| 134 | +@external(erlang, "gleam_regexp_ffi", "split") |
| 135 | +@external(javascript, "../gleam_regexp_ffi.mjs", "split") |
| 136 | +fn do_split(regexp: Regexp, string: String) -> List(String) |
| 137 | + |
| 138 | +/// Collects all matches of the regular expression. |
| 139 | +/// |
| 140 | +/// ## Examples |
| 141 | +/// |
| 142 | +/// ```gleam |
| 143 | +/// let assert Ok(re) = from_string("[oi]n a (\\w+)") |
| 144 | +/// scan(with: re, content: "I am on a boat in a lake.") |
| 145 | +/// // -> [ |
| 146 | +/// // Match(content: "on a boat", submatches: [Some("boat")]), |
| 147 | +/// // Match(content: "in a lake", submatches: [Some("lake")]), |
| 148 | +/// // ] |
| 149 | +/// ``` |
| 150 | +/// |
| 151 | +/// ```gleam |
| 152 | +/// let assert Ok(re) = regexp.from_string("([+|\\-])?(\\d+)(\\w+)?") |
| 153 | +/// scan(with: re, content: "-36") |
| 154 | +/// // -> [ |
| 155 | +/// // Match(content: "-36", submatches: [Some("-"), Some("36")]) |
| 156 | +/// // ] |
| 157 | +/// |
| 158 | +/// scan(with: re, content: "36") |
| 159 | +/// // -> [ |
| 160 | +/// // Match(content: "36", submatches: [None, Some("36")]) |
| 161 | +/// // ] |
| 162 | +/// ``` |
| 163 | +/// |
| 164 | +/// ```gleam |
| 165 | +/// let assert Ok(re) = |
| 166 | +/// regexp.from_string("var\\s*(\\w+)\\s*(int|string)?\\s*=\\s*(.*)") |
| 167 | +/// scan(with: re, content: "var age = 32") |
| 168 | +/// // -> [ |
| 169 | +/// // Match( |
| 170 | +/// // content: "var age = 32", |
| 171 | +/// // submatches: [Some("age"), None, Some("32")], |
| 172 | +/// // ), |
| 173 | +/// // ] |
| 174 | +/// ``` |
| 175 | +/// |
| 176 | +/// ```gleam |
| 177 | +/// let assert Ok(re) = regexp.from_string("let (\\w+) = (\\w+)") |
| 178 | +/// scan(with: re, content: "let age = 32") |
| 179 | +/// // -> [ |
| 180 | +/// // Match( |
| 181 | +/// // content: "let age = 32", |
| 182 | +/// // submatches: [Some("age"), Some("32")], |
| 183 | +/// // ), |
| 184 | +/// // ] |
| 185 | +/// |
| 186 | +/// scan(with: re, content: "const age = 32") |
| 187 | +/// // -> [] |
| 188 | +/// ``` |
| 189 | +/// |
| 190 | +pub fn scan(with regexp: Regexp, content string: String) -> List(Match) { |
| 191 | + do_scan(regexp, string) |
| 192 | +} |
| 193 | + |
| 194 | +@external(erlang, "gleam_regexp_ffi", "scan") |
| 195 | +@external(javascript, "../gleam_regexp_ffi.mjs", "scan") |
| 196 | +fn do_scan(regexp: Regexp, string: String) -> List(Match) |
| 197 | + |
| 198 | +/// Creates a new `String` by replacing all substrings that match the regular |
| 199 | +/// expression. |
| 200 | +/// |
| 201 | +/// ## Examples |
| 202 | +/// |
| 203 | +/// ```gleam |
| 204 | +/// let assert Ok(re) = regexp.from_string("^https://") |
| 205 | +/// replace(each: re, in: "https://example.com", with: "www.") |
| 206 | +/// // -> "www.example.com" |
| 207 | +/// ``` |
| 208 | +/// |
| 209 | +/// ```gleam |
| 210 | +/// let assert Ok(re) = regexp.from_string("[, +-]") |
| 211 | +/// replace(each: re, in: "a,b-c d+e", with: "/") |
| 212 | +/// // -> "a/b/c/d/e" |
| 213 | +/// ``` |
| 214 | +@external(erlang, "gleam_regexp_ffi", "replace") |
| 215 | +@external(javascript, "../gleam_regexp_ffi.mjs", "replace") |
| 216 | +pub fn replace( |
| 217 | + each pattern: Regexp, |
| 218 | + in string: String, |
| 219 | + with substitute: String, |
| 220 | +) -> String |
0 commit comments