tttm

git clone https://orangeshoelaces.net/git/tttm.git

c5ac580b4c59f623a05279afbf780edb72d39ff9

Author: Vasily Kolobkov on 05/03/2016

Committer: Vasily Kolobkov on 05/03/2016

Add parser shades - parser testbed

Plus next round of refinement and cleanup

Stats

parse.c   | 917 ++++++--
parse.h   | 117 +-
pshades.c | 129 +
3 files changed, 926 insertions(+), 237 deletions(-)

Patch

diff --git a/parse.c b/parse.c
index abc1ac6..801ff23 100644
--- a/parse.c
+++ b/parse.c
@@ -4,52 +4,131 @@
    specific error code or 0 is dispensed to the parse context.
 */
 
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
 #include <strings.h>
 
 #include "parse.h"
 
+#define LEN(a) (sizeof(a) / sizeof(a)[0])
+
 static struct literal {
-	enum { OK, NO, BAD, SP } kind;
-	char  *val;
-	size_t len;
+	int    val;
+	char  *srep;
+	size_t slen;
 } literals[] = {
-	{ ALERT, "ALERT", 5 },
-	{ ANSWERED, "\\Answered", 9 },
-	{ ANYFL, "\\*", 2 },
-	{ ASTERISK, "*", 1 },
-	{ AUTHEQ, "AUTH=", 5 },
-	{ BAD, "BAD", 3 },
-	{ BADCS, "BADCHARSET", 10 },
-	{ CAP, "CAPABILITY", 10 },
-	{ CBRACK, "]", 1 },
-	{ CPAR, ")", 1 },
-	{ DELETED, "\\Deleted", 8 },
-	{ DQUOTE, "\"", 1 },
-	{ DRAFT, "\\Draft", 6 },
-	{ EOL, "\r\n", 2 },
-	{ FLAGGED, "\\Flagged", 8 },
-	{ FLAGS, "FLAGS", 5 },
-	{ LIST, "LIST", 4 },
-	{ NIL, "NIL", 3 },
-	{ NO, "NO", 2 },
-	{ OBRACK, "[", 1 },
-	{ OK, "OK", 2 },
-	{ OPAR, "(", 1 },
-	{ PARSE, "PARSE", 5 },
-	{ PERMFL, "PERMANENTFLAGS", 14 },
-	{ PLUS, "+", 1 },
-	{ RO, "READ-ONLY", 9 },
-	{ RW, "READ-WRITE", 10 },
-	{ SEEN, "\\Seen", 5 },
-	{ SP, " ", 1 },
-	{ TRYC, "TRYCREATE", 9 },
-	{ UIDNEXT, "UIDNEXT", 7 },
-	{ UIDVAL, "UIDVALIDITY", 11 },
-	{ UNSEEN, "UNSEEN", 6 },
+	{ IL_7BIT,              "7BIT",              4  },
+	{ IL_8BIT,              "8BIT",              4  },
+	{ IL_ALERT,             "ALERT",             5  },
+	{ IL_APP,               "APPLICATION",       11 },
+	{ IL_APR,               "Apr",               3  },
+	{ IL_ASTERISK,          "*",                 1  },
+	{ IL_AUDIO,             "AUDIO",             5  },
+	{ IL_AUG,               "Aug",               3  },
+	{ IL_AUTHEQ,            "AUTH=",             5  },
+	{ IL_B64,               "BASE64",            6  },
+	{ IL_BAD,               "BAD",               3  },
+	{ IL_BADCS,             "BADCHARSET",        10 },
+	{ IL_BIN,               "BINARY",            6  },
+	{ IL_BODY,              "BODY",              4  },
+	{ IL_BSLASH,            "\\",                1  },
+	{ IL_BYE,               "BYE",               3  },
+	{ IL_CAP,               "CAPABILITY",        10 },
+	{ IL_CBRACE,            "}",                 1  },
+	{ IL_CBRACK,            "]",                 1  },
+	{ IL_COLON,             ":",                 1  },
+	{ IL_CPAR,              ")",                 1  },
+	{ IL_DEC,               "Dec",               3  },
+	{ IL_DHDR,              ".HEADER",           7  },
+	{ IL_DNOT,              ".NOT",              4  },
+	{ IL_DOT,               ".",                 1  },
+	{ IL_DQUOTE,            "\"",                1  },
+	{ IL_DTXT,              ".TEXT",             5  },
+	{ IL_ENVELOPE,          "ENVELOPE",          8  },
+	{ IL_EOL,               "\r\n",              2  },
+	{ IL_EXISTS,            "EXISTS",            6  },
+	{ IL_EXPUNGE,           "EXPUNGE",           7  },
+	{ IL_FEB,               "Feb",               3  },
+	{ IL_FETCH,             "FETCH",             5  },
+	{ IL_FLAGS,             "FLAGS",             5  },
+	{ IL_FLANSWERED,        "\\Answered",        9  },
+	{ IL_FLANY,             "\\*",               2  },
+	{ IL_FLDELETED,         "\\Deleted",         8  },
+	{ IL_FLDRAFT,           "\\Draft",           6  },
+	{ IL_FLFLAGGED,         "\\Flagged",         8  },
+	{ IL_FLMARKED,          "\\Marked",          7  },
+	{ IL_FLNOINFERIORS,     "\\Noinferiors",     12 },
+	{ IL_FLNOSEL,           "\\Noselect",        9  },
+	{ IL_FLRECENT,          "\\Recent",          7  },
+	{ IL_FLSEEN,            "\\Seen",            5  },
+	{ IL_FLUNMARKED,        "\\Unmarked",        9  },
+	{ IL_GT,                ">",                 1  },
+	{ IL_HDR,               "HEADER",            6  },
+	{ IL_HDRFIELDS,         "HEADER.FIELDS",     13 },
+	{ IL_IMG,               "IMAGE",             5  },
+	{ IL_INBOX,             "INBOX",             5  },
+	{ IL_INTDATE,           "INTERNALDATE",      12 },
+	{ IL_JAN,               "Jan",               3  },
+	{ IL_JUL,               "Jul",               3  },
+	{ IL_JUN,               "Jun",               3  },
+	{ IL_LIST,              "LIST",              4  },
+	{ IL_LSUB,              "LSUB",              4  },
+	{ IL_LT,                "<",                 1  },
+	{ IL_MAR,               "Mar",               3  },
+	{ IL_MAY,               "May",               3  },
+	{ IL_MIME,              "MIME",              4  },
+	{ IL_MINUS,             "-",                 1  },
+	{ IL_MSG,               "MESSAGE",           7  },
+	{ IL_MSGS,              "MESSAGES",          8  },
+	{ IL_NIL,               "NIL",               3  },
+	{ IL_NO,                "NO",                2  },
+	{ IL_NOV,               "Nov",               3  },
+	{ IL_OBRACE,            "{",                 1  },
+	{ IL_OBRACK,            "[",                 1  },
+	{ IL_OCT,               "Oct",               3  },
+	{ IL_OK,                "OK",                2  },
+	{ IL_OPAR,              "(",                 1  },
+	{ IL_PARSE,             "PARSE",             5  },
+	{ IL_PERMFL,            "PERMANENTFLAGS",    14 },
+	{ IL_PLUS,              "+",                 1  },
+	{ IL_QPRN,              "QUOTED-PRINTABLE",  16 },
+	{ IL_RECENT,            "RECENT",            6  },
+	{ IL_RFC822,            "RFC822",            6  },
+	{ IL_RFC822SIZE,        "RFC822.SIZE",       11 },
+	{ IL_RO,                "READ-ONLY",         9  },
+	{ IL_RW,                "READ-WRITE",        10 },
+	{ IL_SEARCH,            "SEARCH",            6  },
+	{ IL_SEP,               "Sep",               3  },
+	{ IL_SP,                " ",                 1  },
+	{ IL_STATUS,            "STATUS",            6  },
+	{ IL_STRUCT,            "STRUCTURE",         9  },
+	{ IL_TRYC,              "TRYCREATE",         9  },
+	{ IL_TXT,               "TEXT",              4  },
+	{ IL_UID,               "UID",               3  },
+	{ IL_UIDNEXT,           "UIDNEXT",           7  },
+	{ IL_UIDVAL,            "UIDVALIDITY",       11 },
+	{ IL_UNSEEN,            "UNSEEN",            6  },
+	{ IL_VIDEO,             "VIDEO",             5  },
 };
 
+static const char astr_specials[] = ""
+	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+	" \"%()*\\{\x7f";
+
+static const char atom_specials[] = ""
+	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+	" \"%()*\\]{\x7f";
+
+static const char tag_specials[] = ""
+	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+	" \"%()*+\\{\x7f";
+
 struct parse_cur {
-	const char   *tok;
+	const char *tok;
 	union parse_node *pt;
 };
 
