extmod/re1.5: Update to 0.8.

Contains implementation of ?: (non-capturing groups), ?? (non-greedy ?),
as well as much improved robustness, and edge cases and error handling by
Amir Plivatsky (@ampli).
sys-override
Paul Sokolovsky 2015-11-01 00:37:44 +03:00
parent 000a12783c
commit 7cce2f664c
2 changed files with 95 additions and 139 deletions

View File

@ -4,203 +4,175 @@
#include "re1.5.h" #include "re1.5.h"
static void insert_code(char *code, int at, int num, int *pc) #define INSERT_CODE(at, num, pc) \
{ ((code ? memmove(code + at + num, code + at, pc - at) : (void)0), pc += num)
memmove(code + at + num, code + at, *pc - at);
*pc += num;
}
#define REL(at, to) (to - at - 2) #define REL(at, to) (to - at - 2)
#define EMIT(at, byte) (code ? (code[at] = byte) : (void)(at))
#define PC (prog->bytelen)
int re1_5_sizecode(const char *re) static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
{ {
int pc = 5 + NON_ANCHORED_PREFIX; // Save 0, Save 1, Match; more bytes for "search" (vs "match") prefix code char *code = sizecode ? NULL : prog->insts;
int start = PC;
for (; *re; re++) { int term = PC;
switch (*re) {
case '\\':
re++;
default:
pc += 2;
break;
case '+':
// Skip entire "+?"
if (re[1] == '?')
re++;
case '?':
pc += 2;
break;
case '.':
case '^':
case '$':
pc++;
break;
case '*':
// Skip entire "*?"
if (re[1] == '?')
re++;
case '|':
case '(':
pc += 4;
break;
case ')':
break;
case '[': {
pc += 2;
re++;
if (*re == '^') re++;
while (*re != ']') {
if (!*re) return -1;
if (re[1] == '-') {
re += 2;
}
pc += 2;
re++;
}
}
}
}
return pc;
}
#define EMIT(at, byte) code[at] = byte
static const char *_compilecode(const char *re, ByteProg *prog)
{
char *code = prog->insts;
int pc = prog->bytelen;
int start = pc;
int term = pc;
int alt_label = 0; int alt_label = 0;
for (; *re && *re != ')'; re++) { for (; *re && *re != ')'; re++) {
switch (*re) { switch (*re) {
case '\\': case '\\':
re++; re++;
if (!*re) return NULL; // Trailing backslash
if ((*re | 0x20) == 'd' || (*re | 0x20) == 's' || (*re | 0x20) == 'w') { if ((*re | 0x20) == 'd' || (*re | 0x20) == 's' || (*re | 0x20) == 'w') {
term = pc; term = PC;
EMIT(pc++, NamedClass); EMIT(PC++, NamedClass);
EMIT(pc++, *re); EMIT(PC++, *re);
prog->len++; prog->len++;
break; break;
} }
default: default:
term = pc; term = PC;
EMIT(pc++, Char); EMIT(PC++, Char);
EMIT(pc++, *re); EMIT(PC++, *re);
prog->len++; prog->len++;
break; break;
case '.': case '.':
term = pc; term = PC;
EMIT(pc++, Any); EMIT(PC++, Any);
prog->len++; prog->len++;
break; break;
case '[': { case '[': {
int cnt; int cnt;
term = pc; term = PC;
re++; re++;
if (*re == '^') { if (*re == '^') {
EMIT(pc++, ClassNot); EMIT(PC++, ClassNot);
re++; re++;
} else { } else {
EMIT(pc++, Class); EMIT(PC++, Class);
} }
pc++; // Skip # of pair byte PC++; // Skip # of pair byte
prog->len++; prog->len++;
for (cnt = 0; *re != ']'; re++, cnt++) { for (cnt = 0; *re != ']'; re++, cnt++) {
if (!*re) return NULL; if (!*re) return NULL;
EMIT(pc++, *re); EMIT(PC++, *re);
if (re[1] == '-') { if (re[1] == '-') {
re += 2; re += 2;
} }
EMIT(pc++, *re); EMIT(PC++, *re);
} }
EMIT(term + 1, cnt); EMIT(term + 1, cnt);
break; break;
} }
case '(': { case '(': {
term = pc; term = PC;
int sub = ++prog->sub; int sub;
int capture = re[1] != '?' || re[2] != ':';
EMIT(pc++, Save); if (capture) {
EMIT(pc++, 2 * sub); sub = ++prog->sub;
prog->len++; EMIT(PC++, Save);
EMIT(PC++, 2 * sub);
prog->len++;
} else {
re += 2;
}
prog->bytelen = pc; re = _compilecode(re + 1, prog, sizecode);
re = _compilecode(re + 1, prog);
if (re == NULL || *re != ')') return NULL; // error, or no matching paren if (re == NULL || *re != ')') return NULL; // error, or no matching paren
pc = prog->bytelen;
EMIT(pc++, Save); if (capture) {
EMIT(pc++, 2 * sub + 1); EMIT(PC++, Save);
prog->len++; EMIT(PC++, 2 * sub + 1);
prog->len++;
}
break; break;
} }
case '?': case '?':
if (pc == term) return NULL; // nothing to repeat if (PC == term) return NULL; // nothing to repeat
insert_code(code, term, 2, &pc); INSERT_CODE(term, 2, PC);
EMIT(term, Split);
EMIT(term + 1, REL(term, pc));
prog->len++;
break;
case '*':
if (pc == term) return NULL; // nothing to repeat
insert_code(code, term, 2, &pc);
EMIT(pc, Jmp);
EMIT(pc + 1, REL(pc, term));
pc += 2;
if (re[1] == '?') { if (re[1] == '?') {
EMIT(term, RSplit); EMIT(term, RSplit);
re++; re++;
} else { } else {
EMIT(term, Split); EMIT(term, Split);
} }
EMIT(term + 1, REL(term, pc)); EMIT(term + 1, REL(term, PC));
prog->len += 2; prog->len++;
term = PC;
break; break;
case '+': case '*':
if (pc == term) return NULL; // nothing to repeat if (PC == term) return NULL; // nothing to repeat
INSERT_CODE(term, 2, PC);
EMIT(PC, Jmp);
EMIT(PC + 1, REL(PC, term));
PC += 2;
if (re[1] == '?') { if (re[1] == '?') {
EMIT(pc, Split); EMIT(term, RSplit);
re++; re++;
} else { } else {
EMIT(pc, RSplit); EMIT(term, Split);
} }
EMIT(pc + 1, REL(pc, term)); EMIT(term + 1, REL(term, PC));
pc += 2; prog->len += 2;
term = PC;
break;
case '+':
if (PC == term) return NULL; // nothing to repeat
if (re[1] == '?') {
EMIT(PC, Split);
re++;
} else {
EMIT(PC, RSplit);
}
EMIT(PC + 1, REL(PC, term));
PC += 2;
prog->len++; prog->len++;
term = PC;
break; break;
case '|': case '|':
if (alt_label) { if (alt_label) {
EMIT(alt_label, REL(alt_label, pc) + 1); EMIT(alt_label, REL(alt_label, PC) + 1);
} }
insert_code(code, start, 2, &pc); INSERT_CODE(start, 2, PC);
EMIT(pc++, Jmp); EMIT(PC++, Jmp);
alt_label = pc++; alt_label = PC++;
EMIT(start, Split); EMIT(start, Split);
EMIT(start + 1, REL(start, pc)); EMIT(start + 1, REL(start, PC));
prog->len += 2; prog->len += 2;
term = PC;
break; break;
case '^': case '^':
EMIT(pc++, Bol); EMIT(PC++, Bol);
prog->len++; prog->len++;
term = PC;
break; break;
case '$': case '$':
EMIT(pc++, Eol); EMIT(PC++, Eol);
prog->len++; prog->len++;
term = PC;
break; break;
} }
} }
if (alt_label) { if (alt_label) {
EMIT(alt_label, REL(alt_label, pc) + 1); EMIT(alt_label, REL(alt_label, PC) + 1);
} }
prog->bytelen = pc;
return re; return re;
} }
int re1_5_sizecode(const char *re)
{
ByteProg dummyprog = {
// Save 0, Save 1, Match; more bytes for "search" (vs "match") prefix code
.bytelen = 5 + NON_ANCHORED_PREFIX
};
if (_compilecode(re, &dummyprog, /*sizecode*/1) == NULL) return -1;
return dummyprog.bytelen;
}
int re1_5_compilecode(ByteProg *prog, const char *re) int re1_5_compilecode(ByteProg *prog, const char *re)
{ {
prog->len = 0; prog->len = 0;
@ -221,7 +193,7 @@ int re1_5_compilecode(ByteProg *prog, const char *re)
prog->insts[prog->bytelen++] = 0; prog->insts[prog->bytelen++] = 0;
prog->len++; prog->len++;
re = _compilecode(re, prog); re = _compilecode(re, prog, /*sizecode*/0);
if (re == NULL || *re) return 1; if (re == NULL || *re) return 1;
prog->insts[prog->bytelen++] = Save; prog->insts[prog->bytelen++] = Save;
@ -234,25 +206,6 @@ int re1_5_compilecode(ByteProg *prog, const char *re)
return 0; return 0;
} }
void
cleanmarks(ByteProg *prog)
{
char *pc = prog->insts;
char *end = pc + prog->bytelen;
while (pc < end) {
*pc &= 0x7f;
switch (*pc) {
case Jmp:
case Split:
case RSplit:
case Save:
case Char:
pc++;
}
pc++;
}
}
#if 0 #if 0
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {

View File

@ -44,6 +44,9 @@ void re1_5_dumpcode(ByteProg *prog)
printf("\n"); printf("\n");
break; break;
} }
case NamedClass:
printf("namedclass %c\n", code[pc++]);
break;
case Match: case Match:
printf("match\n"); printf("match\n");
break; break;