| cs-pcre2 - Chez Scheme bindings for PCRE2.
git clone https://benconnors.ca/git-repos/cs-pcre2 |
pcre2.scm (10146B) - raw
1 ; Chez Scheme bindings for (a subset of) PCRE2 2 3 (library (pcre2 (1)) 4 (export 5 pcre2-compile 6 pcre2-match 7 pcre2-get-match-count 8 pcre2-get-match-groupcount 9 pcre2-get-match-group 10 pcre2-get-match-groups) 11 (import (chezscheme)) 12 13 (define pcre2-library-load (load-shared-object "libpcre2-8.so")) 14 15 ;;; BEGIN Constants 16 17 (define SIZE_T-SIZE (foreign-sizeof 'size_t)) 18 (define PCRE2-ZERO-TERMINATED (- (expt 2 (* 8 SIZE_T-SIZE)) 1)) 19 (define PCRE2-UNSET PCRE2-ZERO-TERMINATED) 20 21 ;;; END Constants 22 23 ;;; BEGIN Memory Management 24 25 ; This guardian handles deallocating PCRE2-allocated foreign pointers when they're no 26 ; longer accessible from scheme 27 (define pcre2-guardian (make-guardian)) 28 29 ; Add a pointer to the guardian with the given cleanup function (`pcre2-code-free-base` or 30 ; `pcre2-match-data-free-base`) 31 (define (pcre2-guard-pointer ptr cleanup) 32 (cons ptr cleanup)) 33 34 35 ; Get the foreign pointer from a guarded pointer 36 (define (pcre2-get-pointer-ptr ptr) 37 (car ptr)) 38 39 ; Get the cleanup function from a guarded pointer 40 (define (pcre2-get-pointer-cleanup ptr) 41 (cdr ptr)) 42 43 ; Guard a code pointer 44 (define (pcre2-guard-code-pointer ptr) 45 (pcre2-guard-pointer ptr pcre2-code-free-base)) 46 47 ; Guard a match data pointer 48 (define (pcre2-guard-match-pointer ptr) 49 (pcre2-guard-pointer ptr pcre2-match-data-free-base)) 50 51 ; Run cleanup; called when new data is allocated 52 (define (pcre2-cleanup) 53 (let f () 54 (let ((x (pcre2-guardian))) 55 (when x 56 ((pcre2-get-pointer-cleanup x) (pcre2-get-pointer-ptr x)) 57 (f))))) 58 59 ;;; END Memory Management 60 61 ;;; BEGIN Compilation Functions 62 63 (define pcre2-compile-base 64 (foreign-procedure "pcre2_compile_8" 65 (string size_t unsigned-32 void* void* void*) 66 void*)) 67 68 (define pcre2-code-free-base 69 (foreign-procedure "pcre2_code_free_8" 70 (void*) 71 void)) 72 73 ; Compile a string pattern for use in match 74 (define (pcre2-compile pattern options) 75 (pcre2-cleanup) 76 (let* ((errorcode (foreign-alloc (foreign-sizeof 'int))) 77 (erroroffset (foreign-alloc (foreign-sizeof 'size_t))) 78 (code (pcre2-compile-base pattern PCRE2-ZERO-TERMINATED options errorcode erroroffset 0)) 79 (errorc (foreign-ref 'int errorcode 0))) 80 (if (< errorc 0) 81 (error 'pcre2-compile (pcre2-get-error-message errorc) (foreign-ref 'size_t erroroffset 0)) 82 (pcre2-guard-code-pointer code)))) 83 84 ;;; END Compilation Functions 85 86 ;;; BEGIN Utility Functions 87 88 (define pcre2-get-error-message-base 89 (foreign-procedure "pcre2_get_error_message_8" 90 (int u8* size_t) 91 int)) 92 93 ; Get the PCRE2 error message corresponding to the given code 94 (define (pcre2-get-error-message errorcode) 95 (let* ((buff (make-bytevector 255)) 96 (len (pcre2-get-error-message-base errorcode buff 255))) 97 (substring (utf8->string buff) 0 len))) 98 99 ;;; END Utility Functions 100 101 ;;; BEGIN Matching Functions 102 103 ; Note: functions here take either `match-data-ptr` or `match-data`; if they take 104 ; `match-data-ptr`, they expect a guarded pointer as returned from `pcre2-guard-pointer`. 105 ; Otherwise, they expect a match data object as returned from `pcre2-match` 106 107 (define pcre2-match-base 108 (foreign-procedure "pcre2_match_8" 109 (void* string size_t size_t unsigned-32 void* void*) 110 int)) 111 112 (define pcre2-match-data-free-base 113 (foreign-procedure "pcre2_match_data_free_8" 114 (void*) 115 void)) 116 117 (define pcre2-match-data-create-from-pattern-base 118 (foreign-procedure "pcre2_match_data_create_from_pattern_8" 119 (void* void*) 120 void*)) 121 122 ; Create a match data block with enough storage to hold the groups in `pattern` 123 (define (pcre2-match-data-create-from-pattern pattern) 124 (pcre2-guard-match-pointer (pcre2-match-data-create-from-pattern-base (pcre2-get-pointer-ptr pattern) 0))) 125 126 (define pcre2-get-ovector-count-base 127 (foreign-procedure "pcre2_get_ovector_count_8" 128 (void*) 129 unsigned-32)) 130 131 ; Get the number of groups in a given match data block 132 (define (pcre2-get-ovector-count match-data-ptr) 133 (pcre2-get-ovector-count-base (pcre2-get-pointer-ptr match-data-ptr))) 134 135 (define pcre2-get-ovector-pointer-base 136 (foreign-procedure "pcre2_get_ovector_pointer_8" 137 (void*) 138 void*)) 139 140 ; Get the pointer to the first group in the match data block 141 (define (pcre2-get-ovector-pointer match-data-ptr) 142 (pcre2-get-ovector-pointer-base (pcre2-get-pointer-ptr match-data-ptr))) 143 144 ; Create a match data object for `match-data-ptr` with the relevant info 145 (define (pcre2-make-match-data-object count match-data-ptr) 146 (list match-data-ptr count (pcre2-get-ovector-count match-data-ptr) (pcre2-get-ovector-pointer match-data-ptr))) 147 148 ; Accessor functions for match data objects returned from `pcre2-match` 149 (define pcre2-get-match-ptr car) 150 (define pcre2-get-match-ovector-ptr cadddr) 151 ; These three are the only public ones 152 (define pcre2-get-match-count cadr) 153 (define pcre2-get-match-groupcount caddr) 154 155 ; Match `str` against `pattern` starting at `offset`; returns #f if no match 156 (define (pcre2-match pattern str offset options) 157 (pcre2-cleanup) 158 (let* ((match-data (pcre2-match-data-create-from-pattern pattern)) 159 (errorcode (pcre2-match-base (pcre2-get-pointer-ptr pattern) str PCRE2-ZERO-TERMINATED offset options (pcre2-get-pointer-ptr match-data) 0))) 160 (display errorcode) 161 (newline) 162 (cond ((< errorcode -1) (error 'pcre2-match (pcre2-get-error-message errorcode) errorcode)) 163 ((< errorcode 2) #f) 164 (else (pcre2-make-match-data-object (- errorcode 1) match-data))))) 165 166 (define pcre2-substring-length-bynumber-base 167 (foreign-procedure "pcre2_substring_length_bynumber_8" 168 (void* unsigned-32 void*) 169 int)) 170 171 ; Get the length of a substring 172 (define (pcre2-substring-length-bynumber match-data-ptr num) 173 (let* ((lenptr (foreign-alloc SIZE_T-SIZE)) 174 (errorcode (pcre2-substring-length-bynumber-base (pcre2-get-pointer-ptr match-data-ptr) num lenptr)) 175 (len (foreign-ref 'size_t lenptr 0))) 176 (if (< errorcode 0) 177 (error 'pcre2-substring-length-bynumber (pcre2-get-error-message errorcode) match-data-ptr) 178 len))) 179 180 (define (pcre2-substring-length-bynumber-b match-data num) 181 (let* ((ovector (pcre2-get-match-ovector-ptr match-data)) 182 (start (foreign-ref 'size_t ovector (* 2 num SIZE_T-SIZE))) 183 (end (foreign-ref 'size_t ovector (* (+ 1 (* 2 num)) SIZE_T-SIZE)))) 184 (if (= start end PCRE2-UNSET) #f 185 (- end start)))) 186 187 (define pcre2-substring-copy-bynumber-base 188 (foreign-procedure "pcre2_substring_copy_bynumber_8" 189 (void* unsigned-32 u8* void*) 190 int)) 191 192 ; Get a match substring; some gymnastics are needed because PCRE2 operates in code units 193 ; (bytes) while Chez Scheme operates in UTF-8 characters 194 (define (pcre2-substring-copy-bynumber match-data num) 195 (let* ((strlen (pcre2-substring-length-bynumber-b match-data num))) 196 (if (not strlen) 197 #f ; This group is unset 198 (begin 199 (let* 200 ((match-data-ptr (pcre2-get-match-ptr match-data)) 201 (bufflen (+ strlen 1)) 202 (buff (make-bytevector (+ bufflen 1))) 203 (bufflenptr (foreign-alloc SIZE_T-SIZE))) 204 (foreign-set! 'size_t bufflenptr 0 bufflen) 205 (let* ((errorcode (pcre2-substring-copy-bynumber-base 206 (pcre2-get-pointer-ptr match-data-ptr) 207 num 208 buff 209 bufflenptr))) 210 (when (< errorcode 0) 211 (error 'pcre2-substring-copy-bynumber (pcre2-get-error-message errorcode) match-data-ptr)) 212 (bytevector-truncate! buff (- bufflen 1)) 213 (utf8->string buff))))))) 214 215 ; Get a match group for `match-data` 216 (define (pcre2-get-match-group match-data n) 217 (when (or (>= n (pcre2-get-match-groupcount match-data)) (< n 0)) 218 (error 'pcre2-get-match-group "Invalid group number" n)) 219 (pcre2-substring-copy-bynumber match-data n)) 220 221 ; Get all the substring groups for `match-data` 222 (define (pcre2-get-match-groups match-data) 223 (define count (pcre2-get-match-groupcount match-data)) 224 (define (inner n running) 225 (if (= n -1) running 226 (inner (- n 1) (cons (pcre2-get-match-group match-data n) running)))) 227 (inner (- count 1) '())) 228 229 ;;; END Matching Functions 230 231 )