@@ -67,14 +146,177 @@ struct parse_ctx {
 
 typedef int parsefn(struct parse_ctx *);
 
+int parse_respln(const char *, size_t, union parse_node *, size_t, size_t);
+
+static int cmpchr(const void *, const void *);
+static int contains(const char *, size_t, char);
+
+static int p_chk(struct parse_ctx *, struct parse_cur *);
+static int p_insint(struct parse_ctx *, int, size_t);
+static int p_inslit(struct parse_ctx *, int);
+static int p_insnum(struct parse_ctx *, uint32_t);
+static int p_insoblstr(struct parse_ctx *, uint32_t);
+static int p_insstr(struct parse_ctx *, int, const char *, uint32_t);
+static int p_beg(struct parse_ctx *, struct parse_cur *, int);
+static int p_end(struct parse_ctx *, struct parse_cur *);
+static int p_rwd(struct parse_ctx *, struct parse_cur *);
+static int p_opt(struct parse_ctx *);
+static int p_list(struct parse_ctx *, parsefn *);
+static int p_listsep(struct parse_ctx *, parsefn *, int);
+static int p_rep(struct parse_ctx *, parsefn *);
+static int p_repchr(struct parse_ctx *, const char *, size_t, int);
+static int p_2xcombo(struct parse_ctx *, int, parsefn *);
+static int p_3xcombo(struct parse_ctx *, int, int, parsefn *);
+static int p_r3xcombo(struct parse_ctx *, parsefn *, int, int);
+static int p_sss(struct parse_ctx *);
+
+static int p_addr(struct parse_ctx *);
+static int p_astr(struct parse_ctx *);
+static int p_atom(struct parse_ctx *);
+static int p_authtype(struct parse_ctx *);
+static int p_badcscode(struct parse_ctx *);
+static int p_badcsopt(struct parse_ctx *);
+static int p_base64(struct parse_ctx *);
+static int p_body(struct parse_ctx *);
+static int p_body1pt(struct parse_ctx *);
+static int p_bodybas(struct parse_ctx *);
+static int p_bodyext(struct parse_ctx *);
+static int p_bodyf(struct parse_ctx *);
+static int p_bodyfdes(struct parse_ctx *);
+static int p_bodyfdsp(struct parse_ctx *);
+static int p_bodyfenc(struct parse_ctx *);
+static int p_bodyfid(struct parse_ctx *);
+static int p_bodyflan(struct parse_ctx *);
+static int p_bodyflin(struct parse_ctx *);
+static int p_bodyfloc(struct parse_ctx *);
+static int p_bodyfmd5(struct parse_ctx *);
+static int p_bodyfoct(struct parse_ctx *);
+static int p_bodyfpar(struct parse_ctx *);
+static int p_bodympt(struct parse_ctx *);
+static int p_bodymsg(struct parse_ctx *);
+static int p_bodytxt(struct parse_ctx *);
+static int p_bodyx1pt(struct parse_ctx *);
+static int p_bodyxcmn(struct parse_ctx *);
+static int p_bodyxmpt(struct parse_ctx *);
+static int p_cap(struct parse_ctx *);
+static int p_capdata(struct parse_ctx *);
+static int p_contreq(struct parse_ctx *);
+static int p_datetime(struct parse_ctx *);
+static int p_dayfix(struct parse_ctx *);
+static int p_dig(struct parse_ctx *, size_t);
+static int p_dquotedchar(struct parse_ctx *);
+static int p_env(struct parse_ctx *);
+static int p_envaddr(struct parse_ctx *, int);
+static int p_envbcc(struct parse_ctx *);
+static int p_envcc(struct parse_ctx *);
+static int p_envdate(struct parse_ctx *);
+static int p_envfrom(struct parse_ctx *);
+static int p_envinrepto(struct parse_ctx *);
+static int p_envmsgid(struct parse_ctx *);
+static int p_envrepto(struct parse_ctx *);
+static int p_envsender(struct parse_ctx *);
+static int p_envsubj(struct parse_ctx *);
+static int p_envto(struct parse_ctx *);
+static int p_fetchrec(struct parse_ctx *);
+static int p_flag(struct parse_ctx *);
+static int p_flext(struct parse_ctx *);
+static int p_flfetch(struct parse_ctx *);
+static int p_flkeyword(struct parse_ctx *);
+static int p_gencode(struct parse_ctx *);
+static int p_genctext(struct parse_ctx *);
+static int p_hdrfname(struct parse_ctx *);
+static int p_hdrlist(struct parse_ctx *);
+static int p_lit(struct parse_ctx *, int);
+static int p_lstr(struct parse_ctx *);
+static int p_mailbox(struct parse_ctx *);
+static int p_matbody(struct parse_ctx *);
+static int p_matbsect(struct parse_ctx *);
+static int p_matenv(struct parse_ctx *);
+static int p_mathdrotxt(struct parse_ctx *);
+static int p_matintdate(struct parse_ctx *);
+static int p_matsize(struct parse_ctx *);
+static int p_matuid(struct parse_ctx *);
+static int p_mbdata(struct parse_ctx *);
+static int p_mbexists(struct parse_ctx *);
+static int p_mbflags(struct parse_ctx *);
+static int p_mblist(struct parse_ctx *);
+static int p_mblistdata(struct parse_ctx *);
+static int p_mblistfl(struct parse_ctx *);
+static int p_mblistofl(struct parse_ctx *);
+static int p_mblistosfl(struct parse_ctx *);
+static int p_mblistsfl(struct parse_ctx *);
+static int p_mblsub(struct parse_ctx *);
+static int p_mbrecent(struct parse_ctx *);
+static int p_mbsearch(struct parse_ctx *);
+static int p_mbstatus(struct parse_ctx *);
+static int p_medbas(struct parse_ctx *);
+static int p_medmsg(struct parse_ctx *);
+static int p_medsubt(struct parse_ctx *);
+static int p_medtxt(struct parse_ctx *);
+static int p_month(struct parse_ctx *);
+static int p_msgdata(struct parse_ctx *);
+static int p_msgatt(struct parse_ctx *);
+static int p_msgattdyn(struct parse_ctx *);
+static int p_msgattstat(struct parse_ctx *);
+static int p_nstr(struct parse_ctx *);
+static int p_num(struct parse_ctx *);
+static int p_nznum(struct parse_ctx *);
+static int p_permfl(struct parse_ctx *);
+static int p_permflcode(struct parse_ctx *);
+static int p_qchar(struct parse_ctx *);
+static int p_qchars(struct parse_ctx *);
+static int p_qstr(struct parse_ctx *);
+static int p_respbye(struct parse_ctx *);
+static int p_respdata(struct parse_ctx *);
+static int p_respln(struct parse_ctx *);
+static int p_respstate(struct parse_ctx *);
+static int p_resptext(struct parse_ctx *);
+static int p_resptextcode(struct parse_ctx *);
+static int p_secmsgtxt(struct parse_ctx *);
+static int p_secpt(struct parse_ctx *);
+static int p_secspec(struct parse_ctx *);
+static int p_section(struct parse_ctx *);
+static int p_sectxt(struct parse_ctx *);
+static int p_statt(struct parse_ctx *);
+static int p_stattrec(struct parse_ctx *);
+static int p_str(struct parse_ctx *);
+static int p_tag(struct parse_ctx *);
+static int p_taggedresp(struct parse_ctx *);
+static int p_text(struct parse_ctx *);
+static int p_time(struct parse_ctx *);
+static int p_uid(struct parse_ctx *);
+static int p_uidncode(struct parse_ctx *);
+static int p_uidvcode(struct parse_ctx *);
+static int p_unseencode(struct parse_ctx *);
+static int p_year(struct parse_ctx *);
+static int p_zone(struct parse_ctx *);
+
+static int p_cpar(struct parse_ctx *);
+static int p_dquote(struct parse_ctx *);
+static int p_opar(struct parse_ctx *);
+static int p_sp(struct parse_ctx *);
+
 int
 parse_respln(const char *tok, size_t toklen, union parse_node *pt,
     size_t ptlen, size_t oblth)
 {
-	struct parse_ctx p = { tok, tok + toklen, toklen, pt,
-	    pt + ptlen, ptlen, { tok, pt }, oblth, 0 };
+	struct parse_ctx p = { { tok, pt}, tok, tok + toklen, toklen,
+	    pt, pt + ptlen, ptlen, 0, oblth };
+
 	p_respln(&p);
-	return p->e;
+	return p.e;
+}
+
+int
+cmpchr(const void *a, const void *b)
+{
+	return *(char *)a - *(char *)b;
+}
+
+int
+contains(const char *s, size_t slen, char c)
+{
+	return bsearch(&c, s, slen, sizeof(char), cmpchr) != 0;
 }
 
 /*
@@ -101,6 +343,77 @@ p_insint(struct parse_ctx *p, int prod, size_t len)
 	cur->inter.type = PN_INTER;
 	cur->inter.prod = prod;
 	cur->inter.len = len;
+	p->e = PE_OK;
+	p->cur.pt++;
+	return 1;
+}
+
+int
+p_inslit(struct parse_ctx *p, int val)
+{
+	union parse_node *cur;
+
+	cur = p->cur.pt;
+	if (cur == p->ptend) {
+		p->e = PE_NOTENNODES;
+		return 0;
+	}
+	cur->lit.type = PN_LIT;
+	cur->lit.val = val;
+	p->e = PE_OK;
+	p->cur.pt++;
+	return 1;
+}
+
+int
+p_insnum(struct parse_ctx *p, uint32_t val)
+{
+	union parse_node *cur;
+
+	cur = p->cur.pt;
+	if (cur == p->ptend) {
+		p->e = PE_NOTENNODES;
+		return 0;
+	}
+	cur->lit.type = PN_NUM;
+	cur->lit.val = val;
+	p->e = PE_OK;
+	p->cur.pt++;
+	return 1;
+}
+
+int
+p_insoblstr(struct parse_ctx *p, uint32_t len)
+{
+	union parse_node *cur;
+
+	cur = p->cur.pt;
+	if (cur == p->ptend) {
+		p->e = PE_NOTENNODES;
+		return 0;
+	}
+	cur->str.type = PN_OBLSTR;
+	cur->str.len = len;
+	cur->str.iob = 0;
+	p->e = PE_OK;
+	p->cur.pt++;
+	return 1;
+}
+
+int
+p_insstr(struct parse_ctx *p, int type, const char *tok, uint32_t len)
+{
+	union parse_node *cur;
+
+	cur = p->cur.pt;
+	if (cur == p->ptend) {
+		p->e = PE_NOTENNODES;
+		return 0;
+	}
+	cur->str.type = type;
+	cur->str.len = len;
+	cur->str.tok = tok;
+	p->e = PE_OK;
 	p->cur.pt++;
 	return 1;
 }
@@ -108,13 +421,13 @@ p_insint(struct parse_ctx *p, int prod, size_t len)
 int
 p_beg(struct parse_ctx *p, struct parse_cur *beg, int prod)
 {
-	return p_chk(p, beg) && p_insint(p, type, 0);
+	return p_chk(p, beg) && p_insint(p, prod, 0);
 }
 
 int
 p_end(struct parse_ctx *p, struct parse_cur *beg)
 {
-	beg->pt->inter.len = p->cur.mark - beg->mark;
+	beg->pt->inter.len = p->cur.pt - beg->pt - 1;
 	p->e = PE_OK;
 	return 1;
 }
@@ -144,7 +457,7 @@ p_list(struct parse_ctx *p, parsefn *prod)
 {
 	int res;
 
-	for (res = prod(p); res && p_2xcombo(p, SP, prod);)
+	for (res = prod(p); res && p_2xcombo(p, IL_SP, prod);)
 		;
 	return res && p_opt(p);
 }
@@ -172,22 +485,24 @@ p_rep(struct parse_ctx *p, parsefn *prod)
 
 /* 1*<any char but those in `except'> */
 int
-p_repchr(struct parse_ctx *p, const char *except, int strtype)
+p_repchr(struct parse_ctx *p, const char *except, size_t elen, int strtype)
 {
-	char *t;
+	const char *t;
 	size_t len;
 
 	t = p->cur.tok;
-
-	while (t != p->tokend && binsearch(except, *t) == -1)
+	while (t != p->tokend && !contains(except, elen, *t))
 		t++;
 
 	len = t - p->cur.tok;
 	if (len == 0) {
 		p->e = PE_PARSE;
 		goto exit;
+	} else if (len > UINT32_MAX) {
+		p->e = PE_STRTOOBIG;
+		goto exit;
 	}
-	if (!p_insstr(p, strtype, p->cur.tok, len))
+	if (!p_insstr(p, strtype, p->cur.tok, (uint32_t)len))
 		goto exit;
 
 	p->cur.tok = t;
@@ -251,16 +566,18 @@ p_astr(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_beg(p, &b, PR_ASTRING) &&
-	    (p_repchr(p, astr_specials, PN_STR) || p_str(p)) &&
-	    p_end(p, &b) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_ASTR) &&
+	    (p_repchr(p, astr_specials, LEN(astr_specials), PN_STR) ||
+	    p_str(p)) && p_end(p, &b) || p_rwd(p, &b);
 }
 
 int
 p_atom(struct parse_ctx *p)
 {
-	return p_beg(p, &b, PR_ATOM) &&
-	    p_repchr(p, atom_specials, PN_STR) &&
+	struct parse_cur b;
+
+	return p_beg(p, &b, IP_ATOM) &&
+	    p_repchr(p, atom_specials, LEN(atom_specials), PN_STR) &&
 	    p_end(p, &b) || p_rwd(p, &b);
 }
 
@@ -275,7 +592,7 @@ p_badcscode(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, BADCS) &&
+	return p_chk(p, &b) && p_lit(p, IL_BADCS) &&
 	    (p_badcsopt(p) || p_opt(p)) || p_rwd(p, &b);
 }
 
@@ -285,7 +602,7 @@ p_badcsopt(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && p_sp(p) && p_opar(p) &&
-	    p_list(p, parse_astr) && p_cpar(p) || p_rwd(p, &b);
+	    p_list(p, p_astr) && p_cpar(p) || p_rwd(p, &b);
 }
 
 int
@@ -336,6 +653,8 @@ p_bodyext(struct parse_ctx *p)
 int
 p_bodyf(struct parse_ctx *p)
 {
+	struct parse_cur b;
+
 	return p_chk(p, &b) && p_bodyfpar(p) && p_sp(p) &&
 	    p_bodyfid(p) && p_sp(p) && p_bodyfdes(p) && p_sp(p) &&
 	    p_bodyfenc(p) && p_sp(p) && p_bodyfoct(p) || p_rwd(p, &b);
@@ -353,7 +672,7 @@ p_bodyfdsp(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && (p_opar(p) && p_str(p) && p_sp(p) &&
-	    p_bodyfpar(p) && p_cpar(p) || p_rwd(p, &b)) || p_lit(p, NIL);
+	    p_bodyfpar(p) && p_cpar(p) || p_rwd(p, &b)) || p_lit(p, IL_NIL);
 }
 
 int
@@ -361,9 +680,9 @@ p_bodyfenc(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && (p_dquote(p) && (p_lit(p, 7BIT) ||
-	    p_lit(p, 8BIT) || p_lit(p, BIN) || p_lit(p, B64) ||
-	    p_lit(p, QPRN)) && p_dquote(p) || p_rwd(p, &b)) ||
+	return p_chk(p, &b) && (p_dquote(p) && (p_lit(p, IL_7BIT) ||
+	    p_lit(p, IL_8BIT) || p_lit(p, IL_BIN) || p_lit(p, IL_B64) ||
+	    p_lit(p, IL_QPRN)) && p_dquote(p) || p_rwd(p, &b)) ||
 	    p_str(p);
 }
 
@@ -412,7 +731,7 @@ p_bodyfpar(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && (p_opar(p) && p_list(p, p_sss) &&
-	    p_cpar(p) || p_rwd(p, &b)) || p_lit(p, NIL);
+	    p_cpar(p) || p_rwd(p, &b)) || p_lit(p, IL_NIL);
 }
 
 int
@@ -483,8 +802,8 @@ p_cap(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_beg(p, &b, PR_CAP) && p_sp(p) &&
-	    (p_2xcombo(p, AUTHEQ, parse_authtype) || p_atom(p)) &&
+	return p_beg(p, &b, IP_CAP) && p_sp(p) &&
+	    (p_2xcombo(p, IL_AUTHEQ, p_authtype) || p_atom(p)) &&
 	    p_end(p, &b) || p_rwd(p, &b);
 }
 
@@ -493,8 +812,8 @@ p_capdata(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, CAP) &&
-	    p_rep(p, parse_cap) || p_rwd(p, &b);
+	return p_chk(p, &b) && p_lit(p, IL_CAP) &&
+	    p_rep(p, p_cap) || p_rwd(p, &b);
 }
 
 int
@@ -503,9 +822,9 @@ p_contreq(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) &&
-	    p_lit(p, PLUS) && p_sp(p) &&
+	    p_lit(p, IL_PLUS) && p_sp(p) &&
 	    (p_resptext(p) || p_base64(p)) &&
-	    p_lit(p, EOL) || p_rwd(p, &b);
+	    p_lit(p, IL_EOL) || p_rwd(p, &b);
 }
 
 int
