Added PCRE2 support. (0.29.0)
This commit is contained in:
parent
5c3d4f1247
commit
d9804d9bf8
15 changed files with 596 additions and 3 deletions
|
|
@ -151,6 +151,9 @@ static const char *opcode_name(OpCode op) {
|
|||
case OP_PCSC_CONNECT: return "PCSC_CONNECT";
|
||||
case OP_PCSC_DISCONNECT: return "PCSC_DISCONNECT";
|
||||
case OP_PCSC_TRANSMIT: return "PCSC_TRANSMIT";
|
||||
case OP_PCRE2_TEST: return "PCRE2_TEST";
|
||||
case OP_PCRE2_MATCH: return "PCRE2_MATCH";
|
||||
case OP_PCRE2_FINDALL: return "PCRE2_FINDALL";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,11 @@ typedef enum {
|
|||
OP_PCSC_DISCONNECT, // pops handle id; returns 1/0
|
||||
OP_PCSC_TRANSMIT, // pops apdu array, handle id; returns map {"data":[],"sw1":n,"sw2":n,"code":n}
|
||||
|
||||
// PCRE2 regex ops (optional)
|
||||
OP_PCRE2_TEST, // pops flags, text, pattern; pushes 1/0
|
||||
OP_PCRE2_MATCH, // pops flags, text, pattern; pushes match map or Nil
|
||||
OP_PCRE2_FINDALL, // pops flags, text, pattern; pushes array of match maps
|
||||
|
||||
// Sockets (UNIX platforms)
|
||||
OP_SOCK_TCP_LISTEN, // pops backlog, port; returns listen fd (>0) or 0
|
||||
OP_SOCK_TCP_ACCEPT, // pops listen fd; returns client fd (>0) or 0
|
||||
|
|
|
|||
38
src/parser.c
38
src/parser.c
|
|
@ -787,6 +787,44 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
free(name);
|
||||
return 1;
|
||||
}
|
||||
/* PCRE2 builtins */
|
||||
if (strcmp(name, "pcre2_test") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (pattern, text, flags) */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_test expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "pcre2_test expects (pattern, text, flags)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_test expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "pcre2_test expects (pattern, text, flags)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_test expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after pcre2_test args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_PCRE2_TEST, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "pcre2_match") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_match expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "pcre2_match expects (pattern, text, flags)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_match expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "pcre2_match expects (pattern, text, flags)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_match expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after pcre2_match args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_PCRE2_MATCH, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "pcre2_findall") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)" ); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after pcre2_findall args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_PCRE2_FINDALL, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "pcsc_release") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcsc_release expects 1 argument (ctx)"); free(name); return 0; }
|
||||
|
|
|
|||
17
src/vm.c
17
src/vm.c
|
|
@ -20,6 +20,18 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Ensure PCRE2 is configured consistently across the whole translation unit.
|
||||
* vm.c includes many opcode implementation .c files; some use PCRE2. For PCRE2
|
||||
* headers to expose the correct typedefs (e.g., pcre2_code, PCRE2_SPTR), the
|
||||
* PCRE2_CODE_UNIT_WIDTH macro must be defined before the first inclusion of
|
||||
* <pcre2.h>. We do this once here when PCRE2 support is enabled. */
|
||||
#ifdef FUN_WITH_PCRE2
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
#endif
|
||||
|
||||
/* forward declarations for include mapping used in error reporting */
|
||||
extern char *preprocess_includes(const char *src);
|
||||
static int map_expanded_line_to_include(const char *path, int line, char *out_path, size_t out_path_cap, int *out_line);
|
||||
|
|
@ -667,6 +679,11 @@ void vm_run(VM *vm, Bytecode *entry) {
|
|||
#include "vm/json/from_file.c"
|
||||
#include "vm/json/to_file.c"
|
||||
|
||||
/* PCRE2 ops */
|
||||
#include "vm/pcre2/test.c"
|
||||
#include "vm/pcre2/match.c"
|
||||
#include "vm/pcre2/findall.c"
|
||||
|
||||
#include "vm/strings/find.c"
|
||||
#include "vm/strings/regex_match.c"
|
||||
#include "vm/strings/regex_search.c"
|
||||
|
|
|
|||
1
src/vm.h
1
src/vm.h
|
|
@ -40,6 +40,7 @@ static const char *opcode_names[] = {
|
|||
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
|
||||
"JSON_PARSE","JSON_STRINGIFY","JSON_FROM_FILE","JSON_TO_FILE",
|
||||
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT",
|
||||
"PCRE2_TEST","PCRE2_MATCH","PCRE2_FINDALL",
|
||||
"SOCK_TCP_LISTEN","SOCK_TCP_ACCEPT","SOCK_TCP_CONNECT","SOCK_SEND","SOCK_RECV","SOCK_CLOSE","SOCK_UNIX_LISTEN","SOCK_UNIX_CONNECT",
|
||||
"EXIT"
|
||||
};
|
||||
|
|
|
|||
100
src/vm/pcre2/findall.c
Normal file
100
src/vm/pcre2/findall.c
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-25
|
||||
*/
|
||||
|
||||
/* PCRE2_FINDALL */
|
||||
case OP_PCRE2_FINDALL: {
|
||||
#ifdef FUN_WITH_PCRE2
|
||||
Value vflags = pop_value(vm);
|
||||
Value vtext = pop_value(vm);
|
||||
Value vpat = pop_value(vm);
|
||||
int flags = 0;
|
||||
if (vflags.type == VAL_INT || vflags.type == VAL_BOOL) flags = (int)vflags.i;
|
||||
char *pattern = value_to_string_alloc(&vpat);
|
||||
char *subject = value_to_string_alloc(&vtext);
|
||||
free_value(vflags);
|
||||
free_value(vtext);
|
||||
free_value(vpat);
|
||||
if (!pattern || !subject) {
|
||||
if (pattern) free(pattern);
|
||||
if (subject) free(subject);
|
||||
push_value(vm, make_array_from_values(NULL, 0));
|
||||
break;
|
||||
}
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
int errorcode; PCRE2_SIZE erroff;
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) {
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, make_array_from_values(NULL, 0));
|
||||
break;
|
||||
}
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
Value out = make_array_from_values(NULL, 0);
|
||||
size_t subj_len = strlen(subject);
|
||||
size_t start_off = 0;
|
||||
int gcount = 0;
|
||||
while (1) {
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)subj_len, start_off, 0, mdata, NULL);
|
||||
if (rc <= 0) break;
|
||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(mdata);
|
||||
int s0 = (int)ov[0];
|
||||
int e0 = (int)ov[1];
|
||||
/* result map for this match */
|
||||
Value res = make_map_empty();
|
||||
char *full = string_substr(subject, s0, e0 - s0);
|
||||
(void)map_set(&res, "full", make_string(full ? full : ""));
|
||||
if (full) free(full);
|
||||
(void)map_set(&res, "start", make_int(s0));
|
||||
(void)map_set(&res, "end", make_int(e0));
|
||||
Value groups = make_array_from_values(NULL, 0);
|
||||
for (int i = 1; i < rc; ++i) {
|
||||
int s = (int)ov[2*i];
|
||||
int e = (int)ov[2*i+1];
|
||||
char *gstr = (s >= 0 && e >= s) ? string_substr(subject, s, e - s) : NULL;
|
||||
Value gv = make_string(gstr ? gstr : "");
|
||||
if (gstr) free(gstr);
|
||||
(void)array_push(&groups, gv);
|
||||
}
|
||||
(void)map_set(&res, "groups", groups);
|
||||
(void)array_push(&out, res);
|
||||
/* advance start offset; guard against empty match */
|
||||
if (e0 == s0) {
|
||||
if ((size_t)e0 < subj_len) {
|
||||
start_off = e0 + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
start_off = e0;
|
||||
}
|
||||
gcount = rc;
|
||||
}
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, out);
|
||||
#else
|
||||
Value a = pop_value(vm); free_value(a);
|
||||
Value b = pop_value(vm); free_value(b);
|
||||
Value c = pop_value(vm); free_value(c);
|
||||
push_value(vm, make_array_from_values(NULL, 0));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
89
src/vm/pcre2/match.c
Normal file
89
src/vm/pcre2/match.c
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-25
|
||||
*/
|
||||
|
||||
/* PCRE2_MATCH */
|
||||
case OP_PCRE2_MATCH: {
|
||||
#ifdef FUN_WITH_PCRE2
|
||||
Value vflags = pop_value(vm);
|
||||
Value vtext = pop_value(vm);
|
||||
Value vpat = pop_value(vm);
|
||||
int flags = 0;
|
||||
if (vflags.type == VAL_INT || vflags.type == VAL_BOOL) flags = (int)vflags.i;
|
||||
char *pattern = value_to_string_alloc(&vpat);
|
||||
char *subject = value_to_string_alloc(&vtext);
|
||||
free_value(vflags);
|
||||
free_value(vtext);
|
||||
free_value(vpat);
|
||||
if (!pattern || !subject) {
|
||||
if (pattern) free(pattern);
|
||||
if (subject) free(subject);
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
int errorcode; PCRE2_SIZE erroff;
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) {
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, 0, mdata, NULL);
|
||||
if (rc <= 0) {
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(mdata);
|
||||
/* Build result map */
|
||||
Value res = make_map_empty();
|
||||
int start0 = (int)ov[0];
|
||||
int end0 = (int)ov[1];
|
||||
char *full = string_substr(subject, start0, end0 - start0);
|
||||
(void)map_set(&res, "full", make_string(full ? full : ""));
|
||||
if (full) free(full);
|
||||
(void)map_set(&res, "start", make_int(start0));
|
||||
(void)map_set(&res, "end", make_int(end0));
|
||||
/* groups array (excluding group 0) */
|
||||
Value groups = make_array_from_values(NULL, 0);
|
||||
for (int i = 1; i < rc; ++i) {
|
||||
int s = (int)ov[2*i];
|
||||
int e = (int)ov[2*i+1];
|
||||
char *gstr = (s >= 0 && e >= s) ? string_substr(subject, s, e - s) : NULL;
|
||||
Value gv = make_string(gstr ? gstr : "");
|
||||
if (gstr) free(gstr);
|
||||
(void)array_push(&groups, gv);
|
||||
}
|
||||
(void)map_set(&res, "groups", groups);
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, res);
|
||||
#else
|
||||
Value a = pop_value(vm); free_value(a);
|
||||
Value b = pop_value(vm); free_value(b);
|
||||
Value c = pop_value(vm); free_value(c);
|
||||
push_value(vm, make_nil());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
62
src/vm/pcre2/test.c
Normal file
62
src/vm/pcre2/test.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-25
|
||||
*/
|
||||
|
||||
/* PCRE2_TEST */
|
||||
case OP_PCRE2_TEST: {
|
||||
#ifdef FUN_WITH_PCRE2
|
||||
Value vflags = pop_value(vm);
|
||||
Value vtext = pop_value(vm);
|
||||
Value vpat = pop_value(vm);
|
||||
int flags = 0;
|
||||
if (vflags.type == VAL_INT || vflags.type == VAL_BOOL) flags = (int)vflags.i;
|
||||
char *pattern = value_to_string_alloc(&vpat);
|
||||
char *subject = value_to_string_alloc(&vtext);
|
||||
free_value(vflags);
|
||||
free_value(vtext);
|
||||
free_value(vpat);
|
||||
if (!pattern || !subject) {
|
||||
if (pattern) free(pattern);
|
||||
if (subject) free(subject);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
int errorcode; PCRE2_SIZE erroff;
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) {
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, 0, mdata, NULL);
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
free(pattern); free(subject);
|
||||
push_value(vm, make_int(rc >= 0 ? 1 : 0));
|
||||
#else
|
||||
/* pop args and return 0 when PCRE2 disabled */
|
||||
Value a = pop_value(vm); free_value(a);
|
||||
Value b = pop_value(vm); free_value(b);
|
||||
Value c = pop_value(vm); free_value(c);
|
||||
push_value(vm, make_int(0));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue