also looking at this
Port to latest zig-master
#2
53 files
+1149
-1017
| @@ -1,215 +1,214 @@ | ||
| 1 | const std = ; | |
| 2 | const Regex = .Regex; | |
| 3 | const Match = .Match; | |
| 4 | ||
| 5 | /// C FFI for the Zig regex library | |
| 6 | /// | |
| 7 | /// This module provides a C-compatible API for using the regex library | |
| 8 | /// from other languages (C, C++, Python via ctypes, etc.) | |
| 9 | /// | |
| 10 | /// Example usage from C: | |
| 11 | /// ```c | |
| 12 | /// #include "zig_regex.h" | |
| 13 | /// | |
| 14 | /// ZigRegex* regex = zig_regex_compile("\\d+", NULL); | |
| 15 | /// if (regex) { | |
| 16 | /// bool matches = zig_regex_is_match(regex, "test 123"); | |
| 17 | /// printf("Matches: %d\n", matches); | |
| 18 | /// zig_regex_free(regex); | |
| 19 | /// } | |
| 20 | /// ``` | |
| 21 | ||
| 22 | /// Opaque handle to a compiled regex | |
| 23 | pub const ZigRegex = opaque {}; | |
| 24 | ||
| 25 | /// Opaque handle to a match result | |
| 26 | pub const ZigMatch = opaque {}; | |
| 27 | ||
| 28 | /// Error codes | |
| 29 | pub const ZigRegexError = enum(c_int) { | |
| 30 | ok = 0, | |
| 31 | compile_error = -1, | |
| 32 | invalid_pattern = -2, | |
| 33 | out_of_memory = -3, | |
| 34 | no_match = -4, | |
| 35 | null_pointer = -5, | |
| 36 | }; | |
| 37 | ||
| 38 | // Global allocator for C API (uses C allocator) | |
| 39 | var c_allocator = std.heap.c_allocator; | |
| 40 | ||
| 41 | /// Compile a regex pattern | |
| 42 | /// | |
| 43 | /// Returns NULL on error. Call zig_regex_get_last_error() to get error details. | |
| 44 | /// The returned regex must be freed with zig_regex_free(). | |
| 45 | export fn zig_regex_compile(pattern: [*:0]const u8) ?*ZigRegex { | |
| 46 | const pattern_slice = std.mem.span(pattern); | |
| 47 | ||
| 48 | const regex_ptr = c_allocator.create(Regex) catch return null; | |
| 49 | regex_ptr.* = Regex.compile(c_allocator, pattern_slice) catch { | |
| 50 | c_allocator.destroy(regex_ptr); | |
| 51 | return null; | |
| 52 | }; | |
| 53 | ||
| 54 | return @ptrCast(regex_ptr); | |
| 55 | } | |
| 56 | ||
| 57 | /// Free a compiled regex | |
| 58 | export fn zig_regex_free(regex: ?*ZigRegex) void { | |
| 59 | if (regex) |r| { | |
| 60 | const regex_ptr: *Regex = @ptrCast(@alignCast(r)); | |
| 61 | regex_ptr.deinit(); | |
| 62 | c_allocator.destroy(regex_ptr); | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | /// Check if a pattern matches a string | |
| 67 | /// | |
| 68 | /// Returns 1 if matches, 0 if no match, -1 on error | |
| 69 | export fn zig_regex_is_match(regex: ?*ZigRegex, input: [*:0]const u8) c_int { | |
| 70 | if (regex == null) return @intFromEnum(ZigRegexError.null_pointer); | |
| 71 | ||
| 72 | const regex_ptr: *const Regex = @ptrCast(@alignCast(regex.?)); | |
| 73 | const input_slice = std.mem.span(input); | |
| 74 | ||
| 75 | const result = regex_ptr.isMatch(input_slice) catch return -1; | |
| 76 | return if (result) 1 else 0; | |
| 77 | } | |
| 78 | ||
| 79 | /// Find the first match in a string | |
| 80 | /// | |
| 81 | /// Returns NULL if no match or on error. | |
| 82 | /// The returned match must be freed with zig_match_free(). | |
| 83 | export fn zig_regex_find(regex: ?*ZigRegex, input: [*:0]const u8) ?*ZigMatch { | |
| 84 | if (regex == null) return null; | |
| 85 | ||
| 86 | const regex_ptr: *const Regex = @ptrCast(@alignCast(regex.?)); | |
| 87 | const input_slice = std.mem.span(input); | |
| 88 | ||
| 89 | const maybe_match = regex_ptr.find(input_slice) catch return null; | |
| 90 | if (maybe_match) |match| { | |
| 91 | const match_ptr = c_allocator.create(Match) catch return null; | |
| 92 | match_ptr.* = match; | |
| 93 | return @ptrCast(match_ptr); | |
| 94 | } | |
| 95 | ||
| 96 | return null; | |
| 97 | } | |
| 98 | ||
| 99 | /// Get the matched substring | |
| 100 | /// | |
| 101 | /// Returns NULL if match is NULL. | |
| 102 | /// The returned string is owned by the match and should not be freed separately. | |
| 103 | export fn zig_match_get_text(match: ?*ZigMatch) ?[*:0]const u8 { | |
| 104 | if (match == null) return null; | |
| 105 | ||
| 106 | const match_ptr: *const Match = @ptrCast(@alignCast(match.?)); | |
| 107 | // Note: This assumes the slice is null-terminated, which may not always be true | |
| 108 | // In production, you'd want to copy to a null-terminated buffer | |
| 109 | return @ptrCast(match_ptr.slice.ptr); | |
| 110 | } | |
| 111 | ||
| 112 | /// Get the start position of a match | |
| 113 | export fn zig_match_get_start(match: ?*ZigMatch) c_int { | |
| 114 | if (match == null) return -1; | |
| 115 | ||
| 116 | const match_ptr: *const Match = @ptrCast(@alignCast(match.?)); | |
| 117 | return @intCast(match_ptr.start); | |
| 118 | } | |
| 119 | ||
| 120 | /// Get the end position of a match | |
| 121 | export fn zig_match_get_end(match: ?*ZigMatch) c_int { | |
| 122 | if (match == null) return -1; | |
| 123 | ||
| 124 | const match_ptr: *const Match = @ptrCast(@alignCast(match.?)); | |
| 125 | return @intCast(match_ptr.end); | |
| 126 | } | |
| 127 | ||
| 128 | /// Free a match result | |
| 129 | export fn zig_match_free(match: ?*ZigMatch) void { | |
| 130 | if (match) |m| { | |
| 131 | const match_ptr: *Match = @ptrCast(@alignCast(m)); | |
| 132 | var mut_match = match_ptr.*; | |
| 133 | mut_match.deinit(c_allocator); | |
| 134 | c_allocator.destroy(match_ptr); | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | /// Get version string | |
| 139 | export fn zig_regex_version() [*:0]const u8 { | |
| 140 | return "0.1.0"; | |
| 141 | } | |
| 142 | ||
| 143 | // Header file generator (for documentation) | |
| 144 | pub fn generateHeader(writer: anytype) !void { | |
| 145 | try writer.writeAll( | |
| 146 | \\#ifndef ZIG_REGEX_H | |
| 147 | \\#define ZIG_REGEX_H | |
| 148 | \\ | |
| 149 | \\#ifdef __cplusplus | |
| 150 | \\extern "C" { | |
| 151 | \\#endif | |
| 152 | \\ | |
| 153 | \\#include <stddef.h> | |
| 154 | \\#include <stdbool.h> | |
| 155 | \\ | |
| 156 | \\/* Opaque types */ | |
| 157 | \\typedef struct ZigRegex ZigRegex; | |
| 158 | \\typedef struct ZigMatch ZigMatch; | |
| 159 | \\ | |
| 160 | \\/* Error codes */ | |
| 161 | \\typedef enum { | |
| 162 | \\ ZIG_REGEX_OK = 0, | |
| 163 | \\ ZIG_REGEX_COMPILE_ERROR = -1, | |
| 164 | \\ ZIG_REGEX_INVALID_PATTERN = -2, | |
| 165 | \\ ZIG_REGEX_OUT_OF_MEMORY = -3, | |
| 166 | \\ ZIG_REGEX_NO_MATCH = -4, | |
| 167 | \\ ZIG_REGEX_NULL_POINTER = -5, | |
| 168 | \\} ZigRegexError; | |
| 169 | \\ | |
| 170 | \\/* Compile a regex pattern */ | |
| 171 | \\ZigRegex* zig_regex_compile(const char* pattern); | |
| 172 | \\ | |
| 173 | \\/* Free a compiled regex */ | |
| 174 | \\void zig_regex_free(ZigRegex* regex); | |
| 175 | \\ | |
| 176 | \\/* Check if pattern matches string (returns 1=match, 0=no match, -1=error) */ | |
| 177 | \\int zig_regex_is_match(ZigRegex* regex, const char* input); | |
| 178 | \\ | |
| 179 | \\/* Find first match in string (returns NULL if no match) */ | |
| 180 | \\ZigMatch* zig_regex_find(ZigRegex* regex, const char* input); | |
| 181 | \\ | |
| 182 | \\/* Get matched text from match result */ | |
| 183 | \\const char* zig_match_get_text(ZigMatch* match); | |
| 184 | \\ | |
| 185 | \\/* Get start position of match */ | |
| 186 | \\int zig_match_get_start(ZigMatch* match); | |
| 187 | \\ | |
| 188 | \\/* Get end position of match */ | |
| 189 | \\int zig_match_get_end(ZigMatch* match); | |
| 190 | \\ | |
| 191 | \\/* Free a match result */ | |
| 192 | \\void zig_match_free(ZigMatch* match); | |
| 193 | \\ | |
| 194 | \\/* Get library version */ | |
| 195 | \\const char* zig_regex_version(void); | |
| 196 | \\ | |
| 197 | \\#ifdef __cplusplus | |
| 198 | \\} | |
| 199 | \\#endif | |
| 200 | \\ | |
| 201 | \\#endif /* ZIG_REGEX_H */ | |
| 202 | \\ | |
| 203 | ); | |
| 204 | } | |
| 205 | ||
| 206 | test "C API header generation" { | |
| 207 | var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); | |
| 208 | defer aw.deinit(); | |
| 209 | ||
| 210 | try generateHeader(&aw.writer); | |
| 211 | var result = aw.toArrayList(); | |
| 212 | defer result.deinit(std.testing.allocator); | |
| 213 | try std.testing.expect(result.items.len > 0); | |
| 214 | try std.testing.expect(std.mem.indexOf(u8, result.items, "ZIG_REGEX_H") != null); | |
| 215 | } | |
| 1 | //const std = ; | |
| 2 | //const Regex = .Regex; | |
| 3 | //const Match = .Match; | |
| 4 | // | |
| 5 | ///// C FFI for the Zig regex library | |
| 6 | ///// | |
| 7 | ///// This module provides a C-compatible API for using the regex library | |
| 8 | ///// from other languages (C, C++, Python via ctypes, etc.) | |
| 9 | ///// | |
| 10 | ///// Example usage from C: | |
| 11 | ///// ```c | |
| 12 | ///// #include "zig_regex.h" | |
| 13 | ///// | |
| 14 | ///// ZigRegex* regex = zig_regex_compile("\\d+", NULL); | |
| 15 | ///// if (regex) { | |
| 16 | ///// bool matches = zig_regex_is_match(regex, "test 123"); | |
| 17 | ///// printf("Matches: %d\n", matches); | |
| 18 | ///// zig_regex_free(regex); | |
| 19 | ///// } | |
| 20 | ///// ``` | |
| 21 | ///// Opaque handle to a compiled regex | |
| 22 | //pub const ZigRegex = opaque {}; | |
| 23 | // | |
| 24 | ///// Opaque handle to a match result | |
| 25 | //pub const ZigMatch = opaque {}; | |
| 26 | // | |
| 27 | ///// Error codes | |
| 28 | //pub const ZigRegexError = enum(c_int) { | |
| 29 | // ok = 0, | |
| 30 | // compile_error = -1, | |
| 31 | // invalid_pattern = -2, | |
| 32 | // out_of_memory = -3, | |
| 33 | // no_match = -4, | |
| 34 | // null_pointer = -5, | |
| 35 | //}; | |
| 36 | // | |
| 37 | //// Global allocator for C API (uses C allocator) | |
| 38 | //var c_allocator = std.heap.c_allocator; | |
| 39 | // | |
| 40 | ///// Compile a regex pattern | |
| 41 | ///// | |
| 42 | ///// Returns NULL on error. Call zig_regex_get_last_error() to get error details. | |
| 43 | ///// The returned regex must be freed with zig_regex_free(). | |
| 44 | //export fn zig_regex_compile(pattern: [*:0]const u8) ?*ZigRegex { | |
| 45 | // const pattern_slice = std.mem.span(pattern); | |
| 46 | // | |
| 47 | // const regex_ptr = c_allocator.create(Regex) catch return null; | |
| 48 | // regex_ptr.* = Regex.compile(c_allocator, pattern_slice) catch { | |
| 49 | // c_allocator.destroy(regex_ptr); | |
| 50 | // return null; | |
| 51 | // }; | |
| 52 | // | |
| 53 | // return @ptrCast(regex_ptr); | |
| 54 | //} | |
| 55 | // | |
| 56 | ///// Free a compiled regex | |
| 57 | //export fn zig_regex_free(regex: ?*ZigRegex) void { | |
| 58 | // if (regex) |r| { | |
| 59 | // const regex_ptr: *Regex = @ptrCast(@alignCast(r)); | |
| 60 | // regex_ptr.deinit(); | |
| 61 | // c_allocator.destroy(regex_ptr); | |
| 62 | // } | |
| 63 | //} | |
| 64 | // | |
| 65 | ///// Check if a pattern matches a string | |
| 66 | ///// | |
| 67 | ///// Returns 1 if matches, 0 if no match, -1 on error | |
| 68 | //export fn zig_regex_is_match(regex: ?*ZigRegex, input: [*:0]const u8) c_int { | |
| 69 | // if (regex == null) return @intFromEnum(ZigRegexError.null_pointer); | |
| 70 | // | |
| 71 | // const regex_ptr: *const Regex = @ptrCast(@alignCast(regex.?)); | |
| 72 | // const input_slice = std.mem.span(input); | |
| 73 | // | |
| 74 | // const result = regex_ptr.isMatch(input_slice) catch return -1; | |
| 75 | // return if (result) 1 else 0; | |
| 76 | //} | |
| 77 | // | |
| 78 | ///// Find the first match in a string | |
| 79 | ///// | |
| 80 | ///// Returns NULL if no match or on error. | |
| 81 | ///// The returned match must be freed with zig_match_free(). | |
| 82 | //export fn zig_regex_find(regex: ?*ZigRegex, input: [*:0]const u8) ?*ZigMatch { | |
| 83 | // if (regex == null) return null; | |
| 84 | // | |
| 85 | // const regex_ptr: *const Regex = @ptrCast(@alignCast(regex.?)); | |
| 86 | // const input_slice = std.mem.span(input); | |
| 87 | // | |
| 88 | // const maybe_match = regex_ptr.find(input_slice) catch return null; | |
| 89 | // if (maybe_match) |match| { | |
| 90 | // const match_ptr = c_allocator.create(Match) catch return null; | |
| 91 | // match_ptr.* = match; | |
| 92 | // return @ptrCast(match_ptr); | |
| 93 | // } | |
| 94 | // | |
| 95 | // return null; | |
| 96 | //} | |
| 97 | // | |
| 98 | ///// Get the matched substring | |
| 99 | ///// | |
| 100 | ///// Returns NULL if match is NULL. | |
| 101 | ///// The returned string is owned by the match and should not be freed separately. | |
| 102 | //export fn zig_match_get_text(match: ?*ZigMatch) ?[*:0]const u8 { | |
| 103 | // if (match == null) return null; | |
| 104 | // | |
| 105 | // const match_ptr: *const Match = @ptrCast(@alignCast(match.?)); | |
| 106 | // // Note: This assumes the slice is null-terminated, which may not always be true | |
| 107 | // // In production, you'd want to copy to a null-terminated buffer | |
| 108 | // return @ptrCast(match_ptr.slice.ptr); | |
| 109 | //} | |
| 110 | // | |
| 111 | ///// Get the start position of a match | |
| 112 | //export fn zig_match_get_start(match: ?*ZigMatch) c_int { | |
| 113 | // if (match == null) return -1; | |
| 114 | // | |
| 115 | // const match_ptr: *const Match = @ptrCast(@alignCast(match.?)); | |
| 116 | // return @intCast(match_ptr.start); | |
| 117 | //} | |
| 118 | // | |
| 119 | ///// Get the end position of a match | |
| 120 | //export fn zig_match_get_end(match: ?*ZigMatch) c_int { | |
| 121 | // if (match == null) return -1; | |
| 122 | // | |
| 123 | // const match_ptr: *const Match = @ptrCast(@alignCast(match.?)); | |
| 124 | // return @intCast(match_ptr.end); | |
| 125 | //} | |
| 126 | // | |
| 127 | ///// Free a match result | |
| 128 | //export fn zig_match_free(match: ?*ZigMatch) void { | |
| 129 | // if (match) |m| { | |
| 130 | // const match_ptr: *Match = @ptrCast(@alignCast(m)); | |
| 131 | // var mut_match = match_ptr.*; | |
| 132 | // mut_match.deinit(c_allocator); | |
| 133 | // c_allocator.destroy(match_ptr); | |
| 134 | // } | |
| 135 | //} | |
| 136 | // | |
| 137 | ///// Get version string | |
| 138 | //export fn zig_regex_version() [*:0]const u8 { | |
| 139 | // return "0.1.0"; | |
| 140 | //} | |
| 141 | // | |
| 142 | //// Header file generator (for documentation) | |
| 143 | //pub fn generateHeader(writer: anytype) !void { | |
| 144 | // try writer.writeAll( | |
| 145 | // \\#ifndef ZIG_REGEX_H | |
| 146 | // \\#define ZIG_REGEX_H | |
| 147 | // \\ | |
| 148 | // \\#ifdef __cplusplus | |
| 149 | // \\extern "C" { | |
| 150 | // \\#endif | |
| 151 | // \\ | |
| 152 | // \\#include <stddef.h> | |
| 153 | // \\#include <stdbool.h> | |
| 154 | // \\ | |
| 155 | // \\/* Opaque types */ | |
| 156 | // \\typedef struct ZigRegex ZigRegex; | |
| 157 | // \\typedef struct ZigMatch ZigMatch; | |
| 158 | // \\ | |
| 159 | // \\/* Error codes */ | |
| 160 | // \\typedef enum { | |
| 161 | // \\ ZIG_REGEX_OK = 0, | |
| 162 | // \\ ZIG_REGEX_COMPILE_ERROR = -1, | |
| 163 | // \\ ZIG_REGEX_INVALID_PATTERN = -2, | |
| 164 | // \\ ZIG_REGEX_OUT_OF_MEMORY = -3, | |
| 165 | // \\ ZIG_REGEX_NO_MATCH = -4, | |
| 166 | // \\ ZIG_REGEX_NULL_POINTER = -5, | |
| 167 | // \\} ZigRegexError; | |
| 168 | // \\ | |
| 169 | // \\/* Compile a regex pattern */ | |
| 170 | // \\ZigRegex* zig_regex_compile(const char* pattern); | |
| 171 | // \\ | |
| 172 | // \\/* Free a compiled regex */ | |
| 173 | // \\void zig_regex_free(ZigRegex* regex); | |
| 174 | // \\ | |
| 175 | // \\/* Check if pattern matches string (returns 1=match, 0=no match, -1=error) */ | |
| 176 | // \\int zig_regex_is_match(ZigRegex* regex, const char* input); | |
| 177 | // \\ | |
| 178 | // \\/* Find first match in string (returns NULL if no match) */ | |
| 179 | // \\ZigMatch* zig_regex_find(ZigRegex* regex, const char* input); | |
| 180 | // \\ | |
| 181 | // \\/* Get matched text from match result */ | |
| 182 | // \\const char* zig_match_get_text(ZigMatch* match); | |
| 183 | // \\ | |
| 184 | // \\/* Get start position of match */ | |
| 185 | // \\int zig_match_get_start(ZigMatch* match); | |
| 186 | // \\ | |
| 187 | // \\/* Get end position of match */ | |
| 188 | // \\int zig_match_get_end(ZigMatch* match); | |
| 189 | // \\ | |
| 190 | // \\/* Free a match result */ | |
| 191 | // \\void zig_match_free(ZigMatch* match); | |
| 192 | // \\ | |
| 193 | // \\/* Get library version */ | |
| 194 | // \\const char* zig_regex_version(void); | |
| 195 | // \\ | |
| 196 | // \\#ifdef __cplusplus | |
| 197 | // \\} | |
| 198 | // \\#endif | |
| 199 | // \\ | |
| 200 | // \\#endif /* ZIG_REGEX_H */ | |
| 201 | // \\ | |
| 202 | // ); | |
| 203 | //} | |
| 204 | // | |
| 205 | //test "C API header generation" { | |
| 206 | // var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); | |
| 207 | // defer aw.deinit(); | |
| 208 | // | |
| 209 | // try generateHeader(&aw.writer); | |
| 210 | // var result = aw.toArrayList(); | |
| 211 | // defer result.deinit(std.testing.allocator); | |
| 212 | // try std.testing.expect(result.items.len > 0); | |
| 213 | // try std.testing.expect(std.mem.indexOf(u8, result.items, "ZIG_REGEX_H") != null); | |
| 214 | //} | |