├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── README.md ├── hello.v ├── match_after.v └── match_all.v ├── match_data.v ├── pcre.v └── regex.v /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ubuntu: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout V 10 | uses: actions/checkout@v2 11 | with: 12 | repository: vlang/v 13 | path: v 14 | 15 | - name: Checkout the module 16 | uses: actions/checkout@v2 17 | with: 18 | path: regex 19 | 20 | - name: Link module to ~/.vmodules 21 | run: mkdir -p ~/.vmodules/spytheman && ln -s $(realpath regex) ~/.vmodules/spytheman/regex 22 | 23 | - name: Install libpcre3-dev package 24 | run: sudo apt-get install --quiet -y libpcre3-dev 25 | 26 | - name: Build V 27 | run: cd v && make && sudo ./v symlink && cd - 28 | 29 | - name: V doctor 30 | run: v doctor 31 | 32 | - name: Ensure everything is formatted 33 | run: v fmt -verify ~/.vmodules/spytheman/regex/ 34 | 35 | - name: Run tests 36 | run: v test ~/.vmodules/spytheman/regex/ 37 | 38 | - name: Build example with -cstrict 39 | run: v -cstrict ~/.vmodules/spytheman/regex/examples/hello.v 40 | 41 | - name: Build example with -cstrict -prod 42 | run: v -cc gcc -cstrict -prod ~/.vmodules/spytheman/regex/examples/hello.v 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | hello 2 | match_after 3 | match_all 4 | example 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Shellbear 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obsoleted by https://github.com/vlang/pcre 2 | 3 | 2021/10/18 - Please use the official `pcre` module, by `v install pcre`, then 4 | updating your code to do `import pcre` instead of `import spytheman.regex` . 5 | 6 | The API is the same, but the official one is supported, unlike this module. 7 | 8 | # v-regex 9 | 10 | A simple regex library for [VLang](https://github.com/vlang/v). 11 | It wraps the venerable [PCRE](https://www.pcre.org/) library, so 12 | you will need it installed as well. 13 | 14 | ## Prerequisites: 15 | 16 | You can install libpcre using your favourite package manager: 17 | 18 | Debian: apt-get install libpcre3-dev 19 | 20 | Fedora: yum install pcre-devel 21 | 22 | 23 | ## Installation 24 | 25 | You can install this module using `v install spytheman.regex`, and 26 | then use it with `import spytheman.regex` . 27 | 28 | When there are updates, you can update with `v update spytheman.regex` . 29 | 30 | You can also just run `v install spytheman.regex` again. 31 | 32 | 33 | ## Example 34 | (this can also be found in [examples/match_after.v](https://github.com/spytheman/v-regex/blob/master/examples/match_after.v)) 35 | ```v 36 | import spytheman.regex 37 | 38 | fn main() { 39 | r := regex.new_regex('Match everything after this: (.+)', 0) or { 40 | println('An error occured!') 41 | return 42 | } 43 | 44 | m := r.match_str('Match everything after this: "I <3 VLang!"', 0, 0) or { 45 | println('No match!') 46 | return 47 | } 48 | 49 | // m.get(0) -> Match everything after this: "I <3 VLang!" 50 | // m.get(1) -> "I <3 VLang!"' 51 | // m.get(2) -> Error! 52 | whole_match := m.get(0) or { 53 | println('We matched nothing...') 54 | return 55 | } 56 | 57 | matched_str := m.get(1) or { 58 | println('We matched nothing...') 59 | return 60 | } 61 | 62 | println(whole_match) // Match everything after this: "I <3 VLang!" 63 | println(matched_str) // "I <3 VLang!" 64 | 65 | r.free() 66 | } 67 | ``` 68 | 69 | ```bash 70 | $ v -o example examples/match_after.v 71 | $ ./example 72 | Match everything after this: "I <3 VLang!" 73 | "I <3 VLang!" 74 | ``` 75 | 76 | ## Usage 77 | 78 | Some examples are available in the [examples](examples/) directory. 79 | 80 | ## Built With 81 | 82 | * [PCRE](https://www.pcre.org/) - Perl Compatible Regular Expressions 83 | * [Vlang](https://github.com/vlang/v) - Simple, fast, safe, compiled language 84 | 85 | ## License 86 | 87 | [MIT](LICENSE) 88 | 89 | ## Contributors 90 | 91 | * [Shellbear](https://github.com/shellbear) - creator 92 | * [Spytheman](https://github.com/spytheman) - maintainer 93 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | To run an example just run: 4 | 5 | ```go 6 | v run example_of_your_choice.v 7 | ``` 8 | 9 | Or compile: 10 | 11 | ```go 12 | v example_of_your_choice.v 13 | ./example_of_your_choice 14 | ``` 15 | -------------------------------------------------------------------------------- /examples/hello.v: -------------------------------------------------------------------------------- 1 | /* 2 | * V bindings for lpcre library 3 | * http://www.pcre.org/ 4 | * examples/hello.v 5 | * https://github.com/shellbear/v-regex 6 | */ 7 | import spytheman.regex 8 | 9 | fn main() { 10 | r := regex.new_regex('Hello', 0) or { 11 | println('An error occured!') 12 | return 13 | } 14 | 15 | m := r.match_str('Hello world!', 0, 0) or { 16 | println('No match!') 17 | return 18 | } 19 | 20 | // m.get(0) -> Hello 21 | // m.get(1) -> Error! 22 | matched_str := m.get(0) or { 23 | println('We matched nothing...') 24 | return 25 | } 26 | 27 | println(matched_str) // Hello 28 | 29 | r.free() 30 | } 31 | -------------------------------------------------------------------------------- /examples/match_after.v: -------------------------------------------------------------------------------- 1 | /* 2 | * V bindings for lpcre library 3 | * http://www.pcre.org/ 4 | * examples/match_after.v 5 | * https://github.com/shellbear/v-regex 6 | */ 7 | import spytheman.regex 8 | 9 | fn main() { 10 | r := regex.new_regex('Match everything after this: (.+)', 0) or { 11 | println('An error occured!') 12 | return 13 | } 14 | 15 | m := r.match_str('Match everything after this: "I <3 VLang!"', 0, 0) or { 16 | println('No match!') 17 | return 18 | } 19 | 20 | // m.get(0) -> Match everything after this: "I <3 VLang!" 21 | // m.get(1) -> "I <3 VLang!" 22 | // m.get(2) -> Error! 23 | whole_match := m.get(0) or { 24 | println('We matched nothing...') 25 | return 26 | } 27 | 28 | matched_str := m.get(1) or { 29 | println('We matched nothing...') 30 | return 31 | } 32 | 33 | println(whole_match) // Match everything after this: "I <3 VLang!" 34 | println(matched_str) // "I <3 VLang!"' 35 | 36 | r.free() 37 | } 38 | -------------------------------------------------------------------------------- /examples/match_all.v: -------------------------------------------------------------------------------- 1 | /* 2 | * V bindings for lpcre library 3 | * http://www.pcre.org/ 4 | * examples/match_all.v 5 | * https://github.com/shellbear/v-regex 6 | */ 7 | import spytheman.regex 8 | 9 | fn main() { 10 | r := regex.new_regex('(.*)(hello)+', 0) or { 11 | println('An error occured!') 12 | return 13 | } 14 | 15 | tests := [ 16 | 'This should match... hello', 17 | 'This could match... hello!', 18 | 'More than one hello.. hello', 19 | 'No chance of a match...', 20 | ] 21 | 22 | for test in tests { 23 | println('\nTesting string: $test') 24 | m := r.match_str(test, 0, 0) or { 25 | println('No match!') 26 | continue 27 | } 28 | 29 | println(m.get_all()) 30 | } 31 | 32 | r.free() 33 | } 34 | -------------------------------------------------------------------------------- /match_data.v: -------------------------------------------------------------------------------- 1 | module regex 2 | 3 | struct MatchData { 4 | pub: 5 | re &C.pcre // A pointer to pcre structure 6 | ovector []int 7 | str string 8 | pos int 9 | group_size int 10 | } 11 | 12 | // valid_group checks if group index is valid 13 | pub fn (m MatchData) valid_group(index int) bool { 14 | return -m.group_size <= index && index < m.group_size 15 | } 16 | 17 | // get returns a matched group based on index 18 | // * (.+) hello (.+) -> 'This is a simple hello world' 19 | // * get(0) -> This is a simple hello world 20 | // * get(1) -> This is a simple 21 | // * get(2) -> world 22 | // * get(3) -> Error! 23 | pub fn (m MatchData) get(index_ int) ?string { 24 | mut index := index_ 25 | if !m.valid_group(index) { 26 | return error('Index out of bounds') 27 | } 28 | if index < 0 { 29 | index += m.group_size 30 | } 31 | start := m.ovector[index * 2] 32 | end := m.ovector[index * 2 + 1] 33 | if start < 0 || end > m.str.len { 34 | return error('Match group is invalid') 35 | } 36 | substr := m.str.substr(start, end) 37 | return substr 38 | } 39 | 40 | // get_all returns all matched groups 41 | pub fn (m MatchData) get_all() []string { 42 | mut res := []string{} 43 | for i := 1; true; i++ { 44 | substr := m.get(i) or { break } 45 | res << substr 46 | } 47 | return res 48 | } 49 | -------------------------------------------------------------------------------- /pcre.v: -------------------------------------------------------------------------------- 1 | module regex 2 | 3 | // TODO: windows support 4 | 5 | #flag linux -lpcre 6 | #flag darwin -lpcre 7 | 8 | #flag darwin -I /opt/homebrew/include/ 9 | 10 | #include 11 | 12 | [typedef] 13 | struct C.pcre {} 14 | 15 | [typedef] 16 | struct C.pcre_extra {} 17 | 18 | fn C.pcre_compile(pattern byteptr, options int, const_perr &&char, perroroffset &int, ptable voidptr) &C.pcre 19 | fn C.pcre_compile2(pattern byteptr, options int, perrorcode &int, const_perr &&char, perroroffset &int, ptable byteptr) &C.pcre 20 | fn C.pcre_copy_named_substring(&C.pcre, byteptr, &int, int, byteptr, byteptr, int) int 21 | fn C.pcre_copy_substring(byteptr, &int, int, int, byteptr, int) int 22 | fn C.pcre_dfa_exec(const_pcode &C.pcre, const_pextra &C.pcre_extra, const_subject &char, length int, startoffset int, options int, povector &int, ovecsize int, workspace &int, wscount int) int 23 | fn C.pcre_study(const_pcode &C.pcre, options int, const_x &&char) &C.pcre_extra 24 | fn C.pcre_exec(const_pcode &C.pcre, const_pextra &C.pcre_extra, subject byteptr, length int, startoffset int, options int, povector &int, ovecsize int) int 25 | fn C.pcre_fullinfo(const_pcode &C.pcre, const_pextra &C.pcre_extra, what int, where voidptr) int 26 | fn C.pcre_get_stringnumber(const_pcode &C.pcre, const_pname &char) int 27 | fn C.pcre_get_stringtable_entries(const_pcode &C.pcre, const_pname &char, pfirst &&char, plast &&char) int 28 | fn C.pcre_get_substring(byteptr, &int, int, int, &byteptr) int 29 | fn C.pcre_get_substring_list(byteptr, &int, int, &&byteptr) int 30 | fn C.pcre_get_named_substring(&C.pctr, byteptr, &int, int, byteptr, &byteptr) int 31 | fn C.pcre_maketables() byteptr 32 | fn C.pcre_refcount(&C.pcre, int) int 33 | fn C.pcre_version() byteptr 34 | fn C.pcre_free_substring_list(&byteptr) 35 | fn C.pcre_free_substring(byteptr) 36 | fn C.pcre_free_study(&C.pcre_extra) 37 | fn C.pcre_free(voidptr) 38 | -------------------------------------------------------------------------------- /regex.v: -------------------------------------------------------------------------------- 1 | module regex 2 | 3 | struct Regex { 4 | pub: 5 | re &C.pcre // A pointer to pcre structure 6 | extra &C.pcre_extra // A pointer to pcre_extra structure 7 | captures int // The number of capture groups 8 | } 9 | 10 | pub fn (r &Regex) free() { 11 | if !isnil(r.re) { 12 | C.pcre_free(r.re) 13 | } 14 | if !isnil(r.extra) { 15 | C.pcre_free_study(r.extra) 16 | } 17 | } 18 | 19 | // match_str returns a MatchData structure containing matched strings and: 20 | // str: the string to test 21 | // pos: the position of the beginning of the string (default: 0) 22 | // options: the options as mentioned in the PCRE documentation 23 | pub fn (r Regex) match_str(str string, pos int, options int) ?MatchData { 24 | if pos < 0 || pos >= str.len { 25 | return error('Invalid position') 26 | } 27 | ovector_size := (r.captures + 1) * 3 28 | ovector := []int{len: ovector_size} 29 | ret := C.pcre_exec(r.re, r.extra, &char(str.str), str.len, pos, options, ovector.data, 30 | ovector_size) 31 | if ret <= 0 { 32 | return error('No match!') 33 | } 34 | return MatchData{ 35 | re: r.re 36 | str: str 37 | ovector: ovector 38 | pos: pos 39 | group_size: r.captures + 1 40 | } 41 | } 42 | 43 | // new_regex create a new regex 44 | // * source: the string representing the regex 45 | // * options: the options as mentioned in the PCRE documentation 46 | pub fn new_regex(source string, options int) ?Regex { 47 | mut perrbuf := &char(0) 48 | mut pstudyerr := &char(0) 49 | erroffset := 0 50 | captures := 0 51 | re := C.pcre_compile(&char(source.str), options, voidptr(&perrbuf), &erroffset, 0) 52 | if isnil(re) { 53 | err := unsafe { cstring_to_vstring(perrbuf) } 54 | return error('Failed to compile regex: $err') 55 | } 56 | extra := C.pcre_study(re, 0, voidptr(&pstudyerr)) 57 | if extra == 0 { 58 | if pstudyerr == 0 { 59 | return error('no additional information') 60 | } 61 | err := unsafe { cstring_to_vstring(pstudyerr) } 62 | return error('Failed to study regex: $err') 63 | } 64 | C.pcre_fullinfo(re, 0, C.PCRE_INFO_CAPTURECOUNT, &captures) 65 | return Regex{re, extra, captures} 66 | } 67 | --------------------------------------------------------------------------------