More robust character class parser.

This commit is contained in:
Tor Andersson
2014-02-21 19:46:12 +01:00
parent d68a6f3424
commit 3585abad41
+127 -77
View File
@@ -36,7 +36,7 @@ struct cstate {
int lookahead;
Rune yychar;
Reclass *yycclass;
Reclass *yycc;
int yymin, yymax;
const char *error;
@@ -72,39 +72,6 @@ enum {
L_COUNT, /* {M,N} */
};
static Reclass class_d = {
class_d.spans + 2, {
'0', '9',
}
};
static Reclass class_s = {
class_s.spans + 12, {
0x9, 0x9,
0xA, 0xD,
0x20, 0x20,
0xA0, 0xA0,
0x2028, 0x2029,
0xFEFF, 0xFEFF,
}
};
static Reclass class_w = {
class_w.spans + 8, {
'0', '9',
'A', 'Z',
'_', '_',
'a', 'z',
}
};
static Reclass *newclass(struct cstate *g)
{
if (g->ncclass >= nelem(g->prog->cclass))
die(g, "too many character classes");
return &g->prog->cclass[g->ncclass++];
}
static int hex(struct cstate *g, int c)
{
if (c >= '0' && c <= '9') return c - '0';
@@ -128,7 +95,6 @@ static int nextrune(struct cstate *g)
g->source += chartorune(&g->yychar, g->source);
switch (g->yychar) {
case 0: die(g, "unterminated escape sequence");
// case 'b': g->yychar = '\b'; return 0;
case 'f': g->yychar = '\f'; return 0;
case 'n': g->yychar = '\n'; return 0;
case 'r': g->yychar = '\r'; return 0;
@@ -187,16 +153,80 @@ static int lexcount(struct cstate *g)
return L_COUNT;
}
static void newcclass(struct cstate *g)
{
if (g->ncclass >= nelem(g->prog->cclass))
die(g, "too many character classes");
g->yycc = g->prog->cclass + g->ncclass++;
g->yycc->end = g->yycc->spans;
}
static void addrange(struct cstate *g, Rune a, Rune b)
{
if (a > b)
die(g, "invalid character class range");
if (g->yycc->end + 2 == g->yycc->spans + nelem(g->yycc->spans))
die(g, "too many character class ranges");
*g->yycc->end++ = a;
*g->yycc->end++ = b;
}
static void addranges_d(struct cstate *g)
{
addrange(g, '0', '9');
}
static void addranges_D(struct cstate *g)
{
addrange(g, 0, '0'-1);
addrange(g, '9'+1, 0xFFFF);
}
static void addranges_s(struct cstate *g)
{
addrange(g, 0x9, 0x9);
addrange(g, 0xA, 0xD);
addrange(g, 0x20, 0x20);
addrange(g, 0xA0, 0xA0);
addrange(g, 0x2028, 0x2029);
addrange(g, 0xFEFF, 0xFEFF);
}
static void addranges_S(struct cstate *g)
{
addrange(g, 0, 0x9-1);
addrange(g, 0x9+1, 0xA-1);
addrange(g, 0xD+1, 0x20-1);
addrange(g, 0x20+1, 0xA0-1);
addrange(g, 0xA0+1, 0x2028-1);
addrange(g, 0x2029+1, 0xFEFF-1);
addrange(g, 0xFEFF+1, 0xFFFF);
}
static void addranges_w(struct cstate *g)
{
addrange(g, '0', '9');
addrange(g, 'A', 'Z');
addrange(g, '_', '_');
addrange(g, 'a', 'z');
}
static void addranges_W(struct cstate *g)
{
addrange(g, 0, '0'-1);
addrange(g, '9'+1, 'A'-1);
addrange(g, 'Z'+1, '_'-1);
addrange(g, '_'+1, 'a'-1);
addrange(g, 'z'+1, 0xFFFF);
}
static int lexclass(struct cstate *g)
{
int type = L_CCLASS;
int quoted;
Rune *p, *ep;
int quoted, havesave, havedash;
Rune save;
g->yycclass = newclass(g);
p = g->yycclass->spans;
ep = p + nelem(g->yycclass->spans);
newcclass(g);
quoted = nextrune(g);
if (!quoted && g->yychar == '^') {
@@ -204,45 +234,65 @@ static int lexclass(struct cstate *g)
quoted = nextrune(g);
}
while (p < ep) {
havesave = havedash = 0;
for (;;) {
if (g->yychar == 0)
die(g, "unterminated character class");
if (!quoted && g->yychar == ']')
break;
save = g->yychar;
quoted = nextrune(g);
// TODO: \d \D \s \S \w \W
if (!quoted && g->yychar == '-') {
quoted = nextrune(g);
if (g->yychar == 0)
die(g, "unterminated character class");
if (!quoted && g->yychar == ']') {
*p++ = save;
*p++ = save;
if (p == ep)
die(g, "too many character classes");
*p++ = '-';
*p++ = '-';
break;
if (havesave) {
if (havedash) {
addrange(g, save, '-');
havesave = havedash = 0;
} else {
havedash = 1;
}
} else {
save = '-';
havesave = 1;
}
if (g->yychar < save)
die(g, "invalid character class range");
*p++ = save;
*p++ = g->yychar;
quoted = nextrune(g);
} else if (quoted && strchr("DSWdsw", g->yychar)) {
if (havesave) {
addrange(g, save, save);
if (havedash)
addrange(g, '-', '-');
}
switch (g->yychar) {
case 'd': addranges_d(g); break;
case 's': addranges_s(g); break;
case 'w': addranges_w(g); break;
case 'D': addranges_D(g); break;
case 'S': addranges_S(g); break;
case 'W': addranges_W(g); break;
}
havesave = havedash = 0;
} else {
*p++ = save;
*p++ = save;
if (quoted && g->yychar == 'b')
g->yychar = '\b';
if (havesave) {
if (havedash) {
addrange(g, save, g->yychar);
havesave = havedash = 0;
} else {
addrange(g, save, save);
save = g->yychar;
}
} else {
save = g->yychar;
havesave = 1;
}
}
}
if (p == ep)
die(g, "too many character classes");
g->yycclass->end = p;
quoted = nextrune(g);
}
if (havesave) {
addrange(g, save, save);
if (havedash)
addrange(g, '-', '-');
}
return type;
}
@@ -254,12 +304,12 @@ static int lex(struct cstate *g)
switch (g->yychar) {
case 'b': return L_WORD;
case 'B': return L_NWORD;
case 'd': g->yycclass = &class_d; return L_CCLASS;
case 's': g->yycclass = &class_s; return L_CCLASS;
case 'w': g->yycclass = &class_w; return L_CCLASS;
case 'D': g->yycclass = &class_d; return L_NCCLASS;
case 'S': g->yycclass = &class_s; return L_NCCLASS;
case 'W': g->yycclass = &class_w; return L_NCCLASS;
case 'd': newcclass(g); addranges_d(g); return L_CCLASS;
case 's': newcclass(g); addranges_s(g); return L_CCLASS;
case 'w': newcclass(g); addranges_w(g); return L_CCLASS;
case 'D': newcclass(g); addranges_d(g); return L_NCCLASS;
case 'S': newcclass(g); addranges_s(g); return L_NCCLASS;
case 'W': newcclass(g); addranges_w(g); return L_NCCLASS;
}
if (g->yychar >= '0' && g->yychar <= '9') {
g->yychar -= '0';
@@ -384,13 +434,13 @@ static Renode *parseatom(struct cstate *g)
}
if (g->lookahead == L_CCLASS) {
atom = newnode(g, P_CCLASS);
atom->cc = g->yycclass;
atom->cc = g->yycc;
next(g);
return atom;
}
if (g->lookahead == L_NCCLASS) {
atom = newnode(g, P_NCCLASS);
atom->cc = g->yycclass;
atom->cc = g->yycc;
next(g);
return atom;
}