summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTyge Løvset <[email protected]>2022-07-28 00:24:13 +0200
committerTyge Løvset <[email protected]>2022-07-28 12:39:34 +0200
commit281a4148d9143260c614c92e81d3484103479761 (patch)
tree6661f8f41c416c0120d677fbaffb07f9b3eabbc9
parent18392a1f49bb742fce3e28bb8196b2a64ea07219 (diff)
downloadSTC-modified-281a4148d9143260c614c92e81d3484103479761.tar.gz
STC-modified-281a4148d9143260c614c92e81d3484103479761.zip
VERSION 3.8 RC. Added cregex with "final" API + docs. README.md updated with links to cregex, coption.
crandom.h: fixed "stc64_with_seq doesn't use seq argument #31" thanks to funny-falcon. Removed deprecated funcs. Added tags for v3.6 and v3.7.
-rw-r--r--README.md24
-rw-r--r--docs/cregex_api.md183
-rw-r--r--examples/regex_replace.c19
-rw-r--r--include/stc/crandom.h11
-rw-r--r--include/stc/cregex.h20
-rw-r--r--src/cregex.c13
6 files changed, 231 insertions, 39 deletions
diff --git a/README.md b/README.md
index 9544a071..36417f8d 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,10 @@ STC - Smart Template Containers for C
News: Version 3.8 released (July 2022)
---------------------------------------
-- [See changes version 3](#version-3). Note some code-breaking API changes!
+- "Officially" added **cregex** - powerful regular expressions.
+- Added back **coption** - command line argument parsing.
+- Some changes in **cstr** and **csview** API.
+- [See detailed changes for version 3](#version-3).
Introduction
------------
@@ -41,10 +44,9 @@ The library is mature and well tested, so you may use it in projects. However, m
still happen. The main development of this project is finished, but I will handle PRs with bugs and improvements
in the future, and do minor modifications.
-Contents
---------
-- [***ccommon*** - RAII and iterator macros](docs/ccommon_api.md)
-- [***carc*** - **std::shared_ptr** alike support](docs/carc_api.md)
+Containers
+----------
+- [***carc*** - **std::shared_ptr** alike type](docs/carc_api.md)
- [***carr2***, ***carr3*** - 2D and 3D **array** types](docs/carray_api.md)
- [***cbits*** - **std::bitset** alike type](docs/cbits_api.md)
- [***cbox*** - **std::unique_ptr** alike type](docs/cbox_api.md)
@@ -53,7 +55,6 @@ Contents
- [***cmap*** - **std::unordered_map** alike type](docs/cmap_api.md)
- [***cpque*** - **std::priority_queue** alike type](docs/cpque_api.md)
- [***cqueue*** - **std::queue** alike type](docs/cqueue_api.md)
-- [***crandom*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md)
- [***cset*** - **std::unordered_set** alike type](docs/cset_api.md)
- [***csmap*** - **std::map** sorted map alike type](docs/csmap_api.md)
- [***csset*** - **std::set** sorted set alike type](docs/csset_api.md)
@@ -62,6 +63,13 @@ Contents
- [***csview*** - **std::string_view** alike type](docs/csview_api.md)
- [***cvec*** - **std::vector** alike type](docs/cvec_api.md)
+Others
+------
+- [***ccommon*** - RAII and iterator macros](docs/ccommon_api.md)
+- [***coption*** - getopt() alike command line args parser](docs/coption_api.md)
+- [***crandom*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md)
+- [***cregex*** - Regular expression parser (extended from Rob Pike's regexp9)](docs/cregex_api.md)
+
Highlights
----------
- **User friendly** - Just include the headers and you are good. The API and functionality is very close to c++ STL, and is fully listed in the docs.
@@ -467,7 +475,9 @@ Memory efficiency
- Added cstr_u8_slice() and csview_u8_slice().
- Removed `csview_from_s()`: Use `cstr_sv(s)` instead.
- Removed `csview_from_n()`: Use `c_sv(str, n)` instead.
- - Updated cstr and csview docs.
+ - Added back file coption.h
+ - Simplified **cbits** usage: all inlined.
+ - Updated docs.
## Changes version 3.7
- NB! Changed self argument from value to const pointer on containers (does not apply to **cstr**):
diff --git a/docs/cregex_api.md b/docs/cregex_api.md
new file mode 100644
index 00000000..d04bb32e
--- /dev/null
+++ b/docs/cregex_api.md
@@ -0,0 +1,183 @@
+# STC [cregex](../include/stc/cregex.h): Regular Expressions
+
+
+## Description
+
+**cregex** is a small and fast unicode UTF8 regular expression parser. It is based on Rob Pike's non-backtracking NFA-based regular expression implementation for the Plan 9 project. See Russ Cox's articles [Implementing Regular Expressions](https://swtch.com/~rsc/regexp/) on why NFA-based regular expression engines often are superiour to the common backtracking implementations (hint: NFAs have no "bad/slow" RE patterns).
+
+The API is simple and includes powerful string pattern matches and replace functions. See example below and in the example folder.
+
+## Methods
+
+```c
+enum {
+ // compile-flags
+ cre_c_dotall = 1<<0, // dot matches newline too
+ cre_c_caseless = 1<<1, // ignore case
+ // match-flags
+ cre_m_fullmatch = 1<<2, // like start-, end-of-line anchors were in pattern: "^ ... $"
+ cre_m_next = 1<<3, // use end of previous match[0] as start of input
+ cre_m_startend = 1<<4, // use match[0] as start+end of input
+ // replace-flags
+ cre_r_strip = 1<<5, // only keep the matched strings, strip the rest
+};
+
+cregex cregex_init(void);
+
+cregex cregex_from(const char* pattern, int cflags);
+ // return 1 = success, negative = error.
+int cregex_compile(cregex *self, const char* pattern, int cflags);
+
+ // num. of capture groups in regex. 0 if RE is invalid. First group is the full match.
+int cregex_captures(const cregex* self);
+
+ // return 1=match, 0=nomatch, -1=error. match array size: at least num groups in RE (1+).
+int cregex_find(const char* input, const cregex* re, csview match[], int mflags);
+ // takes string pattern instead of re. (for one-time matches)
+int cregex_find_p(const char* input, const char* pattern, csview match[], int cmflags);
+
+bool cregex_is_match(const char* input, const cregex* re, int mflags);
+
+cstr cregex_replace(const char* input, const cregex* re, const char* replace);
+cstr cregex_replace_re(const char* input, const cregex* re, const char* replace, // extended args:
+ bool (*mfun)(int grp, csview match, cstr* mstr), unsigned count, int rflags);
+ // takes string pattern instead of re
+cstr cregex_replace_p(const char* input, const char* pattern, const char* replace);
+cstr cregex_replace_pe(const char* input, const char* pattern, const char* replace,
+ bool (*mfun)(int grp, csview match, cstr* mstr), unsigned count, int crflags);
+
+void cregex_drop(cregex* self); // destroy
+```
+
+### Error codes
+
+- cre_success = 1
+- cre_nomatch = 0
+- cre_matcherror = -1
+- cre_outofmemory = -2
+- cre_unmatchedleftparenthesis = -3
+- cre_unmatchedrightparenthesis = -4
+- cre_toomanysubexpressions = -5
+- cre_toomanycharacterclasses = -6
+- cre_malformedcharacterclass = -7
+- cre_missingoperand = -8
+- cre_unknownoperator = -9
+- cre_operandstackoverflow = -10
+- cre_operatorstackoverflow = -11
+- cre_operatorstackunderflow = -12
+
+### Limits
+- cre_MAXCLASSES
+- cre_MAXCAPTURES
+
+## Usage
+
+### Compiling a regular expression
+```c
+cregex re1 = cregex_init();
+int result = cregex_compile(&re1, "[0-9]+", 0);
+if (result < 0) return result;
+
+const char* url = "(https?://|ftp://|www\\.)([0-9A-Za-z@:%_+~#=-]+\\.)+([a-z][a-z][a-z]?)(/[/0-9A-Za-z\\.@:%_+~#=\\?&-]*)?";
+cregex re2 = cregex_from(url, 0);
+if (re2.error) return re2.error;
+...
+cregex_drop(&re2);
+cregex_drop(&re1);
+```
+If an error occurs ```cregex_compile``` returns a negative value, see error codes.
+
+### Getting the first match
+```c
+#define i_implement
+#include <stc/cstr.h>
+#include <stc/cregex.h>
+
+int main() {
+ const char* input = "start date is 2023-03-01, and end date is 2025-12-31.";
+ const char* pattern = "\\b(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)\\b";
+
+ cregex re = cregex_from(pattern, 0);
+
+ // Lets find the first date in the string:
+ csview match[4]; // full-match, year, month, date.
+ if (cregex_find(input, &re, match, 0) == cre_success)
+ printf("Found date: %.*s\n", c_ARGsv(match[0]));
+ else
+ printf("Could not find any date\n");
+
+ // Lets change all dates into US date format MM/DD/YYYY:
+ cstr us_input = cregex_replace(input, &re, "$2/$3/$1");
+ printf("US input: %s\n", cstr_str(&us_input));
+
+ // Free allocated data
+ cstr_drop(&us_input);
+ cregex_drop(&re);
+}
+```
+To compile, use: `gcc first_match.c src/cregex.c src/utf8code.c`.
+For a single match you may use the all-in-one function:
+```c
+if (cregex_find_p(input, pattern, match, 0))
+ printf("Found date: %.*s\n", c_ARGsv(match[0]));
+```
+
+## Using cregex in a project
+**cregex** uses the following files:
+- `stc/cregex.h`, `stc/utf8.h`, `stc/csview.h`, `stc/cstr.h`, `stc/ccommon.h`, `stc/forward.h`
+- `src/cregex.c`, `src/utf8code.c`.
+
+## Regex Cheatsheet
+
+| Metacharacter | Description | STC addition |
+|:--:|:--:|:--:|
+| c | Most characters (like c) match themselve literally | |
+| \c | Some characters are used as metacharacters. To use them literally escape them | |
+| . | Match any character, except newline unless in (?s) mode | |
+| ? | Match the preceding token zero or one time | |
+| * | Match the preceding token as often as possible | |
+| + | Match the preceding token at least once and as often as possible | |
+| \| | Match either the expression before the \| or the expression after it | |
+| (c) | Match the expression inside the parentheses. This adds a capture group | |
+| [c] | Match all characters inside the brackets. Ranges like a-z may also be used | |
+| [^c] | Do not match the characters inside the bracket. | |
+| \x{***hex***} | Match character given as hex number | * |
+| ^ | Start of line anchor | |
+| $ | End of line anchor | |
+| \A | Start of input anchor | * |
+| \Z | End of input anchor | * |
+| \z | End of input including optional newline | * |
+| \b | UTF8 word boundary anchor | * |
+| \B | Not UTF8 word boundary | * |
+| \Q | Start literal input mode | * |
+| \E | End literal input mode | * |
+| (?i) (?-i) | Ignore case on/off (override global) | * |
+| (?s) (?-s) | Dot matches newline on/off (override global) | * |
+| \n \t \r | Match UTF8 newline, tab, carriage return | |
+| \d \s \w | Match UTF8 digit, whitespace, alphanumeric character | |
+| \D \S \W | Do not match the groups described above | |
+| \p{Space} or \p{Sz} | Match UTF8 whitespace | * |
+| \p{Digit} or \p{Nd} | Match UTF8 numeric | * |
+| \p{XDigit} | Match UTF8 hex number | * |
+| \p{Lower} or \p{Ll} | Match UTF8 lower case | * |
+| \p{Upper} or \p{Lu} | Match UTF8 upper case | * |
+| \p{Alpha} or \p{LC} | Match UTF8 cased letter | * |
+| \p{Alnum} | Match UTF8 alpha numeric | * |
+| \P{*class*} | Do not match the classes described above | * |
+| [[:alnum:]] [[:alpha:]] [[:ascii:]] | Match ASCII character class | * |
+| [[:blank:]] [[:cntrl:]] [[:digit:]] | Match ASCII character class | * |
+| [[:graph:]] [[:lower:]] [[:print:]] | Match ASCII character class | * |
+| [[:punct:]] [[:space:]] [[:upper:]] | Match ASCII character class | * |
+| [[:xdigit:]] [[:word:]] | Match ASCII character class | * |
+| [[:^\<class\>:]] | Do not match ASCII character class | * |
+| $***n*** | *n*-th substitution backreference to capture group. *n* in 0-9. $0 is the entire match.
+
+## Limitations
+
+The main goal of **cregex** is to be small and fast with limited but useful unicode support. In order to reach these goals, **cregex** currently does not support the following features (non-exhaustive list):
+- In order to limit table sizes, most general UTF8 character classes are missing, like \p{L}, \p{S}, and all specific scripts like \p{Greek} etc. Some/all of these may be added in the future as an alternative source file with unicode tables to link with.
+- {n, m} syntax for repeating previous token min-max times.
+- Non-capturing groups
+- Lookaround and backreferences
+
+If you need a more feature complete, but bigger library, use [RE2 with C-wrapper](https://github.com/google/re2) which uses the same type of regex engine as **cregex**, or use [PCRE2](https://www.pcre.org/). \ No newline at end of file
diff --git a/examples/regex_replace.c b/examples/regex_replace.c
index 3dcb965d..35b3c696 100644
--- a/examples/regex_replace.c
+++ b/examples/regex_replace.c
@@ -33,13 +33,18 @@ int main()
cstr_take(&str, cregex_replace_pe(input, pattern, "[$0]", NULL, 1, 0));
printf("brack: %s\n", cstr_str(&str));
- /* European date format. Show how to compile RE separately */
- cregex re = cregex_from(pattern, 0);
- if (cregex_captures(&re) == 0)
- continue;
- cstr_take(&str, cregex_replace(input, &re, "$3.$2.$1"));
- cregex_drop(&re);
- printf("euros: %s\n", cstr_str(&str));
+ /* Shows how to compile RE separately */
+ c_autovar (cregex re = cregex_from(pattern, 0), cregex_drop(&re)) {
+ if (cregex_captures(&re) == 0)
+ continue;
+ /* European date format. */
+ cstr_take(&str, cregex_replace(input, &re, "$3.$2.$1"));
+ printf("euros: %s\n", cstr_str(&str));
+
+ /* Strip out everything but the matches */
+ cstr_take(&str, cregex_replace_re(input, &re, "$3.$2.$1;", NULL, 0, cre_r_strip));
+ printf("strip: %s\n", cstr_str(&str));
+ }
/* Wrap all words in ${} */
cstr_take(&str, cregex_replace_p("[52] apples and [31] mangoes", "[a-z]+", "$${$0}"));
diff --git a/include/stc/crandom.h b/include/stc/crandom.h
index 06964a5d..0e34e850 100644
--- a/include/stc/crandom.h
+++ b/include/stc/crandom.h
@@ -108,15 +108,6 @@ STC_INLINE stc64_normalf_t stc64_normalf_new(double mean, double stddev) {
return c_make(stc64_normalf_t){mean, stddev, 0.0, 0};
}
-/* Following functions are deprecated (will be removed in the future): */
-STC_INLINE void stc64_srandom(uint64_t seed) { csrandom(seed); }
-STC_INLINE uint64_t stc64_random() { return crandom(); }
-STC_INLINE stc64_t stc64_init(uint64_t seed) { return stc64_new(seed); }
-STC_INLINE stc64_uniformf_t stc64_uniformf_init(double low, double high)
- { return stc64_uniformf_new(low, high); }
-STC_INLINE stc64_normalf_t stc64_normalf_init(double mean, double stddev)
- { return stc64_normalf_new(mean, stddev); }
-
/* -------------------------- IMPLEMENTATION ------------------------- */
#if defined(i_implement) || defined(i_extern)
@@ -142,7 +133,7 @@ STC_DEF double crandomf(void) {
/* rng.state[4] must be odd */
STC_DEF stc64_t stc64_with_seq(uint64_t seed, uint64_t seq) {
stc64_t rng = {{seed+0x26aa069ea2fb1a4d, seed+0x70c72c95cd592d04,
- seed+0x504f333d3aa0b359, seed, seed<<1 | 1}};
+ seed+0x504f333d3aa0b359, seed, seq<<1 | 1}};
for (int i = 0; i < 6; ++i) stc64_rand(&rng);
return rng;
}
diff --git a/include/stc/cregex.h b/include/stc/cregex.h
index c920ae7b..4247197c 100644
--- a/include/stc/cregex.h
+++ b/include/stc/cregex.h
@@ -1,6 +1,5 @@
/*
This is a Unix port of the Plan 9 regular expression library, by Rob Pike.
-Please send comments about the packaging to Russ Cox <[email protected]>.
Copyright © 2021 Plan 9 Foundation
Copyright © 2022 Tyge Løvset, for additions made in 2022.
@@ -31,6 +30,7 @@ THE SOFTWARE.
* This is a extended version of regexp9, supporting UTF8 input, common
* shorthand character classes, ++.
*/
+#include <stdbool.h>
#include "forward.h" // csview
typedef enum {
@@ -52,12 +52,14 @@ typedef enum {
enum {
/* compile-flags */
- cre_c_dotall = 1<<0,
- cre_c_caseless = 1<<1,
+ cre_c_dotall = 1<<0, /* dot matches newline too */
+ cre_c_caseless = 1<<1, /* ignore case */
/* match-flags */
- cre_m_fullmatch = 1<<2,
- cre_m_next = 1<<3,
- cre_m_startend = 1<<4,
+ cre_m_fullmatch = 1<<2, /* like start-, end-of-line anchors were in pattern: "^ ... $" */
+ cre_m_next = 1<<3, /* use end of previous match[0] as start of input */
+ cre_m_startend = 1<<4, /* use match[0] as start+end of input */
+ /* replace-flags */
+ cre_r_strip = 1<<5, /* only keep the matched strings, strip rest */
/* limits */
cre_MAXCLASSES = 16,
cre_MAXCAPTURES = 32,
@@ -103,14 +105,14 @@ bool cregex_is_match(const char* input, const cregex* re, int mflags)
/* replace regular expression */
cstr cregex_replace_re(const char* input, const cregex* re, const char* replace,
- bool (*mfun)(int i, csview match, cstr* mstr), unsigned count);
+ bool (*mfun)(int i, csview match, cstr* mstr), unsigned count, int rflags);
static inline
cstr cregex_replace(const char* input, const cregex* re, const char* replace)
- { return cregex_replace_re(input, re, replace, NULL, 0); }
+ { return cregex_replace_re(input, re, replace, NULL, 0, 0); }
/* replace + compile RE pattern, and extra arguments */
cstr cregex_replace_pe(const char* input, const char* pattern, const char* replace,
- bool (*mfun)(int i, csview match, cstr* mstr), unsigned count, int cflags);
+ bool (*mfun)(int i, csview match, cstr* mstr), unsigned count, int crflags);
static inline
cstr cregex_replace_p(const char* input, const char* pattern, const char* replace)
{ return cregex_replace_pe(input, pattern, replace, NULL, 0, 0); }
diff --git a/src/cregex.c b/src/cregex.c
index 1d9609b3..c50e870f 100644
--- a/src/cregex.c
+++ b/src/cregex.c
@@ -1228,34 +1228,35 @@ int cregex_find_p(const char* input, const char* pattern,
cstr
cregex_replace_re(const char* input, const cregex* re, const char* replace,
- bool (*mfun)(int i, csview match, cstr* mstr), unsigned count) {
+ bool (*mfun)(int i, csview match, cstr* mstr), unsigned count, int rflags) {
cstr out = cstr_null;
cstr subst = cstr_null;
size_t from = 0;
csview match[cre_MAXCAPTURES];
unsigned nmatch = cregex_captures(re);
if (!count) count = ~0;
+ bool copy = !(rflags & cre_r_strip);
while (count-- && cregex_find(input + from, re, match, 0) == 1) {
build_subst_string(replace, nmatch, match, mfun, &subst);
const size_t pos = match[0].str - input;
- cstr_append_n(&out, input + from, pos - from);
+ if (copy) cstr_append_n(&out, input + from, pos - from);
cstr_append_s(&out, subst);
from = pos + match[0].size;
}
- cstr_append(&out, input + from);
+ if (copy) cstr_append(&out, input + from);
cstr_drop(&subst);
return out;
}
cstr
cregex_replace_pe(const char* input, const char* pattern, const char* replace,
- bool (*mfun)(int i, csview match, cstr* mstr), unsigned count, int cflags) {
+ bool (*mfun)(int i, csview match, cstr* mstr), unsigned count, int crflags) {
cregex re = cregex_init();
- int res = cregex_compile(&re, pattern, cflags);
+ int res = cregex_compile(&re, pattern, crflags);
if (res < 0)
return cstr_new("[[error: invalid regex pattern]]");
- cstr out = cregex_replace_re(input, &re, replace, mfun, count);
+ cstr out = cregex_replace_re(input, &re, replace, mfun, count, crflags);
cregex_drop(&re);
return out;
}