1
0
Fork 0
forked from fun/fun

Added some standard C regex implementation. (0.20.0)

This commit is contained in:
Johannes Findeisen 2025-10-04 01:49:22 +02:00
commit 94602f0855
12 changed files with 438 additions and 1 deletions

View file

@ -0,0 +1,57 @@
/**
* This file is part of the Fun programming language.
* https://hanez.org/project/fun/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-10-04
*/
/* Regex full-match opcode using POSIX regex */
#ifdef __unix__
#include <regex.h>
#endif
case OP_REGEX_MATCH: {
Value pattern = pop_value(vm);
Value str = pop_value(vm);
if (str.type != VAL_STRING || pattern.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: REGEX_MATCH expects (string, string)\n");
exit(1);
}
#ifndef __unix__
/* Not supported on non-UNIX: return 0 gracefully */
free_value(pattern);
int truth = 0;
free_value(str);
push_value(vm, make_int(truth));
break;
#else
regex_t rx;
int rc = regcomp(&rx, pattern.s ? pattern.s : "", REG_EXTENDED);
if (rc != 0) {
/* invalid regex -> false */
free_value(pattern);
free_value(str);
push_value(vm, make_int(0));
break;
}
regmatch_t m;
int ok = regexec(&rx, str.s ? str.s : "", 1, &m, 0) == 0;
int truth = 0;
if (ok) {
/* full match means the match spans whole string */
if (m.rm_so == 0 && str.s) {
size_t slen = strlen(str.s);
truth = (m.rm_eo == (regoff_t)slen) ? 1 : 0;
}
}
regfree(&rx);
free_value(pattern);
free_value(str);
push_value(vm, make_int(truth));
break;
#endif
}

View file

@ -0,0 +1,116 @@
/**
* This file is part of the Fun programming language.
* https://hanez.org/project/fun/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-10-04
*/
/* Regex global replace opcode using POSIX regex */
#ifdef __unix__
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#endif
case OP_REGEX_REPLACE: {
Value repl = pop_value(vm);
Value pattern = pop_value(vm);
Value str = pop_value(vm);
if (str.type != VAL_STRING || pattern.type != VAL_STRING || repl.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: REGEX_REPLACE expects (string, string, string)\n");
exit(1);
}
#ifndef __unix__
/* Not supported: return original string */
Value out = make_string(str.s ? str.s : "");
free_value(repl);
free_value(pattern);
free_value(str);
push_value(vm, out);
break;
#else
regex_t rx;
int rc = regcomp(&rx, pattern.s ? pattern.s : "", REG_EXTENDED);
if (rc != 0) {
/* invalid regex -> return original */
Value out = make_string(str.s ? str.s : "");
free_value(repl);
free_value(pattern);
free_value(str);
push_value(vm, out);
break;
}
const char *s = str.s ? str.s : "";
const char *r = repl.s ? repl.s : "";
size_t out_cap = strlen(s) + 1;
char *outbuf = (char*)malloc(out_cap);
size_t out_len = 0;
size_t pos = 0;
enum { MAX_CAP = 16 };
regmatch_t caps[MAX_CAP];
while (1) {
if (regexec(&rx, s + pos, MAX_CAP, caps, 0) != 0) {
/* no more matches: append the rest */
size_t rest = strlen(s + pos);
if (out_len + rest + 1 > out_cap) {
out_cap = out_len + rest + 1;
outbuf = (char*)realloc(outbuf, out_cap);
}
memcpy(outbuf + out_len, s + pos, rest + 1);
out_len += rest;
break;
}
int mstart = (int)caps[0].rm_so;
int mend = (int)caps[0].rm_eo;
if (mstart < 0 || mend < mstart) {
/* Shouldn't happen, avoid infinite loop */
break;
}
/* append prefix */
size_t pre_len = (size_t)mstart;
if (out_len + pre_len + 1 > out_cap) {
out_cap = (out_len + pre_len + 1) * 2;
outbuf = (char*)realloc(outbuf, out_cap);
}
memcpy(outbuf + out_len, s + pos, pre_len);
out_len += pre_len;
/* append replacement (no backref expansion for simplicity) */
size_t rlen = strlen(r);
if (out_len + rlen + 1 > out_cap) {
out_cap = (out_len + rlen + 1) * 2;
outbuf = (char*)realloc(outbuf, out_cap);
}
memcpy(outbuf + out_len, r, rlen);
out_len += rlen;
/* advance */
pos += (size_t)mend;
if (mend == 0) { /* prevent zero-length match infinite loop */
if (pos < strlen(s)) {
if (out_len + 1 > out_cap) { out_cap = out_len + 2; outbuf = (char*)realloc(outbuf, out_cap);}
outbuf[out_len++] = s[pos++];
} else {
break;
}
}
}
Value out = make_string(outbuf ? outbuf : "");
if (outbuf) free(outbuf);
regfree(&rx);
free_value(repl);
free_value(pattern);
free_value(str);
push_value(vm, out);
break;
#endif
}

