2013-05-23 23:26:22 +02:00
|
|
|
#define _GNU_SOURCE
|
2013-03-09 21:42:49 -08:00
|
|
|
#include <string.h>
|
|
|
|
|
#include <assert.h>
|
2013-01-13 17:01:10 +01:00
|
|
|
#include "../internal.h"
|
|
|
|
|
#include "../parsers/parser_internal.h"
|
2013-03-09 21:42:49 -08:00
|
|
|
#include "regex.h"
|
2013-01-13 17:01:10 +01:00
|
|
|
|
|
|
|
|
#undef a_new
|
2013-03-09 21:42:49 -08:00
|
|
|
#define a_new(typ, count) a_new_(arena, typ, count)
|
2013-01-13 17:01:10 +01:00
|
|
|
// Stack VM
|
|
|
|
|
typedef enum HSVMOp_ {
|
|
|
|
|
SVM_PUSH, // Push a mark. There is no VM insn to push an object.
|
|
|
|
|
SVM_NOP, // Used to start the chain, and possibly elsewhere. Does nothing.
|
|
|
|
|
SVM_ACTION, // Same meaning as RVM_ACTION
|
|
|
|
|
SVM_CAPTURE, // Same meaning as RVM_CAPTURE
|
|
|
|
|
SVM_ACCEPT,
|
2013-05-23 23:26:22 +02:00
|
|
|
SVM_OPCOUNT
|
2013-01-13 17:01:10 +01:00
|
|
|
} HSVMOp;
|
|
|
|
|
|
|
|
|
|
typedef struct HRVMTrace_ {
|
|
|
|
|
struct HRVMTrace_ *next; // When parsing, these are
|
|
|
|
|
// reverse-threaded. There is a postproc
|
|
|
|
|
// step that inverts all the pointers.
|
2013-03-09 21:42:49 -08:00
|
|
|
size_t input_pos;
|
2013-01-13 17:01:10 +01:00
|
|
|
uint16_t arg;
|
|
|
|
|
uint8_t opcode;
|
|
|
|
|
} HRVMTrace;
|
|
|
|
|
|
|
|
|
|
typedef struct HRVMThread_ {
|
|
|
|
|
HRVMTrace *trace;
|
|
|
|
|
uint16_t ip;
|
|
|
|
|
} HRVMThread;
|
2013-02-20 02:25:42 -05:00
|
|
|
|
2013-03-09 21:42:49 -08:00
|
|
|
HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace, const uint8_t *input, int len);
|
|
|
|
|
|
|
|
|
|
HRVMTrace *invert_trace(HRVMTrace *trace) {
|
|
|
|
|
HRVMTrace *last = NULL;
|
|
|
|
|
if (!trace)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (!trace->next)
|
|
|
|
|
return trace;
|
|
|
|
|
do {
|
|
|
|
|
HRVMTrace *next = trace->next;
|
|
|
|
|
trace->next = last;
|
|
|
|
|
last = trace;
|
|
|
|
|
trace = next;
|
2013-05-23 23:26:22 +02:00
|
|
|
} while (trace);
|
|
|
|
|
return last;
|
2013-03-09 21:42:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* h_rvm_run__m(HAllocator *mm__, HRVMProg *prog, const uint8_t* input, size_t len) {
|
2013-01-13 17:01:10 +01:00
|
|
|
HArena *arena = h_new_arena(mm__, 0);
|
|
|
|
|
HRVMTrace **heads_p = a_new(HRVMTrace*, prog->length),
|
2013-03-09 21:42:49 -08:00
|
|
|
**heads_n = a_new(HRVMTrace*, prog->length);
|
2013-01-13 17:01:10 +01:00
|
|
|
|
2013-05-24 15:07:47 +02:00
|
|
|
HRVMTrace *ret_trace = NULL;
|
2013-01-13 17:01:10 +01:00
|
|
|
|
|
|
|
|
uint8_t *insn_seen = a_new(uint8_t, prog->length); // 0 -> not seen, 1->processed, 2->queued
|
|
|
|
|
HRVMThread *ip_queue = a_new(HRVMThread, prog->length);
|
|
|
|
|
size_t ipq_top;
|
|
|
|
|
|
2013-03-09 21:42:49 -08:00
|
|
|
|
|
|
|
|
|
2013-05-24 15:07:47 +02:00
|
|
|
|
2013-03-09 21:42:49 -08:00
|
|
|
|
2013-01-13 17:01:10 +01:00
|
|
|
#define THREAD ip_queue[ipq_top-1]
|
|
|
|
|
#define PUSH_SVM(op_, arg_) do { \
|
|
|
|
|
HRVMTrace *nt = a_new(HRVMTrace, 1); \
|
|
|
|
|
nt->arg = (arg_); \
|
|
|
|
|
nt->opcode = (op_); \
|
|
|
|
|
nt->next = THREAD.trace; \
|
2013-03-09 21:42:49 -08:00
|
|
|
nt->input_pos = off; \
|
2013-01-13 17:01:10 +01:00
|
|
|
THREAD.trace = nt; \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
heads_n[0] = a_new(HRVMTrace, 1); // zeroing
|
|
|
|
|
heads_n[0]->opcode = SVM_NOP;
|
|
|
|
|
|
|
|
|
|
size_t off = 0;
|
|
|
|
|
int live_threads = 1;
|
|
|
|
|
for (off = 0; off <= len; off++) {
|
|
|
|
|
uint8_t ch = ((off == len) ? 0 : input[off]);
|
2013-03-09 21:42:49 -08:00
|
|
|
size_t ip_s; // BUG: there was an unused variable ip. Not sure if
|
|
|
|
|
// I intended to use it somewhere.
|
2013-01-13 17:01:10 +01:00
|
|
|
/* scope */ {
|
|
|
|
|
HRVMTrace **heads_t;
|
|
|
|
|
heads_t = heads_n;
|
|
|
|
|
heads_n = heads_p;
|
|
|
|
|
heads_p = heads_t;
|
|
|
|
|
memset(heads_n, 0, prog->length * sizeof(*heads_n));
|
|
|
|
|
}
|
|
|
|
|
memset(insn_seen, 0, prog->length); // no insns seen yet
|
|
|
|
|
if (!live_threads)
|
2013-02-20 02:25:42 -05:00
|
|
|
goto match_fail;
|
2013-01-13 17:01:10 +01:00
|
|
|
live_threads = 0;
|
|
|
|
|
for (ip_s = 0; ip_s < prog->length; ip_s++) {
|
|
|
|
|
ipq_top = 1;
|
|
|
|
|
// TODO: Write this as a threaded VM
|
|
|
|
|
if (!heads_p[ip_s])
|
|
|
|
|
continue;
|
|
|
|
|
THREAD.ip = ip_s;
|
2013-05-24 02:50:05 +02:00
|
|
|
THREAD.trace = heads_p[ip_s];
|
2013-01-13 17:01:10 +01:00
|
|
|
uint8_t hi, lo;
|
|
|
|
|
uint16_t arg;
|
|
|
|
|
while(ipq_top > 0) {
|
2013-05-24 15:07:47 +02:00
|
|
|
if (insn_seen[THREAD.ip] == 1) {
|
|
|
|
|
ipq_top--; // Kill thread.
|
2013-01-13 17:01:10 +01:00
|
|
|
continue;
|
2013-05-24 15:07:47 +02:00
|
|
|
}
|
2013-03-09 21:42:49 -08:00
|
|
|
insn_seen[THREAD.ip] = 1;
|
2013-01-13 17:01:10 +01:00
|
|
|
arg = prog->insns[THREAD.ip].arg;
|
|
|
|
|
switch(prog->insns[THREAD.ip].op) {
|
|
|
|
|
case RVM_ACCEPT:
|
2013-02-20 02:25:42 -05:00
|
|
|
PUSH_SVM(SVM_ACCEPT, 0);
|
|
|
|
|
ret_trace = THREAD.trace;
|
2013-05-24 15:07:47 +02:00
|
|
|
ipq_top--;
|
|
|
|
|
goto next_insn;
|
2013-01-13 17:01:10 +01:00
|
|
|
case RVM_MATCH:
|
|
|
|
|
hi = (arg >> 8) & 0xff;
|
|
|
|
|
lo = arg & 0xff;
|
|
|
|
|
THREAD.ip++;
|
2013-03-17 22:44:30 -07:00
|
|
|
if (ch < lo || ch > hi)
|
2013-01-13 17:01:10 +01:00
|
|
|
ipq_top--; // terminate thread
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_GOTO:
|
|
|
|
|
THREAD.ip = arg;
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_FORK:
|
|
|
|
|
THREAD.ip++;
|
2013-03-09 21:42:49 -08:00
|
|
|
if (!insn_seen[arg]) {
|
|
|
|
|
insn_seen[THREAD.ip] = 2;
|
2013-01-13 17:01:10 +01:00
|
|
|
HRVMTrace* tr = THREAD.trace;
|
|
|
|
|
ipq_top++;
|
|
|
|
|
THREAD.ip = arg;
|
|
|
|
|
THREAD.trace = tr;
|
|
|
|
|
}
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_PUSH:
|
2013-03-09 21:42:49 -08:00
|
|
|
PUSH_SVM(SVM_PUSH, 0);
|
2013-01-13 17:01:10 +01:00
|
|
|
THREAD.ip++;
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_ACTION:
|
|
|
|
|
PUSH_SVM(SVM_ACTION, arg);
|
|
|
|
|
THREAD.ip++;
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_CAPTURE:
|
|
|
|
|
PUSH_SVM(SVM_CAPTURE, 0);
|
|
|
|
|
THREAD.ip++;
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_EOF:
|
|
|
|
|
THREAD.ip++;
|
|
|
|
|
if (off != len)
|
|
|
|
|
ipq_top--; // Terminate thread
|
|
|
|
|
goto next_insn;
|
|
|
|
|
case RVM_STEP:
|
|
|
|
|
// save thread
|
|
|
|
|
live_threads++;
|
2013-05-23 23:26:22 +02:00
|
|
|
heads_n[++THREAD.ip] = THREAD.trace;
|
2013-01-13 17:01:10 +01:00
|
|
|
ipq_top--;
|
|
|
|
|
goto next_insn;
|
|
|
|
|
}
|
|
|
|
|
next_insn:
|
2013-03-09 21:42:49 -08:00
|
|
|
;
|
2013-01-13 17:01:10 +01:00
|
|
|
|
|
|
|
|
}
|
2013-02-20 02:25:42 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// No accept was reached.
|
|
|
|
|
match_fail:
|
2013-05-24 15:07:47 +02:00
|
|
|
if (ret_trace == NULL) {
|
|
|
|
|
// No match found; definite failure.
|
|
|
|
|
h_delete_arena(arena);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2013-01-13 17:01:10 +01:00
|
|
|
|
2013-02-20 02:25:42 -05:00
|
|
|
// Invert the direction of the trace linked list.
|
|
|
|
|
|
|
|
|
|
ret_trace = invert_trace(ret_trace);
|
2013-03-09 21:42:49 -08:00
|
|
|
HParseResult *ret = run_trace(mm__, prog, ret_trace, input, len);
|
2013-02-20 02:25:42 -05:00
|
|
|
// ret is in its own arena
|
|
|
|
|
h_delete_arena(arena);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2013-03-09 21:42:49 -08:00
|
|
|
#undef PUSH_SVM
|
|
|
|
|
#undef THREAD
|
2013-02-20 02:25:42 -05:00
|
|
|
|
2013-03-09 21:42:49 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void svm_stack_ensure_cap(HAllocator *mm__, HSVMContext *ctx, size_t addl) {
|
|
|
|
|
if (ctx->stack_count + addl >= ctx->stack_capacity) {
|
|
|
|
|
ctx->stack = mm__->realloc(mm__, ctx->stack, sizeof(*ctx->stack) * (ctx->stack_capacity *= 2));
|
|
|
|
|
// TODO: check for realloc failure
|
|
|
|
|
}
|
2013-01-13 17:01:10 +01:00
|
|
|
}
|
2013-03-09 17:25:25 -08:00
|
|
|
|
2013-03-09 21:42:49 -08:00
|
|
|
HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace, const uint8_t *input, int len) {
|
|
|
|
|
// orig_prog is only used for the action table
|
|
|
|
|
HSVMContext ctx;
|
|
|
|
|
HArena *arena = h_new_arena(mm__, 0);
|
|
|
|
|
ctx.stack_count = 0;
|
|
|
|
|
ctx.stack_capacity = 16;
|
|
|
|
|
ctx.stack = h_new(HParsedToken*, ctx.stack_capacity);
|
|
|
|
|
|
|
|
|
|
HParsedToken *tmp_res;
|
|
|
|
|
HRVMTrace *cur;
|
|
|
|
|
for (cur = trace; cur; cur = cur->next) {
|
|
|
|
|
switch (cur->opcode) {
|
|
|
|
|
case SVM_PUSH:
|
|
|
|
|
svm_stack_ensure_cap(mm__, &ctx, 1);
|
|
|
|
|
tmp_res = a_new(HParsedToken, 1);
|
|
|
|
|
tmp_res->token_type = TT_MARK;
|
|
|
|
|
tmp_res->index = cur->input_pos;
|
|
|
|
|
tmp_res->bit_offset = 0;
|
|
|
|
|
ctx.stack[ctx.stack_count++] = tmp_res;
|
|
|
|
|
break;
|
|
|
|
|
case SVM_NOP:
|
|
|
|
|
break;
|
|
|
|
|
case SVM_ACTION:
|
|
|
|
|
// Action should modify stack appropriately
|
2013-03-17 22:01:54 -07:00
|
|
|
if (!orig_prog->actions[cur->arg].action(arena, &ctx, orig_prog->actions[cur->arg].env)) {
|
2013-05-24 15:07:47 +02:00
|
|
|
|
2013-03-09 21:42:49 -08:00
|
|
|
// action failed... abort somehow
|
2013-05-24 15:07:47 +02:00
|
|
|
goto fail;
|
2013-03-09 21:42:49 -08:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SVM_CAPTURE:
|
|
|
|
|
// Top of stack must be a mark
|
|
|
|
|
// This replaces said mark in-place with a TT_BYTES.
|
2013-05-23 23:26:22 +02:00
|
|
|
assert(ctx.stack[ctx.stack_count-1]->token_type == TT_MARK);
|
2013-03-09 21:42:49 -08:00
|
|
|
|
2013-05-23 23:26:22 +02:00
|
|
|
tmp_res = ctx.stack[ctx.stack_count-1];
|
2013-03-09 21:42:49 -08:00
|
|
|
tmp_res->token_type = TT_BYTES;
|
|
|
|
|
// TODO: Will need to copy if bit_offset is nonzero
|
|
|
|
|
assert(tmp_res->bit_offset == 0);
|
|
|
|
|
|
|
|
|
|
tmp_res->bytes.token = input + tmp_res->index;
|
2013-05-23 23:26:22 +02:00
|
|
|
tmp_res->bytes.len = cur->input_pos - tmp_res->index;
|
2013-03-09 21:42:49 -08:00
|
|
|
break;
|
|
|
|
|
case SVM_ACCEPT:
|
2013-05-24 02:50:05 +02:00
|
|
|
assert(ctx.stack_count <= 1);
|
|
|
|
|
HParseResult *res = a_new(HParseResult, 1);
|
|
|
|
|
if (ctx.stack_count == 1) {
|
|
|
|
|
res->ast = ctx.stack[0];
|
|
|
|
|
} else {
|
|
|
|
|
res->ast = NULL;
|
|
|
|
|
}
|
2013-03-09 21:42:49 -08:00
|
|
|
res->bit_length = cur->input_pos * 8;
|
|
|
|
|
res->arena = arena;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-24 15:07:47 +02:00
|
|
|
fail:
|
2013-03-09 21:42:49 -08:00
|
|
|
h_delete_arena(arena);
|
|
|
|
|
return NULL;
|
2013-03-09 17:25:25 -08:00
|
|
|
}
|
2013-03-09 21:42:49 -08:00
|
|
|
|
2013-03-17 20:17:55 -07:00
|
|
|
uint16_t h_rvm_create_action(HRVMProg *prog, HSVMActionFunc action_func, void* env) {
|
|
|
|
|
for (uint16_t i = 0; i < prog->action_count; i++) {
|
|
|
|
|
if (prog->actions[i].action == action_func && prog->actions[i].env == env)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
// Ensure that there's room in the action array...
|
|
|
|
|
if (!(prog->action_count & (prog->action_count + 1))) {
|
|
|
|
|
// needs to be scaled up.
|
2013-03-17 22:01:54 -07:00
|
|
|
size_t array_size = (prog->action_count + 1) * 2; // action_count+1 is a
|
2013-03-17 20:17:55 -07:00
|
|
|
// power of two
|
2013-03-17 22:01:54 -07:00
|
|
|
prog->actions = prog->allocator->realloc(prog->allocator, prog->actions, array_size * sizeof(*prog->actions));
|
2013-03-17 20:17:55 -07:00
|
|
|
// TODO: Handle the allocation failed case nicely.
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-17 22:01:54 -07:00
|
|
|
HSVMAction *action = &prog->actions[prog->action_count];
|
2013-03-17 20:17:55 -07:00
|
|
|
action->action = action_func;
|
|
|
|
|
action->env = env;
|
|
|
|
|
return prog->action_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t h_rvm_insert_insn(HRVMProg *prog, HRVMOp op, uint16_t arg) {
|
|
|
|
|
// Ensure that there's room in the insn array...
|
|
|
|
|
if (!(prog->length & (prog->length + 1))) {
|
|
|
|
|
// needs to be scaled up.
|
2013-03-17 22:01:54 -07:00
|
|
|
size_t array_size = (prog->length + 1) * 2; // action_count+1 is a
|
|
|
|
|
// power of two
|
|
|
|
|
prog->insns = prog->allocator->realloc(prog->allocator, prog->insns, array_size * sizeof(*prog->insns));
|
2013-03-17 20:17:55 -07:00
|
|
|
// TODO: Handle the allocation failed case nicely.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prog->insns[prog->length].op = op;
|
|
|
|
|
prog->insns[prog->length].arg = arg;
|
|
|
|
|
return prog->length++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t h_rvm_get_ip(HRVMProg *prog) {
|
|
|
|
|
return prog->length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void h_rvm_patch_arg(HRVMProg *prog, uint16_t ip, uint16_t new_val) {
|
|
|
|
|
assert(prog->length > ip);
|
|
|
|
|
prog->insns[ip].arg = new_val;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-26 20:36:54 -07:00
|
|
|
size_t h_svm_count_to_mark(HSVMContext *ctx) {
|
|
|
|
|
size_t ctm;
|
2013-05-24 12:22:15 +02:00
|
|
|
for (ctm = 0; ctm < ctx->stack_count; ctm++) {
|
2013-04-26 20:36:54 -07:00
|
|
|
if (ctx->stack[ctx->stack_count - 1 - ctm]->token_type == TT_MARK)
|
|
|
|
|
return ctm;
|
|
|
|
|
}
|
|
|
|
|
return ctx->stack_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Implement the primitive actions
|
|
|
|
|
bool h_svm_action_make_sequence(HArena *arena, HSVMContext *ctx, void* env) {
|
|
|
|
|
size_t n_items = h_svm_count_to_mark(ctx);
|
|
|
|
|
assert (n_items < ctx->stack_count);
|
|
|
|
|
HParsedToken *res = ctx->stack[ctx->stack_count - 1 - n_items];
|
|
|
|
|
assert (res->token_type == TT_MARK);
|
|
|
|
|
res->token_type = TT_SEQUENCE;
|
2013-05-24 15:07:47 +02:00
|
|
|
|
2013-04-26 20:36:54 -07:00
|
|
|
HCountedArray *ret_carray = h_carray_new_sized(arena, n_items);
|
|
|
|
|
res->seq = ret_carray;
|
|
|
|
|
// res index and bit offset are the same as the mark.
|
|
|
|
|
for (size_t i = 0; i < n_items; i++) {
|
|
|
|
|
ret_carray->elements[i] = ctx->stack[ctx->stack_count - n_items + i];
|
|
|
|
|
}
|
2013-05-24 15:07:47 +02:00
|
|
|
ret_carray->used = n_items;
|
2013-04-26 20:36:54 -07:00
|
|
|
ctx->stack_count -= n_items;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool h_svm_action_clear_to_mark(HArena *arena, HSVMContext *ctx, void* env) {
|
2013-05-24 15:07:47 +02:00
|
|
|
while (ctx->stack_count > 0) { if (ctx->stack[--ctx->stack_count]->token_type == TT_MARK)
|
2013-04-26 20:36:54 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false; // no mark found.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Glue regex backend to rest of system
|
|
|
|
|
|
|
|
|
|
bool h_compile_regex(HRVMProg *prog, const HParser *parser) {
|
|
|
|
|
return parser->vtable->compile_to_rvm(prog, parser->env);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void h_regex_free(HParser *parser) {
|
|
|
|
|
HRVMProg *prog = (HRVMProg*)parser->backend_data;
|
|
|
|
|
HAllocator *mm__ = prog->allocator;
|
|
|
|
|
h_free(prog->insns);
|
|
|
|
|
h_free(prog->actions);
|
|
|
|
|
h_free(prog);
|
|
|
|
|
parser->backend_data = NULL;
|
|
|
|
|
parser->backend = PB_PACKRAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int h_regex_compile(HAllocator *mm__, HParser* parser, const void* params) {
|
|
|
|
|
if (!parser->vtable->isValidRegular(parser->env))
|
|
|
|
|
return 1;
|
|
|
|
|
HRVMProg *prog = h_new(HRVMProg, 1);
|
2013-05-25 03:35:42 +02:00
|
|
|
prog->length = prog->action_count = 0;
|
|
|
|
|
prog->insns = NULL;
|
|
|
|
|
prog->actions = NULL;
|
2013-04-26 20:36:54 -07:00
|
|
|
prog->allocator = mm__;
|
|
|
|
|
if (!h_compile_regex(prog, parser)) {
|
|
|
|
|
h_free(prog->insns);
|
|
|
|
|
h_free(prog->actions);
|
|
|
|
|
h_free(prog);
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
2013-05-23 23:26:22 +02:00
|
|
|
h_rvm_insert_insn(prog, RVM_ACCEPT, 0);
|
2013-04-26 20:36:54 -07:00
|
|
|
parser->backend_data = prog;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static HParseResult *h_regex_parse(HAllocator* mm__, const HParser* parser, HInputStream *input_stream) {
|
|
|
|
|
return h_rvm_run__m(mm__, (HRVMProg*)parser->backend_data, input_stream->input, input_stream->length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HParserBackendVTable h__regex_backend_vtable = {
|
|
|
|
|
.compile = h_regex_compile,
|
|
|
|
|
.parse = h_regex_parse,
|
|
|
|
|
.free = h_regex_free
|
|
|
|
|
};
|
2013-05-23 23:26:22 +02:00
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
#include "regex_debug.c"
|
|
|
|
|
#endif
|