@@ -514,7 +833,7 @@ p_datetime(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && p_dquote(p) && p_dayfix(p) &&
-	    p_lit(p, MINUS) && p_month(p) && p_lit(p, MINUS) &&
+	    p_lit(p, IL_MINUS) && p_month(p) && p_lit(p, IL_MINUS) &&
 	    p_year(p) && p_sp(p) && p_time(p) && p_sp(p) && p_zone(p) &&
 	    p_dquote(p) || p_rwd(p, &b);
 }
@@ -528,13 +847,41 @@ p_dayfix(struct parse_ctx *p)
 	    p_rwd(p, &b) || p_dig(p, 2);
 }
 
+int
+p_dig(struct parse_ctx *p, size_t count)
+{
+	uint32_t n, prev;
+	const char *t;
+
+	prev = n = 0;
+	t = p->cur.tok;
+
+	while (t != p->tokend && count && *t >= '0' && *t <= '9') {
+		prev = n;
+		n = UINT32_MAX & (10 * n + (*t - 48));
+		if (prev > n) {
+			p->e = PE_PARSE;
+			goto exit;
+		}
+		t++;
+		count--;
+	}
+	if (t == p->cur.tok || count != 0) {
+		p->e = PE_PARSE;
+	} else if (p_insnum(p, n)) {
+		p->cur.tok = t;
+		p->e = PE_OK;
+	}
+ exit:
+	return !p->e;
+}
+
 int
 p_dquotedchar(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_dquote(p) &&
-	    p_quotedchar(p) && p_dquote(p) ||
+	return p_chk(p, &b) && p_dquote(p) && p_qchar(p) && p_dquote(p) ||
 	    p_rwd(p, &b);
 }
 
@@ -556,20 +903,21 @@ p_envaddr(struct parse_ctx *p, int prod)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_opar(p) && p_rep(p, p_addr) && p_cpar(p) ||
-	    p_rwd(p, &b) || p_lit(p, NIL);
+	return p_beg(p, &b, prod) &&
+	    (p_opar(p) && p_rep(p, p_addr) && p_cpar(p) ||
+	    p_rwd(p, &b) || p_lit(p, IL_NIL)) && p_end(p, &b);
 }
 
 int
 p_envbcc(struct parse_ctx *p)
 {
-	return p_envaddr(p, PR_ENVBCC);
+	return p_envaddr(p, IP_ENVBCC);
 }
 
 int
 p_envcc(struct parse_ctx *p)
 {
-	return p_envaddr(p, cc);
+	return p_envaddr(p, IP_ENVCC);
 }
 
 int
@@ -581,7 +929,7 @@ p_envdate(struct parse_ctx *p)
 int
 p_envfrom(struct parse_ctx *p)
 {
-	return p_envaddr(p, PR_ENVFROM)
+	return p_envaddr(p, IP_ENVFROM);
 }
 
 int
@@ -599,13 +947,13 @@ p_envmsgid(struct parse_ctx *p)
 int
 p_envrepto(struct parse_ctx *p)
 {
-	return p_envaddr(p, PR_ENVREPTO)
+	return p_envaddr(p, IP_ENVREPTO);
 }
 
 int
 p_envsender(struct parse_ctx *p)
 {
-	return p_envaddr(p, PR_ENVSENDER)
+	return p_envaddr(p, IP_ENVSENDER);
 }
 
 int
@@ -617,17 +965,7 @@ p_envsubj(struct parse_ctx *p)
 int
 p_envto(struct parse_ctx *p)
 {
-	return p_envaddr(p, PR_ENVTO)
-}
-
-int
-p_escqspec(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return p_chk(p, &b) && p_lit(p, BSLASH) &&
-	    (p_dquote(p) || p_lit(p, BSLASH)) ||
-	    p_rwd(p, &b);
+	return p_envaddr(p, IP_ENVTO);
 }
 
 int
@@ -635,16 +973,16 @@ p_fetchrec(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, FETCH) && p_sp(p) &&
+	return p_chk(p, &b) && p_lit(p, IL_FETCH) && p_sp(p) &&
 	    p_opar(p) && p_list(p, p_msgatt) && p_cpar(p) || p_rwd(p, &b);
 }
 
 int
 p_flag(struct parse_ctx *p)
 {
-	return p_lit(p, ANSWERED) || p_lit(p, FLAGGED) ||
-	    p_lit(p, DELETED) || p_lit(p, SEEN) ||
-	    p_lit(p, DRAFT) || p_flkeyword(p) ||
+	return p_lit(p, IL_FLANSWERED) || p_lit(p, IL_FLFLAGGED) ||
+	    p_lit(p, IL_FLDELETED) || p_lit(p, IL_FLSEEN) ||
+	    p_lit(p, IL_FLDRAFT) || p_flkeyword(p) ||
 	    p_flext(p);
 }
 
@@ -653,14 +991,14 @@ p_flext(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, BSLASH) &&
+	return p_chk(p, &b) && p_lit(p, IL_BSLASH) &&
 	    p_atom(p) || p_rwd(p, &b);
 }
 
 int
 p_flfetch(struct parse_ctx *p)
 {
-	return p_flag(p) || p_lit(p, FLRECENT);
+	return p_flag(p) || p_lit(p, IL_FLRECENT);
 }
 
 int
@@ -683,17 +1021,17 @@ int
 p_genctext(struct parse_ctx *p)
 {
 	int res;
-	char *t;
+	const char *t;
 
-	t = p->cur.ntok;
+	t = p->cur.tok;
 
 	while (t != p->tokend && *t >= '\1' && *t <= '\x7f' &&
 	    *t != ']' && *t != '\r' && *t != '\n')
 		t++;
 
-	res = t > p->cur.ntok;
+	res = t > p->cur.tok;
 	if (res)
-		p->cur.ntok = t;
+		p->cur.tok = t;
 
 	p->e = !res;
 	return res;
@@ -715,46 +1053,53 @@ p_hdrlist(struct parse_ctx *p)
 }
 
 int