View file

@ -0,0 +1,113 @@
/**
* This file is part of the Fun programming language.
* https://hanez.org/project/fun/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-10-04
*/
/* Regex search (first match) opcode using POSIX regex */
#ifdef __unix__
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#endif
case OP_REGEX_SEARCH: {
Value pattern = pop_value(vm);
Value str = pop_value(vm);
if (str.type != VAL_STRING || pattern.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: REGEX_SEARCH expects (string, string)\n");
exit(1);
}
#ifndef __unix__
/* Return default empty result on unsupported platforms */
Value m = make_map_empty();
(void)map_set(&m, "match", make_string(""));
(void)map_set(&m, "start", make_int(-1));
(void)map_set(&m, "end", make_int(-1));
Value emptyArr = make_array_from_values(NULL, 0);
(void)map_set(&m, "groups", emptyArr);
free_value(pattern);
free_value(str);
push_value(vm, m);
break;
#else
regex_t rx;
int rc = regcomp(&rx, pattern.s ? pattern.s : "", REG_EXTENDED);
if (rc != 0) {
/* invalid regex -> empty result */
Value m = make_map_empty();
(void)map_set(&m, "match", make_string(""));
(void)map_set(&m, "start", make_int(-1));
(void)map_set(&m, "end", make_int(-1));
Value emptyArr = make_array_from_values(NULL, 0);
(void)map_set(&m, "groups", emptyArr);
free_value(pattern);
free_value(str);
push_value(vm, m);
break;
}
/* capture up to, say, 10 groups (including whole match) */
enum { MAX_CAP = 16 };
regmatch_t caps[MAX_CAP];
int ok = regexec(&rx, str.s ? str.s : "", MAX_CAP, caps, 0) == 0;
Value outMap = make_map_empty();
if (!ok) {
(void)map_set(&outMap, "match", make_string(""));
(void)map_set(&outMap, "start", make_int(-1));
(void)map_set(&outMap, "end", make_int(-1));
Value emptyArr = make_array_from_values(NULL, 0);
(void)map_set(&outMap, "groups", emptyArr);
} else {
int s = (int)caps[0].rm_so;
int e = (int)caps[0].rm_eo;
char *matchStr = NULL;
if (str.s && s >= 0 && e >= s) {
int len = e - s;
matchStr = (char*)malloc((size_t)len + 1);
if (matchStr) { memcpy(matchStr, str.s + s, (size_t)len); matchStr[len] = '\0'; }
}
(void)map_set(&outMap, "match", make_string(matchStr ? matchStr : ""));
if (matchStr) free(matchStr);
(void)map_set(&outMap, "start", make_int(s));
(void)map_set(&outMap, "end", make_int(e));
/* groups 1..n */
Value groupsArr = make_array_from_values(NULL, 0);
/* Count groups available */
int groupCount = 0;
for (int i = 1; i < MAX_CAP; ++i) {
if (caps[i].rm_so == -1 || caps[i].rm_eo == -1) break;
groupCount++;
}
if (groupCount > 0) {
Value *vals = (Value*)calloc((size_t)groupCount, sizeof(Value));
int vi = 0;
for (int i = 1; i <= groupCount; ++i) {
int gs = (int)caps[i].rm_so;
int ge = (int)caps[i].rm_eo;
char *gstr = NULL;
if (str.s && gs >= 0 && ge >= gs) {
int gl = ge - gs;
gstr = (char*)malloc((size_t)gl + 1);
if (gstr) { memcpy(gstr, str.s + gs, (size_t)gl); gstr[gl] = '\0'; }
}
vals[vi++] = make_string(gstr ? gstr : "");
if (gstr) free(gstr);
}
groupsArr = make_array_from_values(vals, groupCount);
for (int i = 0; i < groupCount; ++i) free_value(vals[i]);
free(vals);
}
(void)map_set(&outMap, "groups", groupsArr);
}
regfree(&rx);
free_value(pattern);
free_value(str);
push_value(vm, outMap);
break;
#endif
}