bp/vm.c

702 lines
23 KiB
C
Raw Normal View History

2020-09-11 01:28:06 -07:00
/*
* vm.c - Code for the BPEG virtual machine that performs the matching.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
2020-09-11 02:55:15 -07:00
#include "grammar.h"
2020-12-14 22:01:50 -08:00
#include "types.h"
2020-09-11 01:28:06 -07:00
#include "utils.h"
2020-12-14 22:01:50 -08:00
#include "vm.h"
2020-09-11 01:28:06 -07:00
2020-09-14 12:16:15 -07:00
static match_t *match_backref(const char *str, vm_op_t *op, match_t *m, unsigned int flags);
2020-09-12 18:20:13 -07:00
static size_t push_backrefs(grammar_t *g, match_t *m);
static match_t *get_capture_n(match_t *m, int *n);
static match_t *get_capture_named(match_t *m, const char *name);
/*
* The names of the opcodes (keep in sync with the enum definition above)
*/
static const char *opcode_names[] = {
[VM_ANYCHAR] = "ANYCHAR",
[VM_STRING] = "STRING",
[VM_RANGE] = "RANGE",
[VM_NOT] = "NOT",
[VM_UPTO_AND] = "UPTO_AND",
2020-09-12 18:20:13 -07:00
[VM_REPEAT] = "REPEAT",
[VM_BEFORE] = "BEFORE",
[VM_AFTER] = "AFTER",
[VM_CAPTURE] = "CAPTURE",
[VM_HIDE] = "HIDE",
2020-09-12 18:20:13 -07:00
[VM_OTHERWISE] = "OTHERWISE",
[VM_CHAIN] = "CHAIN",
[VM_REPLACE] = "REPLACE",
2020-09-13 20:33:11 -07:00
[VM_EQUAL] = "EQUAL",
2020-09-28 17:56:02 -07:00
[VM_NOT_EQUAL] = "NOT_EQUAL",
2020-09-12 18:20:13 -07:00
[VM_REF] = "REF",
[VM_BACKREF] = "BACKREF",
2020-09-14 01:21:49 -07:00
[VM_NODENT] = "NODENT",
2020-09-12 18:20:13 -07:00
};
// UTF8-compliant char iteration
2020-09-25 08:41:29 -07:00
static inline const char *next_char(file_t *f, const char *str)
{
char c = *str;
++str;
if (__builtin_expect(!(c & 0x80), 1))
return str;
if (__builtin_expect(str < f->end && !!(*str & 0x80), 1))
++str;
if (c > '\xDF' && __builtin_expect(str < f->end && !!(*str & 0x80), 1))
++str;
if (c > '\xEF' && __builtin_expect(str < f->end && !!(*str & 0x80), 1))
++str;
return str;
}
2020-09-12 18:20:13 -07:00
const char *opcode_name(enum VMOpcode o)
{
return opcode_names[o];
}
2020-09-11 01:28:06 -07:00
/*
* Recursively deallocate a match object and set to NULL
*/
void destroy_match(match_t **m)
{
if (!*m) return;
2020-09-11 01:28:06 -07:00
destroy_match(&((*m)->child));
destroy_match(&((*m)->nextsibling));
*m = NULL;
}
2020-09-12 18:20:13 -07:00
static size_t push_backrefs(grammar_t *g, match_t *m)
{
if (m == NULL) return 0;
if (m->op->op == VM_REF) return 0;
size_t count = 0;
if (m->op->op == VM_CAPTURE && m->op->args.capture.name) {
2020-09-12 18:20:13 -07:00
++count;
push_backref(g, m->op->args.capture.name, m->child);
2020-09-12 18:20:13 -07:00
}
if (m->child) count += push_backrefs(g, m->child);
if (m->nextsibling) count += push_backrefs(g, m->nextsibling);
return count;
}
2020-09-13 00:48:39 -07:00
typedef struct recursive_ref_s {
const vm_op_t *op;
const char *pos;
struct recursive_ref_s *prev;
int hit;
match_t *result;
} recursive_ref_t;
2020-09-11 01:28:06 -07:00
/*
* Run virtual machine operation against a string and return
* a match struct, or NULL if no match is found.
* The returned value should be free()'d to avoid memory leaking.
*/
2020-09-16 19:35:43 -07:00
static match_t *_match(grammar_t *g, file_t *f, const char *str, vm_op_t *op, unsigned int flags, recursive_ref_t *rec)
2020-09-11 01:28:06 -07:00
{
switch (op->op) {
case VM_ANYCHAR: {
if (str >= f->end || (!op->multiline && *str == '\n'))
2020-09-11 01:28:06 -07:00
return NULL;
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->op = op;
m->start = str;
2020-09-25 08:41:29 -07:00
m->end = next_char(f, str);
2020-09-11 01:28:06 -07:00
return m;
}
case VM_STRING: {
if (&str[op->len] > f->end) return NULL;
if ((flags & BPEG_IGNORECASE) ? memicmp(str, op->args.s, (size_t)op->len) != 0
: memcmp(str, op->args.s, (size_t)op->len) != 0)
2020-09-11 01:28:06 -07:00
return NULL;
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->op = op;
m->start = str;
m->end = str + op->len;
return m;
}
case VM_RANGE: {
2020-12-19 18:51:30 -08:00
if (str >= f->end) return NULL;
2020-09-28 21:30:43 -07:00
if ((unsigned char)*str < op->args.range.low || (unsigned char)*str > op->args.range.high)
2020-09-11 01:28:06 -07:00
return NULL;
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->op = op;
m->start = str;
m->end = str + 1;
return m;
}
case VM_NOT: {
2020-09-16 19:35:43 -07:00
match_t *m = _match(g, f, str, op->args.pat, flags, rec);
2020-09-11 01:28:06 -07:00
if (m != NULL) {
destroy_match(&m);
return NULL;
}
m = new(match_t);
2020-09-11 01:28:06 -07:00
m->op = op;
m->start = str;
m->end = str;
return m;
}
case VM_UPTO_AND: {
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->op = op;
2020-12-14 18:39:31 -08:00
if (!op->args.multiple.first && !op->args.multiple.second) {
if (op->multiline) {
str = f->end;
} else {
while (str < f->end && *str != '\n') ++str;
}
} else {
2020-12-14 18:11:33 -08:00
match_t **dest = &m->child;
for (const char *prev = NULL; prev < str; ) {
prev = str;
2020-12-14 18:39:31 -08:00
if (op->args.multiple.first) {
match_t *p = _match(g, f, str, op->args.multiple.first, flags, rec);
if (p) {
*dest = p;
m->end = p->end;
return m;
}
}
2020-12-14 18:11:33 -08:00
if (op->args.multiple.second) {
2020-12-14 18:39:31 -08:00
match_t *p = _match(g, f, str, op->args.multiple.second, flags, rec);
2020-12-14 18:11:33 -08:00
if (p) {
*dest = p;
dest = &p->nextsibling;
str = p->end;
continue;
}
}
// This isn't in the for() structure because there needs to
// be at least once chance to match the pattern, even if
// we're at the end of the string already (e.g. "..$").
2020-09-25 08:41:29 -07:00
if (str < f->end && (op->multiline || *str != '\n'))
str = next_char(f, str);
}
2020-09-16 19:41:05 -07:00
destroy_match(&m);
return NULL;
2020-09-11 01:28:06 -07:00
}
m->end = str;
return m;
2020-09-11 01:28:06 -07:00
}
case VM_REPEAT: {
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->end = str;
m->op = op;
match_t **dest = &m->child;
size_t reps = 0;
ssize_t max = op->args.repetitions.max;
for (reps = 0; max == -1 || reps < (size_t)max; ++reps) {
const char *start = str;
2020-09-11 01:28:06 -07:00
// Separator
match_t *sep = NULL;
if (op->args.repetitions.sep != NULL && reps > 0) {
2020-09-16 19:35:43 -07:00
sep = _match(g, f, str, op->args.repetitions.sep, flags, rec);
2020-09-11 01:28:06 -07:00
if (sep == NULL) break;
str = sep->end;
}
2020-09-16 19:35:43 -07:00
match_t *p = _match(g, f, str, op->args.repetitions.repeat_pat, flags, rec);
if (p == NULL) {
destroy_match(&sep);
break;
}
if (p->end == start && reps > 0) {
// Since no forward progress was made on either `pat` or
// `sep` and BPEG does not have mutable state, it's
// guaranteed that no progress will be made on the next
// loop either. We know that this will continue to loop
// until reps==max, so let's just cut to the chase instead
// of looping infinitely.
2020-09-11 01:28:06 -07:00
destroy_match(&sep);
destroy_match(&p);
if (op->args.repetitions.max == -1)
reps = ~(size_t)0;
else
reps = (size_t)op->args.repetitions.max;
2020-09-11 01:28:06 -07:00
break;
}
if (sep) {
*dest = sep;
dest = &sep->nextsibling;
}
*dest = p;
dest = &p->nextsibling;
str = p->end;
}
if (reps < (size_t)op->args.repetitions.min) {
2020-09-11 01:28:06 -07:00
destroy_match(&m);
return NULL;
}
m->end = str;
return m;
}
case VM_AFTER: {
ssize_t backtrack = op->args.pat->len;
check(backtrack != -1, "'<' is only allowed for fixed-length operations");
2020-09-16 19:35:43 -07:00
if (str - backtrack < f->contents) return NULL;
match_t *before = _match(g, f, str - backtrack, op->args.pat, flags, rec);
2020-09-11 01:28:06 -07:00
if (before == NULL) return NULL;
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->end = str;
m->op = op;
2020-09-13 16:19:37 -07:00
m->child = before;
2020-09-11 01:28:06 -07:00
return m;
}
case VM_BEFORE: {
2020-09-16 19:35:43 -07:00
match_t *after = _match(g, f, str, op->args.pat, flags, rec);
2020-09-11 01:28:06 -07:00
if (after == NULL) return NULL;
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->end = str;
m->op = op;
2020-09-13 16:19:37 -07:00
m->child = after;
2020-09-11 01:28:06 -07:00
return m;
}
case VM_CAPTURE: {
2020-09-16 19:35:43 -07:00
match_t *p = _match(g, f, str, op->args.pat, flags, rec);
2020-09-11 01:28:06 -07:00
if (p == NULL) return NULL;
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->end = p->end;
m->op = op;
m->child = p;
return m;
}
case VM_HIDE: {
match_t *p = _match(g, f, str, op->args.pat, flags, rec);
if (p == NULL) return NULL;
match_t *m = new(match_t);
m->start = str;
m->end = p->end;
m->op = op;
m->child = p;
return m;
}
2020-09-11 01:28:06 -07:00
case VM_OTHERWISE: {
2020-09-16 19:35:43 -07:00
match_t *m = _match(g, f, str, op->args.multiple.first, flags, rec);
if (m == NULL) m = _match(g, f, str, op->args.multiple.second, flags, rec);
2020-09-11 01:28:06 -07:00
return m;
}
case VM_CHAIN: {
2020-09-16 19:35:43 -07:00
match_t *m1 = _match(g, f, str, op->args.multiple.first, flags, rec);
2020-09-11 01:28:06 -07:00
if (m1 == NULL) return NULL;
2020-09-12 18:20:13 -07:00
size_t nbackrefs = push_backrefs(g, m1);
2020-09-16 19:35:43 -07:00
match_t *m2 = _match(g, f, m1->end, op->args.multiple.second, flags, rec);
2020-09-12 18:20:13 -07:00
pop_backrefs(g, nbackrefs);
2020-09-11 01:28:06 -07:00
if (m2 == NULL) {
destroy_match(&m1);
return NULL;
}
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->end = m2->end;
m->op = op;
m->child = m1;
m1->nextsibling = m2;
return m;
}
2020-09-28 17:56:02 -07:00
case VM_EQUAL: case VM_NOT_EQUAL: {
2020-09-16 19:35:43 -07:00
match_t *m1 = _match(g, f, str, op->args.multiple.first, flags, rec);
2020-09-13 20:33:11 -07:00
if (m1 == NULL) return NULL;
// <p1>==<p2> matches iff the text of <p1> matches <p2>
// <p1>!=<p2> matches iff the text of <p1> does not match <p2>
file_t inner = {
.filename=f->filename,
.contents=(char*)m1->start, .end=(char*)m1->end,
.lines=f->lines, // I think this works, but am not 100% sure
.nlines=1 + get_line_number(f, m1->end)-get_line_number(f, m1->start),
.mmapped=f->mmapped,
};
match_t *m2 = _match(g, &inner, str, op->args.multiple.second, flags, rec);
if ((m2 == NULL) == (op->op == VM_EQUAL)) {
2020-09-13 20:33:11 -07:00
destroy_match(&m1);
destroy_match(&m2);
return NULL;
}
match_t *m = new(match_t);
m->start = m1->start;
2020-09-28 17:56:02 -07:00
m->end = m1->end;
2020-09-13 20:33:11 -07:00
m->op = op;
m->child = m1;
2020-09-28 17:56:02 -07:00
if (op->op == VM_EQUAL) {
m1->nextsibling = m2;
} else {
destroy_match(&m2);
}
2020-09-13 20:33:11 -07:00
return m;
}
2020-09-11 01:28:06 -07:00
case VM_REPLACE: {
2020-09-17 01:00:06 -07:00
match_t *p = NULL;
if (op->args.replace.pat) {
p = _match(g, f, str, op->args.replace.pat, flags, rec);
2020-09-17 01:00:06 -07:00
if (p == NULL) return NULL;
}
match_t *m = new(match_t);
2020-09-11 01:28:06 -07:00
m->start = str;
m->op = op;
2020-09-17 01:00:06 -07:00
if (p) {
2020-09-11 01:28:06 -07:00
m->child = p;
m->end = p->end;
} else {
m->end = m->start;
}
return m;
}
case VM_REF: {
2020-09-11 02:55:15 -07:00
vm_op_t *r = lookup(g, op->args.s);
check(r != NULL, "Unknown identifier: '%s'", op->args.s);
2020-09-13 00:48:39 -07:00
// Prevent infinite left recursion:
for (recursive_ref_t *p = rec; p; p = p->prev) {
if (p->pos == str && p->op == r) {
++p->hit;
return p->result;
}
}
recursive_ref_t wrap = {
.op = r,
.pos = str,
.prev = rec,
.hit = 0,
.result = NULL,
};
match_t *best = NULL;
left_recursive:;
2020-09-16 19:35:43 -07:00
match_t *p = _match(g, f, str, r, flags, &wrap);
2020-09-13 00:48:39 -07:00
if (p == NULL) return best;
if (wrap.hit && (best == NULL || p->end > best->end)) {
best = p;
wrap.hit = 0;
wrap.result = p;
goto left_recursive;
} else if (best == NULL) {
best = p;
}
match_t *m = new(match_t);
2020-09-13 00:48:39 -07:00
m->start = best->start;
m->end = best->end;
2020-09-11 02:55:15 -07:00
m->op = op;
2020-09-13 00:48:39 -07:00
m->child = best;
2020-09-11 02:55:15 -07:00
return m;
2020-09-11 01:28:06 -07:00
}
2020-09-12 18:20:13 -07:00
case VM_BACKREF: {
2020-09-14 12:16:15 -07:00
return match_backref(str, op, (match_t*)op->args.backref, flags);
2020-09-12 18:20:13 -07:00
}
2020-09-14 01:21:49 -07:00
case VM_NODENT: {
2020-10-13 17:12:37 -07:00
if (*str != '\n') return NULL;
const char *start = str;
2020-09-16 19:35:43 -07:00
size_t linenum = get_line_number(f, str);
2020-10-13 17:12:37 -07:00
const char *p = get_line(f, linenum);
2020-12-14 22:18:04 -08:00
if (p < f->contents) p=f->contents; // Can happen with recursive matching
2020-10-13 17:12:37 -07:00
// Current indentation:
2020-09-14 01:21:49 -07:00
char denter = *p;
int dents = 0;
if (denter == ' ' || denter == '\t') {
2020-12-14 22:18:04 -08:00
for (; *p == denter && p < f->end; ++p) ++dents;
2020-09-14 01:21:49 -07:00
}
2020-10-13 17:12:37 -07:00
// Subsequent indentation:
while (*str == '\n') ++str;
2020-09-14 01:21:49 -07:00
for (int i = 0; i < dents; i++) {
2020-12-14 22:18:04 -08:00
if (str[i] != denter || &str[i] >= f->end) return NULL;
2020-09-14 01:21:49 -07:00
}
match_t *m = new(match_t);
2020-10-13 17:12:37 -07:00
m->start = start;
2020-09-14 01:21:49 -07:00
m->end = &str[dents];
m->op = op;
return m;
}
2020-09-11 01:28:06 -07:00
default: {
fprintf(stderr, "Unknown opcode: %d", op->op);
_exit(1);
return NULL;
}
}
}
/*
* Get a specific numbered pattern capture.
*/
static match_t *get_capture_n(match_t *m, int *n)
{
if (!m) return NULL;
if (*n == 0) return m;
if (m->op->op == VM_CAPTURE && *n == 1) return m;
if (m->op->op == VM_CAPTURE) --(*n);
2020-09-11 01:28:06 -07:00
for (match_t *c = m->child; c; c = c->nextsibling) {
match_t *cap = get_capture_n(c, n);
if (cap) return cap;
}
return NULL;
}
/*
* Get a named capture.
*/
static match_t *get_capture_named(match_t *m, const char *name)
{
if (m->op->op == VM_CAPTURE && m->op->args.capture.name
&& streq(m->op->args.capture.name, name))
2020-09-11 01:28:06 -07:00
return m;
for (match_t *c = m->child; c; c = c->nextsibling) {
match_t *cap = get_capture_named(c, name);
if (cap) return cap;
}
return NULL;
}
static match_t *get_cap(match_t *m, const char **r)
{
if (isdigit(**r)) {
int n = (int)strtol(*r, (char**)r, 10);
return get_capture_n(m->child, &n);
} else {
const char *end = after_name(*r);
if (end == *r) return NULL;
char *name = strndup(*r, (size_t)(end-*r));
match_t *cap = get_capture_named(m, name);
free(name);
*r = end;
2020-09-28 22:22:05 -07:00
if (**r == ';') ++(*r);
return cap;
}
return NULL;
}
typedef struct {
size_t line, printed_line;
const char *color;
} print_state_t;
static void print_line_number(print_state_t *state, print_options_t options)
{
state->printed_line = state->line;
if (!(options & PRINT_LINE_NUMBERS)) return;
if (options & PRINT_COLOR)
printf("\033[0;2m% 5ld\033(0\x78\033(B%s", state->line, state->color);
else
printf("% 5ld|", state->line);
}
2020-09-11 01:28:06 -07:00
/*
* Print a match with replacements and highlighting.
*/
static void _print_match(file_t *f, match_t *m, print_state_t *state, print_options_t options)
2020-09-11 01:28:06 -07:00
{
static const char *hl = "\033[0;31;1m";
const char *old_color = state->color;
if (m->op->op == VM_HIDE) {
// TODO: handle replacements?
for (const char *p = m->start; p < m->end; p++) {
if (*p == '\n') ++state->line;
}
} else if (m->op->op == VM_REPLACE) {
if (options & PRINT_COLOR && state->color != hl) {
state->color = hl;
printf("%s", state->color);
}
const char *text = m->op->args.replace.text;
const char *end = &text[m->op->args.replace.len];
for (const char *r = text; r < end; ) {
if (*r == '@' && r[1] && r[1] != '@') {
++r;
match_t *cap = get_cap(m, &r);
if (cap != NULL) {
_print_match(f, cap, state, options);
continue;
} else {
--r;
}
2020-09-11 01:28:06 -07:00
}
if (state->printed_line != state->line)
print_line_number(state, options);
if (*r == '\\') {
++r;
unsigned char c = unescapechar(r, &r);
fputc(c, stdout);
if (c == '\n') ++state->line;
continue;
} else if (*r == '\n') {
fputc('\n', stdout);
++state->line;
++r;
continue;
} else {
fputc(*r, stdout);
++r;
continue;
}
2020-09-11 01:28:06 -07:00
}
} else {
if (m->op->op == VM_CAPTURE) {
if (options & PRINT_COLOR && state->color != hl) {
state->color = hl;
printf("%s", state->color);
}
}
2020-09-11 01:28:06 -07:00
const char *prev = m->start;
for (match_t *child = m->child; child; child = child->nextsibling) {
2020-09-13 16:19:37 -07:00
// Skip children from e.g. zero-width matches like >@foo
2020-09-13 20:33:11 -07:00
if (!(prev <= child->start && child->start <= m->end &&
prev <= child->end && child->end <= m->end))
2020-09-13 16:19:37 -07:00
continue;
if (child->start > prev) {
for (const char *p = prev; p < child->start; ++p) {
if (state->printed_line != state->line)
print_line_number(state, options);
fputc(*p, stdout);
if (*p == '\n') ++state->line;
}
}
_print_match(f, child, state, options);
2020-09-11 01:28:06 -07:00
prev = child->end;
}
if (m->end > prev) {
for (const char *p = prev; p < m->end; ++p) {
if (state->printed_line != state->line)
print_line_number(state, options);
fputc(*p, stdout);
if (*p == '\n') ++state->line;
}
}
}
if (options & PRINT_COLOR && old_color != state->color) {
printf("%s", old_color);
state->color = old_color;
2020-09-11 01:28:06 -07:00
}
}
void print_match(file_t *f, match_t *m, print_options_t options)
{
print_state_t state = {.line = 1, .color = "\033[0m"};
_print_match(f, m, &state, options);
}
2020-09-14 12:16:15 -07:00
static match_t *match_backref(const char *str, vm_op_t *op, match_t *cap, unsigned int flags)
2020-09-12 18:20:13 -07:00
{
check(op->op == VM_BACKREF, "Attempt to match backref against something that's not a backref");
match_t *ret = new(match_t);
2020-09-12 18:20:13 -07:00
ret->start = str;
ret->op = op;
match_t **dest = &ret->child;
if (cap->op->op == VM_REPLACE) {
const char *text = cap->op->args.replace.text;
const char *end = &text[cap->op->args.replace.len];
for (const char *r = text; r < end; ) {
2020-09-12 18:20:13 -07:00
if (*r == '\\') {
++r;
if (*(str++) != unescapechar(r, &r)) {
destroy_match(&ret);
return NULL;
}
} else if (*r != '@') {
if (*(str++) != *r) {
destroy_match(&ret);
return NULL;
}
++r;
continue;
}
++r;
match_t *cap = NULL;
switch (*r) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
int n = (int)strtol(r, (char**)&r, 10);
cap = get_capture_n(cap->child, &n);
break;
}
default: {
const char *end = after_name(r);
if (end == r) {
2020-09-12 18:20:13 -07:00
destroy_match(&ret);
return NULL;
}
char *name = strndup(r, (size_t)(end-r));
cap = get_capture_named(cap, name);
free(name);
r = end;
if (*r == ';') ++r;
2020-09-12 18:20:13 -07:00
break;
}
}
if (cap != NULL) {
2020-09-14 12:16:15 -07:00
*dest = match_backref(str, op, cap, flags);
2020-09-12 18:20:13 -07:00
if (*dest == NULL) {
destroy_match(&ret);
return NULL;
}
str = (*dest)->end;
dest = &(*dest)->nextsibling;
}
}
} else {
const char *prev = cap->start;
for (match_t *child = cap->child; child; child = child->nextsibling) {
if (child->start > prev) {
size_t len = (size_t)(child->start - prev);
if ((flags & BPEG_IGNORECASE) ? memicmp(str, prev, len) != 0
: memcmp(str, prev, len) != 0) {
2020-09-12 18:20:13 -07:00
destroy_match(&ret);
return NULL;
}
str += len;
prev = child->start;
}
2020-09-13 20:33:11 -07:00
if (child->start < prev) continue;
2020-09-14 12:16:15 -07:00
*dest = match_backref(str, op, child, flags);
2020-09-12 18:20:13 -07:00
if (*dest == NULL) {
destroy_match(&ret);
return NULL;
}
str = (*dest)->end;
dest = &(*dest)->nextsibling;
prev = child->end;
}
if (cap->end > prev) {
size_t len = (size_t)(cap->end - prev);
if ((flags & BPEG_IGNORECASE) ? memicmp(str, prev, len) != 0
: memcmp(str, prev, len) != 0) {
2020-09-12 18:20:13 -07:00
destroy_match(&ret);
return NULL;
}
str += len;
}
}
ret->end = str;
return ret;
}
2020-09-16 19:35:43 -07:00
match_t *match(grammar_t *g, file_t *f, const char *str, vm_op_t *op, unsigned int flags)
2020-09-13 00:48:39 -07:00
{
2020-09-16 19:35:43 -07:00
return _match(g, f, str, op, flags, NULL);
2020-09-13 00:48:39 -07:00
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1