-p_lit(struct parse_ctx *p, int lit)
+p_lit(struct parse_ctx *p, int val)
 {
 	size_t left;
+	struct literal *lit;
 
-	left = p->toklen - (p->cur.ntok - p->tokens);
-	if (left < literals[lit].len) {
-		p->e = 1;
+	left = p->tokend - p->cur.tok;
+	lit = literals + val;
+
+	if (left < lit->slen) {
+		p->e = PE_PARSE;
 		goto exit;
 	}
-	if (strncasecmp(p->cur.tok, literals[lit].val, literals[lit].len) == 0) {
-		if (p->cur.nact == p->actend)) {
-			p->e = 2;
-			goto exit;
+	if (strncasecmp(p->cur.tok, lit->srep, lit->slen) == 0) {
+		if (p_inslit(p, val)) {
+			p->cur.tok += lit->slen;
+			p->e = PE_OK;
 		}
-		*p->cur.nact++ = { ... };
-		p->cur.ntok += literals[lit].len;
-		p->e = 0;
 	} else {
-		p->e = 1;
-		goto exit;
+		p->e = PE_PARSE;
 	}
  exit:
 	return !p->e;
 }
 
 int
-p_litstr(struct parse_ctx *p)
+p_lstr(struct parse_ctx *p)
 {
 	int res;
 	size_t len;
-	struct parse_cur b;
+	struct parse_cur b, n;
 
-	res = p_beg(p, MK_LITSTR, &b) && p_lit(p, OBRACE) &&
-	    p_number(p) && p_lit(p, CBRACE) && p_lit(p, EOL) &&
-	    p_end(p, &b) || p_rwd(p, &b);
+	res = p_chk(p, &b) && p_lit(p, IL_OBRACE) &&
+	    p_chk(p, &n) && p_num(p) && p_lit(p, IL_CBRACE) &&
+	    p_lit(p, IL_EOL) || p_rwd(p, &b);
 
 	if (res) {
-		len = b.pt[4].num.val;
-		if (len < p->oblth)
-			p->cur.tok += len;
+		len = n.pt->num.val;
+		if (len > UINT32_MAX) {
+			p->e = PE_STRTOOBIG;
+			res = 0;
+		} else if (len < p->oblth) {
+			res = p_insstr(p, PN_LSTR, p->cur.tok, (uint32_t)len);
+			if (res)
+				p->cur.tok += len;
+		} else {
+			res = p_insoblstr(p, (uint32_t)len);
+		}
 	}
 	return res;
 }
@@ -762,7 +1107,7 @@ p_litstr(struct parse_ctx *p)
 int
 p_mailbox(struct parse_ctx *p)
 {
-	return p_lit(p, INBOX) || p_astr(p);
+	return p_lit(p, IL_INBOX) || p_astr(p);
 }
 
 int
@@ -770,8 +1115,8 @@ p_matbody(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, BODY) &&
-	    (p_lit(p, STRUCT) || p_opt(p)) && p_sp(p) &&
+	return p_chk(p, &b) && p_lit(p, IL_BODY) &&
+	    (p_lit(p, IL_STRUCT) || p_opt(p)) && p_sp(p) &&
 	    p_body(p) || p_rwd(p, &b);
 }
 
@@ -780,15 +1125,15 @@ p_matbsect(struct parse_ctx *p)
 {
 	struct parse_cur b, o;
 
-	return p_chk(p, &b) && p_lit(p, BODY) && p_section(p) &&
-	    (p_chk(p, &o) && p_lit(p, LT) && p_num(p) && p_lit(p, GT) ||
+	return p_chk(p, &b) && p_lit(p, IL_BODY) && p_section(p) &&
+	    (p_chk(p, &o) && p_lit(p, IL_LT) && p_num(p) && p_lit(p, IL_GT) ||
 	    p_rwd(p, &o) || p_opt(p)) && p_sp(p) && p_nstr(p) || p_rwd(p, &b);
 }
 
 int
 p_matenv(struct parse_ctx *p)
 {
-	return p_3xcombo(p, EVELOPE, SP, p_env);
+	return p_3xcombo(p, IL_ENVELOPE, IL_SP, p_env);
 }
 
 int
@@ -796,8 +1141,8 @@ p_mathdrotxt(struct parse_ctx *p)
 {
 	struct parse_cur b, o;
 
-	return p_chk(p, &b) && p_lit(p, RFC822) &&
-	    (p_chk(p, &o) && p_lit(p, DHDR) || p_lit(p, DTXT) ||
+	return p_chk(p, &b) && p_lit(p, IL_RFC822) &&
+	    (p_chk(p, &o) && p_lit(p, IL_DHDR) || p_lit(p, IL_DTXT) ||
 	    p_rwd(p, &o) || p_opt(p)) && p_sp(p) && p_nstr(p) ||
 	    p_rwd(p, &b);
 }
@@ -805,19 +1150,19 @@ p_mathdrotxt(struct parse_ctx *p)
 int
 p_matintdate(struct parse_ctx *p)
 {
-	return p_3xcombo(p, INTDATE, SP, p_datetime);
+	return p_3xcombo(p, IL_INTDATE, IL_SP, p_datetime);
 }
 
 int
 p_matsize(struct parse_ctx *p)
 {
-	return p_3xcombo(p, RFC822SIZE, SP, p_num);
+	return p_3xcombo(p, IL_RFC822SIZE, IL_SP, p_num);
 }
 
 int
 p_matuid(struct parse_ctx *p)
 {
-	return p_3xcombo(p, UID, SP, p_uid);
+	return p_3xcombo(p, IL_UID, IL_SP, p_uid);
 }
 
 int
@@ -832,7 +1177,7 @@ p_mbdata(struct parse_ctx *p)
 int
 p_mbexists(struct parse_ctx *p)
 {
-	return p_r3xcombo(p, parse_number, SP, EXISTS);
+	return p_r3xcombo(p, p_num, IL_SP, IL_EXISTS);
 }
 
 int
@@ -840,9 +1185,9 @@ p_mbflags(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	rerurn p_chk(p, &b) && p_lit(p, FLAGS) &&
+	return p_chk(p, &b) && p_lit(p, IL_FLAGS) &&
 	    p_sp(p) && p_opar(p) &&
-	    (p_list(p, parse_flag) || p_opt(p)) &&
+	    (p_list(p, p_flag) || p_opt(p)) &&
 	    p_cpar(p) || p_rwd(p, &b);
 }
 
@@ -851,7 +1196,7 @@ p_mblist(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, LIST) &&
+	return p_chk(p, &b) && p_lit(p, IL_LIST) &&
 	    p_sp(p) && p_mblistdata(p) ||
 	    p_rwd(p, &b);
 }
@@ -862,9 +1207,9 @@ p_mblistdata(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && p_opar(p) &&
-	    (p_list(p, parse_mblistfl) || p_opt(p)) &&
+	    (p_list(p, p_mblistfl) || p_opt(p)) &&
 	    p_cpar(p) && p_sp(p) &&
-	    (p_dquotedchar(p) || p_lit(NIL)) &&
+	    (p_dquotedchar(p) || p_lit(p, IL_NIL)) &&
 	    p_sp(p) && p_mailbox(p) || p_rwd(p, &b);
 }
 
@@ -875,30 +1220,30 @@ p_mblistdata(struct parse_ctx *p)
 int
 p_mblistfl(struct parse_ctx *p)
 {
-	return p_mblistosfl(p) || p_list(p, parse_mblistofl);
+	return p_mblistosfl(p) || p_list(p, p_mblistofl);
 }
 
 int
 p_mblistofl(struct parse_ctx *p)
 {
-	return p_lit(p, NOINFERIORS) || p_flext(p);
+	return p_lit(p, IL_FLNOINFERIORS) || p_flext(p);
 }
 
 int
-p_mblistosfl(struc parse_ctx *p)
+p_mblistosfl(struct parse_ctx *p)
 {
 	int res;
 	struct parse_cur b;
 
-	checkpoint(p, &b);
+	p_chk(p, &b);
 
-	res = p_list(p, parse_mblistofl) &&
+	res = p_list(p, p_mblistofl) &&
 	    p_sp(p) || p_rwd(p, &b) || p_opt(p);
 
 	res = res && p_mblistsfl(p);
 
-	res = res && p_chk(p, &s) && p_sp(p) &&
-	    p_list(p, parse_mblistofl) || p_rwd(p, &s) || p_opt(p);
+	res = res && p_chk(p, &b) && p_sp(p) &&
+	    p_list(p, p_mblistofl) || p_rwd(p, &b) || p_opt(p);
 
 	return res || p_rwd(p, &b);
 }
@@ -906,8 +1251,8 @@ p_mblistosfl(struc parse_ctx *p)
 int
 p_mblistsfl(struct parse_ctx *p)
 {
-	return p_lit(p, NOSEL) || p_lit(p, MARKED) ||
-	    p_lit(p, UNMARKED);
+	return p_lit(p, IL_FLNOSEL) || p_lit(p, IL_FLMARKED) ||
+	    p_lit(p, IL_FLUNMARKED);
 }
 
 int
@@ -915,7 +1260,7 @@ p_mblsub(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, LSUB) &&
+	return p_chk(p, &b) && p_lit(p, IL_LSUB) &&
 	    p_sp(p) && p_mblistdata(p) ||
 	    p_rwd(p, &b);
 }
@@ -923,7 +1268,7 @@ p_mblsub(struct parse_ctx *p)
 int
 p_mbrecent(struct parse_ctx *p)
 {
-	return p_r3xcombo(p, parse_number, SP, RECENT);
+	return p_r3xcombo(p, p_num, IL_SP, IL_RECENT);
 }
 
 int
@@ -931,9 +1276,9 @@ p_mbsearch(struct parse_ctx *p)
 {
 	struct parse_cur b, o;
 
-	return p_chk(p, &b) && p_lit(p, SEARCH) &&
+	return p_chk(p, &b) && p_lit(p, IL_SEARCH) &&
 	    (p_chk(p, &o) && p_sp(p) &&
-	    p_list(p, parse_nznumber) || p_rwd(p, &o) || p_opt(p)) ||
+	    p_list(p, p_nznum) || p_rwd(p, &o) || p_opt(p)) ||
 	    p_rwd(p, &b);
 }
 
@@ -942,18 +1287,20 @@ p_mbstatus(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, STATUS) && p_sp(p) &&
+	return p_chk(p, &b) && p_lit(p, IL_STATUS) && p_sp(p) &&
 	    p_mailbox(p) && p_sp(p) && p_opar(p) &&
-	    (p_list(p, parse_stattrec) || p_opt(p)) && p_cpar(p) ||
+	    (p_list(p, p_stattrec) || p_opt(p)) && p_cpar(p) ||
 	    p_rwd(p, &b);
 }
 
 int
 p_medbas(struct parse_ctx *p)
 {
+	struct parse_cur b;
+
 	return p_chk(p, &b) && (p_dquote(p) &&
-	    (p_lit(p, APP) || p_lit(p, AUDIO) || p_lit(p, IMG) ||
-	    p_lit(p, MSG) || p_lit(p, VIDEO)) && p_dquote(p) ||
+	    (p_lit(p, IL_APP) || p_lit(p, IL_AUDIO) || p_lit(p, IL_IMG) ||
+	    p_lit(p, IL_MSG) || p_lit(p, IL_VIDEO)) && p_dquote(p) ||
 	    (p_rwd(p, &b) || p_str(p))) &&
 	    p_sp(p) && p_medsubt(p) || p_rwd(p, &b);
 }
@@ -964,8 +1311,8 @@ p_medmsg(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) &&
-	    p_dquote(p) && p_lit(p, MSG) && p_dquote(p) && p_sp(p) &&
-	    p_dquote(p) && p_lit(p, RFC822) && p_dquote(p) || p_rwd(p, &b);
+	    p_dquote(p) && p_lit(p, IL_MSG) && p_dquote(p) && p_sp(p) &&
+	    p_dquote(p) && p_lit(p, IL_RFC822) && p_dquote(p) || p_rwd(p, &b);
 }
 
 int
@@ -979,17 +1326,17 @@ p_medtxt(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_dquote(p) && p_lit(p, TXT) && p_dquote(p) &&
+	return p_chk(p, &b) && p_dquote(p) && p_lit(p, IL_TXT) && p_dquote(p) &&
 	    p_sp(p) && p_medsubt(p) || p_rwd(p, &b);
 }
 
 int
 p_month(struct parse_ctx *p)
 {
-	return p_lit(p, JAN) || p_lit(p, FEB) || p_lit(p, MAR) ||
-	    p_lit(p, APR) || p_lit(p, MAY) || p_lit(p, JUN) ||
-	    p_lit(p, JUL) || p_lit(p, AUG) || p_lit(p, SEP) ||
-	    p_lit(p, OCT) || p_lit(p, NOV) || p_lit(p, DEC);
+	return p_lit(p, IL_JAN) || p_lit(p, IL_FEB) || p_lit(p, IL_MAR) ||
+	    p_lit(p, IL_APR) || p_lit(p, IL_MAY) || p_lit(p, IL_JUN) ||
+	    p_lit(p, IL_JUL) || p_lit(p, IL_AUG) || p_lit(p, IL_SEP) ||
+	    p_lit(p, IL_OCT) || p_lit(p, IL_NOV) || p_lit(p, IL_DEC);
 }
 
 int
@@ -998,7 +1345,7 @@ p_msgdata(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && p_nznum(p) && p_sp(p) &&
-	    (p_lit(p, EXPUNGE) || p_fetchrec(p)) || p_rwd(p, &b);
+	    (p_lit(p, IL_EXPUNGE) || p_fetchrec(p)) || p_rwd(p, &b);
 }
 
 int
@@ -1012,7 +1359,7 @@ p_msgattdyn(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, FLAGS) && p_sp(p) && p_opar(p) &&
+	return p_chk(p, &b) && p_lit(p, IL_FLAGS) && p_sp(p) && p_opar(p) &&
 	    (p_list(p, p_flfetch) || p_opt(p)) && p_cpar(p) || p_rwd(p, &b);
 }
 
@@ -1026,42 +1373,57 @@ p_msgattstat(struct parse_ctx *p)
 int
 p_nstr(struct parse_ctx *p)
 {
-	return p_str(p) || p_lit(p, NIL);
+	return p_str(p) || p_lit(p, IL_NIL);
 }
 
 int
-p_number(struct parse_ctx *p)
+p_num(struct parse_ctx *p)
 {
-	/* DO ME */
+	uint32_t n, prev;
+	const char *t;
+
+	prev = n = 0;
+	t = p->cur.tok;
+
+	while (t != p->tokend && *t >= '0' && *t <= '9') {
+		prev = n;
+		n = UINT32_MAX & (10 * n + (*t - 48));
+		if (prev > n) {
+			p->e = PE_PARSE;
+			goto exit;
+		}
+		t++;
+	}
+	if (t == p->cur.tok) {
+		p->e = PE_PARSE;
+	} else if (p_insnum(p, n)) {
+		p->cur.tok = t;
+		p->e = PE_OK;
+	}
+ exit:
+	return !p->e;
 }
 
 int
-p_nznumber(struct parse_ctx *p)
+p_nznum(struct parse_ctx *p)
 {
-	int res;
-	char *t;
+	const char *t;
 
-	t = p->cur.ntok;
+	t = p->cur.tok;
 
-	if (t == p->tokend || *t < '1' || *t > '9')
+	if (t == p->tokend || *t < '1' || *t > '9') {
+		p->e = PE_PARSE;
 		goto exit;
-
-	while (t != p->tokend && *t >= '0' && *t <= '9')
-		t++;
-
+	}
+	p_num(p);
  exit:
-	res = t > p->cur.ntok;
-	if (res)
-		p->cur.ntok = t;
-
-	p->e = !res;
-	return res;
+	return !p->e;
 }
 
 int
 p_permfl(struct parse_ctx *p)
 {
-	return p_flag(p) || p_lit(p, ANYFL);
+	return p_flag(p) || p_lit(p, IL_FLANY);
 }
 
 int
@@ -1069,38 +1431,100 @@ p_permflcode(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, PERMFL) &&
+	return p_chk(p, &b) && p_lit(p, IL_PERMFL) &&
 	    p_sp(p) && p_opar(p) &&
-	    (p_list(p, parse_permfl) || p_opt(p)) &&
+	    (p_list(p, p_permfl) || p_opt(p)) &&
 	    p_cpar(p) || p_rwd(p, &b);
 }
 
 int
-p_quotedchar(struct parse_ctx *p)
+p_qchar(struct parse_ctx *p)
 {
-	return p_quotedcharclean(p) || p_escqspec(p);
+	const char *t;
+	int len;
+	size_t left;
+
+	t = p->cur.tok;
+	left = p->tokend - p->cur.tok;
+
+	if (t == p->tokend) {
+		p->e = PE_PARSE;
+		goto exit;
+	}
+	if (*t >= '\1' && *t <= '\x7f' && *t != '\r' &&
+	    *t != '\n' && *t != '"' && *t != '\\') {
+		len = 1;
+	} else if (left >= 2 &&
+	    (strncmp(t, "\\\"", 2) == 0 || strncmp(t, "\\\\", 2) == 0)) {
+		len = 2;
+	} else {
+		p->e = PE_PARSE;
+		goto exit;
+	}
+
+	if (!p_insstr(p, PN_QSTR, p->cur.tok, len)) {
+		p->e = PE_PARSE;
+		goto exit;
+	}
+	p->cur.tok += len;
+	p->e = PE_OK;
+
+ exit:
+	return !p->e;
 }
 
 int
-p_quotedcharclean(struct parse_ctx *p)
+p_qchars(struct parse_ctx *p)
 {
-	if (p->cur.ntok != p->tokend &&
-	    binsearch(quotedclean, *p->cur.ntok) == -1) {
-		p->cur.ntok++;
-		p->e = 0;
-	} else {
-		p->e = 1;
+	const char *t;
+	size_t len;
+	int esc;
+
+	t = p->cur.tok;
+	esc = 0;
+	while (t != p->tokend) {
+		if (esc) {
+			if (*t == '"' || *t == '\\') {
+				t++;
+				esc = 0;
+			} else {
+				p->e = PE_PARSE;
+				goto exit;
+			}
+		} else if (*t == '\\') {
+			t++;
+			esc = 1;
+		} else if (*t >= '\1' && *t <= '\x7f' && *t != '\r' &&
+		    *t != '\n' && *t != '"' && *t != '\\') {
+			t++;
+		} else {
+			p->e = PE_PARSE;
+			goto exit;
+		}
 	}
+	len = t - p->cur.tok;
+	if (len == 0) {
+		p->e = PE_PARSE;
+		goto exit;
+	} else if (len > UINT32_MAX) {
+		p->e = PE_STRTOOBIG;
+		goto exit;
+	} else if (!p_insstr(p, PN_QSTR, p->cur.tok, (uint32_t)len))
+		goto exit;
+
+	p->cur.tok = t;
+	p->e = PE_OK;
+
+ exit:
 	return !p->e;
 }
 
 int
-p_quotstr(struct parse_ctx *p)
+p_qstr(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_beg(p, &b, MK_QUOTSTR) && p_dquote(p) &&
-	    (p_rep(p, parse_quotedchar) || p_opt(p)) &&
+	return p_chk(p, &b) && p_dquote(p) && (p_qchars(p) || p_opt(p)) &&
 	    p_dquote(p) || p_rwd(p, &b);
 }
 
@@ -1109,7 +1533,7 @@ p_respbye(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, BYE) && p_sp(p) &&
+	return p_chk(p, &b) && p_lit(p, IL_BYE) && p_sp(p) &&
 	    p_resptext(p) || p_rwd(p, &b);
 }
 
@@ -1118,10 +1542,10 @@ p_respdata(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, ASTERISK) &&
+	return p_chk(p, &b) && p_lit(p, IL_ASTERISK) &&
 	    p_sp(p) && (p_respstate(p) ||
 	    p_respbye(p) || p_mbdata(p) || p_msgdata(p) ||
-	    p_capdata(p)) && p_eol(p) || p_rwd(p &s);
+	    p_capdata(p)) && p_lit(p, IL_EOL) || p_rwd(p, &b);
 }
 
 int
@@ -1136,7 +1560,7 @@ p_respstate(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) &&
-	    (p_lit(p, OK) || p_lit(p, NO) || p_lit(p, BAD)) &&
+	    (p_lit(p, IL_OK) || p_lit(p, IL_NO) || p_lit(p, IL_BAD)) &&
 	    p_sp(p) && p_resptext(p) || p_rwd(p, &b);
 }
 
@@ -1146,8 +1570,8 @@ p_resptext(struct parse_ctx *p)
 	int res;
 	struct parse_cur b;
 
-	res = p_chk(p, &b) && p_lit(p, OBRACK) &&
-	    p_resptextcode(p) && p_lit(p, CBRACK) &&
+	res = p_chk(p, &b) && p_lit(p, IL_OBRACK) &&
+	    p_resptextcode(p) && p_lit(p, IL_CBRACK) &&
 	    p_sp(p) || p_rwd(p, &b) || p_opt(p);
 
 	return res && p_text(p);
@@ -1156,10 +1580,10 @@ p_resptext(struct parse_ctx *p)
 int
 p_resptextcode(struct parse_ctx *p)
 {
-	return p_lit(p, ALERT) || p_badcscode(p) ||
-	    p_capdata(p) || p_lit(p, PARSE) ||
-	    p_permflcode(p) || p_lit(p, RO) ||
-	    p_lit(p, RW) || p_lit(p, TRYC) ||
+	return p_lit(p, IL_ALERT) || p_badcscode(p) ||
+	    p_capdata(p) || p_lit(p, IL_PARSE) ||
+	    p_permflcode(p) || p_lit(p, IL_RO) ||
+	    p_lit(p, IL_RW) || p_lit(p, IL_TRYC) ||
 	    p_uidncode(p) || p_uidvcode(p) ||
 	    p_unseencode(p) || p_gencode(p);
 }
@@ -1169,15 +1593,15 @@ p_secmsgtxt(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, HEADER) ||
-	    (p_lit(p, HDRFLDS) && (p_lit(p, DNOT) || p_opt(p)) &&
-	    p_sp(p) && p_hdrlist(p) || p_rwd(p, &b)) || p_lit(p, TXT);
+	return p_chk(p, &b) && p_lit(p, IL_HDR) ||
+	    (p_lit(p, IL_HDRFIELDS) && (p_lit(p, IL_DNOT) || p_opt(p)) &&
+	    p_sp(p) && p_hdrlist(p) || p_rwd(p, &b)) || p_lit(p, IL_TXT);
 }
 
 int
 p_secpt(struct parse_ctx *p)
 {
-	return p_listsep(p, p_nznum, DOT);
+	return p_listsep(p, p_nznum, IL_DOT);
 }
 
 int
@@ -1186,7 +1610,7 @@ p_secspec(struct parse_ctx *p)
 	struct parse_cur b, o;
 
 	return p_chk(p, &b) && p_secmsgtxt(p) || (p_secpt(p) &&
-	    (p_chk(p, &o) && p_lit(p, DOT) && p_sectxt(p) ||
+	    (p_chk(p, &o) && p_lit(p, IL_DOT) && p_sectxt(p) ||
 	    p_rwd(p, &o) || p_opt(p))) || p_rwd(p, &b);
 }
 
@@ -1195,23 +1619,23 @@ p_section(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_lit(p, OBRACK) &&
+	return p_chk(p, &b) && p_lit(p, IL_OBRACK) &&
 	    (p_secspec(p) || p_opt(p)) &&
-	    p_lit(p, CBRACK) || p_rwd(p, &b);
+	    p_lit(p, IL_CBRACK) || p_rwd(p, &b);
 }
 
 int
 p_sectxt(struct parse_ctx *p)
 {
-	return p_secmsgtxt(p) || p_lit(p, MIME);
+	return p_secmsgtxt(p) || p_lit(p, IL_MIME);
 }
 
 int
 p_statt(struct parse_ctx *p)
 {
-	return p_lit(p, MESSAGES) || p_lit(p, RECENT) ||
-	    p_lit(p, UIDNEXT) || p_lit(p, UIDVAL) ||
-	    p_lit(p, UNSEEN);
+	return p_lit(p, IL_MSGS) || p_lit(p, IL_RECENT) ||
+	    p_lit(p, IL_UIDNEXT) || p_lit(p, IL_UIDVAL) ||
+	    p_lit(p, IL_UNSEEN);
 }
 
 int
@@ -1220,19 +1644,19 @@ p_stattrec(struct parse_ctx *p)
 	struct parse_cur b;
 
 	return p_chk(p, &b) && p_statt(p) && p_sp(p) &&
-	    p_number(p) || p_rwd(p, &b);
+	    p_num(p) || p_rwd(p, &b);
 }
 
 int
 p_str(struct parse_ctx *p)
 {
-	return p_qoutstr(p) || p_litstr(p);
+	return p_qstr(p) || p_lstr(p);
 }
 
 int
 p_tag(struct parse_ctx *p)
 {
-	return p_repchr(p, tag_specials);
+	return p_repchr(p, tag_specials, LEN(tag_specials), PN_STR);
 }
 
 int
@@ -1240,8 +1664,38 @@ p_taggedresp(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_tag(p) && p_sp(p) &&
-	    p_respstate(p) && p_eol(p) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_TAGGEDRESP) && (p_tag(p) && p_sp(p) &&
+	    p_respstate(p) && p_lit(p, IL_EOL) || p_rwd(p, &b)) &&
+	    p_end(p, &b);
+}
+
+int
+p_text(struct parse_ctx *p)
+{
+	const char *t;
+	size_t len;
+
+	t = p->cur.tok;
+	while (t != p->tokend && *t >= '\1' && *t <= '\x7f' &&
+	    *t != '\r' && *t != '\n')
+		t++;
+
+	len = t - p->cur.tok;
+	if (len == 0) {
+		p->e = PE_PARSE;
+		goto exit;
+	} else if (len > UINT32_MAX) {
+		p->e = PE_STRTOOBIG;
+		goto exit;
+	}
+	if (!p_insstr(p, PN_STR, p->cur.tok, (uint32_t)len))
+		goto exit;
+
+	p->cur.tok = t;
+	p->e = PE_OK;
+
+ exit:
+	return !p->e;
 }
 
 int
@@ -1249,32 +1703,32 @@ p_time(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && p_dig(p, 2) && p_lit(p, COLON) &&
-	    p_dig(p, 2) && p_lit(p, COLON) && p_dig(p, 2) || p_rwd(p, &b);
+	return p_chk(p, &b) && p_dig(p, 2) && p_lit(p, IL_COLON) &&
+	    p_dig(p, 2) && p_lit(p, IL_COLON) && p_dig(p, 2) || p_rwd(p, &b);
 }
 
 int
 p_uid(struct parse_ctx *p)
 {
-	return p_nznumber(p);
+	return p_nznum(p);
 }
 
 int
 p_uidncode(struct parse_ctx *p)
 {
-	return p_3xcombo(UIDNEXT, SP, parse_nznumber);
+	return p_3xcombo(p, IL_UIDNEXT, IL_SP, p_nznum);
 }
 
 int
 p_uidvcode(struct parse_ctx *p)
 {
-	return p_3xcombo(UIDVAL, SP, parse_nznumber);
+	return p_3xcombo(p, IL_UIDVAL, IL_SP, p_nznum);
 }
 
 int
 p_unseencode(struct parse_ctx *p)
 {
-	return p_3xcombo(UNSEEN, SP, parse_nznumber);
+	return p_3xcombo(p, IL_UNSEEN, IL_SP, p_nznum);
 }
 
 int
@@ -1288,7 +1742,8 @@ p_zone(struct parse_ctx *p)
 {
 	struct parse_cur b;
 
-	return p_chk(p, &b) && (p_lit(p, PLUS) || p_lit(p, MINUS)) &&
+	return p_chk(p, &b) &&
+	    (p_lit(p, IL_PLUS) || p_lit(p, IL_MINUS)) &&
 	    p_dig(p, 4) || p_rwd(p, &b);
 }
 
@@ -1298,23 +1753,23 @@ p_zone(struct parse_ctx *p)
 int
 p_cpar(struct parse_ctx *p)
 {
-	return p_lit(p, CPAR);
+	return p_lit(p, IL_CPAR);
 }
 
 int
 p_dquote(struct parse_ctx *p)
 {
-	return p_lit(p, DQUOTE);
+	return p_lit(p, IL_DQUOTE);
 }
 
 int
 p_opar(struct parse_ctx *p)
 {
-	return p_lit(p, OPAR);
+	return p_lit(p, IL_OPAR);
 }
 
 int
 p_sp(struct parse_ctx *p)
 {
-	return p_lit(p, SP);
+	return p_lit(p, IL_SP);
 }
diff --git a/parse.h b/parse.h
index 9ab51dd..c83ee61 100644
--- a/parse.h
+++ b/parse.h
@@ -1,9 +1,114 @@
-enum parse_lit { };
-enum parse_prod { };
+enum {
+	IL_7BIT,
+	IL_8BIT,
+	IL_ALERT,
+	IL_APP,
+	IL_APR,
+	IL_ASTERISK,
+	IL_AUDIO,
+	IL_AUG,
+	IL_AUTHEQ,
+	IL_B64,
+	IL_BAD,
+	IL_BADCS,
+	IL_BIN,
+	IL_BODY,
+	IL_BSLASH,
+	IL_BYE,
+	IL_CAP,
+	IL_CBRACE,
+	IL_CBRACK,
+	IL_COLON,
+	IL_CPAR,
+	IL_DEC,
+	IL_DHDR,
+	IL_DNOT,
+	IL_DOT,
+	IL_DQUOTE,
+	IL_DTXT,
+	IL_ENVELOPE,
+	IL_EOL,
+	IL_EXISTS,
+	IL_EXPUNGE,
+	IL_FEB,
+	IL_FETCH,
+	IL_FLAGS,
+	IL_FLANSWERED,
+	IL_FLANY,
+	IL_FLDELETED,
+	IL_FLDRAFT,
+	IL_FLFLAGGED,
+	IL_FLMARKED,
+	IL_FLNOINFERIORS,
+	IL_FLNOSEL,
+	IL_FLRECENT,
+	IL_FLSEEN,
+	IL_FLUNMARKED,
+	IL_GT,
+	IL_HDR,
+	IL_HDRFIELDS,
+	IL_IMG,
+	IL_INBOX,
+	IL_INTDATE,
+	IL_JAN,
+	IL_JUL,
+	IL_JUN,
+	IL_LIST,
+	IL_LSUB,
+	IL_LT,
+	IL_MAR,
+	IL_MAY,
+	IL_MIME,
+	IL_MINUS,
+	IL_MSG,
+	IL_MSGS,
+	IL_NIL,
+	IL_NO,
+	IL_NOV,
+	IL_OBRACE,
+	IL_OBRACK,
+	IL_OCT,
+	IL_OK,
+	IL_OPAR,
+	IL_PARSE,
+	IL_PERMFL,
+	IL_PLUS,
+	IL_QPRN,
+	IL_RECENT,
+	IL_RFC822,
+	IL_RFC822SIZE,
+	IL_RO,
+	IL_RW,
+	IL_SEARCH,
+	IL_SEP,
+	IL_SP,
+	IL_STATUS,
+	IL_STRUCT,
+	IL_TRYC,
+	IL_TXT,
+	IL_UID,
+	IL_UIDNEXT,
+	IL_UIDVAL,
+	IL_UNSEEN,
+	IL_VIDEO,
+};
+
+enum {
+	IP_ASTR,
+	IP_ATOM,
+	IP_CAP,
+	IP_ENVBCC,
+	IP_ENVCC,
+	IP_ENVFROM,
+	IP_ENVREPTO,
+	IP_ENVSENDER,
+	IP_ENVTO,
+	IP_TAGGEDRESP,
+};
 
 enum { PN_INTER, PN_LIT, PN_NUM, PN_STR, PN_QSTR, PN_LSTR, PN_OBLSTR };
 
-enum { PE_OK, PE_PARSE, PE_NOTENNODES };
+enum { PE_OK, PE_PARSE, PE_NOTENNODES, PE_STRTOOBIG };
 
 union parse_node {
 	int type;
@@ -18,17 +123,17 @@ union parse_node {
 	} lit;
 	struct {
 		int type;
-		size_t val;
+		uint32_t val;
 	} num;
 	struct {
 		int type;
 		uint32_t len;
 		union {
-			char *tok;
+			const char *tok;
 			size_t iob;
 		};
 	} str;
 };
 
 int parse_respln(const char *tok, size_t toklen, union parse_node *pt,
-    size_t ptlen, size_t oblth)
+    size_t ptlen, size_t oblth);
diff --git a/pshades.c b/pshades.c
new file mode 100644
index 0000000..c5854c3
--- /dev/null
+++ b/pshades.c
@@ -0,0 +1,129 @@
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "parse.h"
+
+#define LEN(a) (sizeof(a) / sizeof(a)[0])
+
+char *perr[] = {
+	[PE_PARSE]      = "input not recognized",
+	[PE_NOTENNODES] = "cannot fit parse tree within supplied space",
+	[PE_STRTOOBIG]  = "stalled upon a string longer than 2^32 - 1",
+};
+
+char *prod[] = {
+	"ASTR",
+	"ATOM",
+	"CAP",
+	"ENVBCC",
+	"ENVCC",
+	"ENVFROM",
+	"ENVREPTO",
+	"ENVSENDER",
+	"ENVTO",
+	"TAGGEDRESP",
+};
+
+char *literals[] = {
+	"7BIT", "8BIT", "ALERT", "APP", "APR", "ASTERISK", "AUDIO", "AUG",
+	"AUTHEQ", "B64", "BAD", "BADCS", "BIN", "BODY", "BSLASH", "BYE",
+	"CAP", "CBRACE", "CBRACK", "COLON", "CPAR", "DEC", "DHDR", "DNOT",
+	"DOT", "DQUOTE", "DTXT", "ENVELOPE", "EOL", "EXISTS", "EXPUNGE",
+	"FEB", "FETCH", "FLAGS", "FLANSWERED", "FLANY", "FLDELETED",
+	"FLDRAFT", "FLFLAGGED", "FLRECENT", "FLSEEN", "GT", "HDR",
+	"HDRFIELDS", "IMG", "INBOX", "INTDATE", "JAN", "JUL", "JUN", "LIST",
+	"LSUB", "LT", "MAR", "MARKED", "MAY", "MIME", "MINUS", "MSG", "MSGS",
+	"NIL", "NO", "NOINFERIORS", "NOSEL", "NOV", "OBRACE", "OBRACK",
+	"OCT", "OK", "OPAR", "PARSE", "PERMFL", "PLUS", "QPRN", "RECENT",
+	"RFC822", "RFC822SIZE", "RO", "RW", "SEARCH", "SEP", "SP", "STATUS",
+	"STRUCT", "TRYC", "TXT", "UID", "UIDNEXT", "UIDVAL", "UNMARKED",
+	"UNSEEN", "VIDEO",
+};
+
+void ptprint(union parse_node *, size_t);
+union parse_node *ptnext(union parse_node *);
+
+int
+main(int argc, char **argv)
+{
+	size_t l, inlen;
+	ssize_t n;
+	char input[1024], *p;
+	union parse_node pt[1024];
+	int e;
+
+	p = input;
+	l = LEN(input);
+
+	while (l > 0 && (n = read(STDIN_FILENO, p, l))) {
+		if (n < 0) {
+			if (errno == EINTR) continue;
+			else break;
+		}
+		p += n;
+		l -= n;
+	}
+
+	if (n < 0)
+		err(1, "error reading input");
+	else if (l == 0 && n > 0)
+		errx(1, "input is too big (%zu is max accepted)", LEN(input));
+
+	inlen = p - input;
+
+	if ((e = parse_respln(input, inlen, pt, LEN(pt), 1024)))
+		errx(1, "error parsing input: %s",  perr[e]);
+
+	ptprint(pt, 0);
+	return 0;
+}
+
+union parse_node *
+ptnext(union parse_node *n)
+{
+	if (n->type == PN_INTER)
+	    n += n->inter.len;
+
+	return n + 1;
+}
+
+void
+ptprint(union parse_node *n, size_t depth)
+{
+	static char *prefix = "               >";
+	size_t l;
+
+	printf("%.*s", (int)depth, prefix);
+
+	switch (n->type) {
+	case PN_INTER:
+		printf("%s\n", prod[n->inter.prod]);
+		for (l = n->inter.len, n++; l > 0; l--, n = ptnext(n))
+			ptprint(n, depth + 1);
+		break;
+	case PN_LIT:
+		printf("%s\n", literals[n->lit.val]);
+		break;
+	case PN_NUM:
+		/* acting a tad careless here... */
+		printf("%zu\n", (size_t)n->num.val);
+		break;
+	case PN_STR:
+		printf("%.*s\n", (int)n->str.len, n->str.tok);
+		break;
+	case PN_QSTR:
+		printf("\"%.*s\"\n", (int)n->str.len, n->str.tok);
+		break;
+	case PN_LSTR:
+		printf("%.*s\n", (int)n->str.len, n->str.tok);
+		break;
+	case PN_OBLSTR:
+		printf("OBLSTR #%zu\n", n->str.iob);
+		break;
+	default:
+		errx(1, "cannot grok parse node of type %d\n", n->type);
+	}
+}