Merge branch 'master' of peewee-vb.local:Projects/hammer/hammer
This commit is contained in:
commit
8e9c480a45
29 changed files with 942 additions and 761 deletions
30
src/Makefile
30
src/Makefile
|
|
@ -1,11 +1,36 @@
|
||||||
|
|
||||||
|
PARSERS := \
|
||||||
|
unimplemented \
|
||||||
|
bits \
|
||||||
|
token \
|
||||||
|
whitespace \
|
||||||
|
ch \
|
||||||
|
action \
|
||||||
|
charset \
|
||||||
|
int_range \
|
||||||
|
sequence \
|
||||||
|
choice \
|
||||||
|
nothing \
|
||||||
|
end \
|
||||||
|
butnot \
|
||||||
|
difference \
|
||||||
|
many \
|
||||||
|
xor \
|
||||||
|
optional \
|
||||||
|
ignore \
|
||||||
|
epsilon \
|
||||||
|
and \
|
||||||
|
not \
|
||||||
|
attr_bool
|
||||||
|
|
||||||
OUTPUTS := bitreader.o \
|
OUTPUTS := bitreader.o \
|
||||||
hammer.o \
|
hammer.o \
|
||||||
libhammer.a \
|
libhammer.a \
|
||||||
pprint.o \
|
pprint.o \
|
||||||
allocator.o \
|
allocator.o \
|
||||||
datastructures.o \
|
datastructures.o \
|
||||||
test_suite
|
test_suite \
|
||||||
|
$(PARSERS:%=parsers/%.o)
|
||||||
|
|
||||||
TOPLEVEL := ../
|
TOPLEVEL := ../
|
||||||
|
|
||||||
|
|
@ -17,7 +42,8 @@ all: libhammer.a test_suite
|
||||||
test_suite: test_suite.o libhammer.a
|
test_suite: test_suite.o libhammer.a
|
||||||
$(call hush, "Linking $@") $(CC) -o $@ $^ $(LDFLAGS)
|
$(call hush, "Linking $@") $(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
libhammer.a: bitreader.o hammer.o pprint.o allocator.o datastructures.o
|
libhammer.a: bitreader.o hammer.o pprint.o allocator.o datastructures.o \
|
||||||
|
$(PARSERS:%=parsers/%.o)
|
||||||
|
|
||||||
bitreader.o: test_suite.h
|
bitreader.o: test_suite.h
|
||||||
hammer.o: hammer.h
|
hammer.o: hammer.h
|
||||||
|
|
|
||||||
765
src/hammer.c
765
src/hammer.c
|
|
@ -15,19 +15,16 @@
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "hammer.h"
|
|
||||||
#include "internal.h"
|
|
||||||
#include "allocator.h"
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "hammer.h"
|
||||||
#define a_new_(arena, typ, count) ((typ*)h_arena_malloc((arena), sizeof(typ)*(count)))
|
#include "internal.h"
|
||||||
#define a_new(typ, count) a_new_(state->arena, typ, count)
|
#include "allocator.h"
|
||||||
// we can create a_new0 if necessary. It would allocate some memory and immediately zero it out.
|
#include "parsers/parser_internal.h"
|
||||||
|
|
||||||
static guint djbhash(const uint8_t *buf, size_t len) {
|
static guint djbhash(const uint8_t *buf, size_t len) {
|
||||||
guint hash = 5381;
|
guint hash = 5381;
|
||||||
|
|
@ -54,7 +51,7 @@ HParserCacheValue* recall(HParserCacheKey *k, HParseState *state) {
|
||||||
if (g_slist_find(head->eval_set, k->parser)) {
|
if (g_slist_find(head->eval_set, k->parser)) {
|
||||||
// Something is in the cache, and the key parser is in the eval set. Remove the key parser from the eval set of the head.
|
// Something is in the cache, and the key parser is in the eval set. Remove the key parser from the eval set of the head.
|
||||||
head->eval_set = g_slist_remove_all(head->eval_set, k->parser);
|
head->eval_set = g_slist_remove_all(head->eval_set, k->parser);
|
||||||
HParseResult *tmp_res = k->parser->fn(k->parser->env, state);
|
HParseResult *tmp_res = k->parser->vtable->parse(k->parser->env, state);
|
||||||
if (tmp_res)
|
if (tmp_res)
|
||||||
tmp_res->arena = state->arena;
|
tmp_res->arena = state->arena;
|
||||||
// we know that cached has an entry here, modify it
|
// we know that cached has an entry here, modify it
|
||||||
|
|
@ -102,7 +99,7 @@ HParseResult* grow(HParserCacheKey *k, HParseState *state, HRecursionHead *head)
|
||||||
head->eval_set = head->involved_set;
|
head->eval_set = head->involved_set;
|
||||||
HParseResult *tmp_res;
|
HParseResult *tmp_res;
|
||||||
if (k->parser) {
|
if (k->parser) {
|
||||||
tmp_res = k->parser->fn(k->parser->env, state);
|
tmp_res = k->parser->vtable->parse(k->parser->env, state);
|
||||||
if (tmp_res)
|
if (tmp_res)
|
||||||
tmp_res->arena = state->arena;
|
tmp_res->arena = state->arena;
|
||||||
} else
|
} else
|
||||||
|
|
@ -170,7 +167,7 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) {
|
||||||
HParseResult *tmp_res;
|
HParseResult *tmp_res;
|
||||||
if (parser) {
|
if (parser) {
|
||||||
HInputStream bak = state->input_stream;
|
HInputStream bak = state->input_stream;
|
||||||
tmp_res = parser->fn(parser->env, state);
|
tmp_res = parser->vtable->parse(parser->env, state);
|
||||||
if (tmp_res) {
|
if (tmp_res) {
|
||||||
tmp_res->arena = state->arena;
|
tmp_res->arena = state->arena;
|
||||||
if (!state->input_stream.overrun) {
|
if (!state->input_stream.overrun) {
|
||||||
|
|
@ -217,760 +214,12 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper function, since these lines appear in every parser */
|
/* Helper function, since these lines appear in every parser */
|
||||||
static HParseResult* make_result(HParseState *state, HParsedToken *tok) {
|
|
||||||
HParseResult *ret = a_new(HParseResult, 1);
|
|
||||||
ret->ast = tok;
|
|
||||||
ret->arena = state->arena;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t *str;
|
|
||||||
uint8_t len;
|
|
||||||
} HToken;
|
|
||||||
|
|
||||||
static HParseResult* parse_unimplemented(void* env, HParseState *state) {
|
|
||||||
(void) env;
|
|
||||||
(void) state;
|
|
||||||
static HParsedToken token = {
|
|
||||||
.token_type = TT_ERR
|
|
||||||
};
|
|
||||||
static HParseResult result = {
|
|
||||||
.ast = &token
|
|
||||||
};
|
|
||||||
return &result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParser unimplemented __attribute__((unused)) = {
|
|
||||||
.fn = parse_unimplemented,
|
|
||||||
.env = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bits_env {
|
|
||||||
uint8_t length;
|
|
||||||
uint8_t signedp;
|
|
||||||
};
|
|
||||||
|
|
||||||
static HParseResult* parse_bits(void* env, HParseState *state) {
|
|
||||||
struct bits_env *env_ = env;
|
|
||||||
HParsedToken *result = a_new(HParsedToken, 1);
|
|
||||||
result->token_type = (env_->signedp ? TT_SINT : TT_UINT);
|
|
||||||
if (env_->signedp)
|
|
||||||
result->sint = h_read_bits(&state->input_stream, env_->length, true);
|
|
||||||
else
|
|
||||||
result->uint = h_read_bits(&state->input_stream, env_->length, false);
|
|
||||||
return make_result(state, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_bits(size_t len, bool sign) {
|
|
||||||
struct bits_env *env = g_new(struct bits_env, 1);
|
|
||||||
env->length = len;
|
|
||||||
env->signedp = sign;
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_bits;
|
|
||||||
res->env = env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SIZED_BITS(name_pre, len, signedp) \
|
|
||||||
const HParser* h_##name_pre##len () { \
|
|
||||||
return h_bits(len, signedp); \
|
|
||||||
}
|
|
||||||
SIZED_BITS(int, 8, true)
|
|
||||||
SIZED_BITS(int, 16, true)
|
|
||||||
SIZED_BITS(int, 32, true)
|
|
||||||
SIZED_BITS(int, 64, true)
|
|
||||||
SIZED_BITS(uint, 8, false)
|
|
||||||
SIZED_BITS(uint, 16, false)
|
|
||||||
SIZED_BITS(uint, 32, false)
|
|
||||||
SIZED_BITS(uint, 64, false)
|
|
||||||
|
|
||||||
static HParseResult* parse_token(void *env, HParseState *state) {
|
|
||||||
HToken *t = (HToken*)env;
|
|
||||||
for (int i=0; i<t->len; ++i) {
|
|
||||||
uint8_t chr = (uint8_t)h_read_bits(&state->input_stream, 8, false);
|
|
||||||
if (t->str[i] != chr) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HParsedToken *tok = a_new(HParsedToken, 1);
|
|
||||||
tok->token_type = TT_BYTES; tok->bytes.token = t->str; tok->bytes.len = t->len;
|
|
||||||
return make_result(state, tok);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_token(const uint8_t *str, const size_t len) {
|
|
||||||
HToken *t = g_new(HToken, 1);
|
|
||||||
t->str = (uint8_t*)str, t->len = len;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_token; ret->env = t;
|
|
||||||
return (const HParser*)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_ch(void* env, HParseState *state) {
|
|
||||||
uint8_t c = (uint8_t)GPOINTER_TO_UINT(env);
|
|
||||||
uint8_t r = (uint8_t)h_read_bits(&state->input_stream, 8, false);
|
|
||||||
if (c == r) {
|
|
||||||
HParsedToken *tok = a_new(HParsedToken, 1);
|
|
||||||
tok->token_type = TT_UINT; tok->uint = r;
|
|
||||||
return make_result(state, tok);
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_ch(const uint8_t c) {
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_ch; ret->env = GUINT_TO_POINTER(c);
|
|
||||||
return (const HParser*)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_whitespace(void* env, HParseState *state) {
|
|
||||||
char c;
|
|
||||||
HInputStream bak;
|
|
||||||
do {
|
|
||||||
bak = state->input_stream;
|
|
||||||
c = h_read_bits(&state->input_stream, 8, false);
|
|
||||||
if (state->input_stream.overrun)
|
|
||||||
return NULL;
|
|
||||||
} while (isspace(c));
|
|
||||||
state->input_stream = bak;
|
|
||||||
return h_do_parse((HParser*)env, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_whitespace(const HParser* p) {
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_whitespace;
|
|
||||||
ret->env = (void*)p;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const HParser *p;
|
|
||||||
HAction action;
|
|
||||||
} HParseAction;
|
|
||||||
|
|
||||||
static HParseResult* parse_action(void *env, HParseState *state) {
|
|
||||||
HParseAction *a = (HParseAction*)env;
|
|
||||||
if (a->p && a->action) {
|
|
||||||
HParseResult *tmp = h_do_parse(a->p, state);
|
|
||||||
//HParsedToken *tok = a->action(h_do_parse(a->p, state));
|
|
||||||
const HParsedToken *tok = a->action(tmp);
|
|
||||||
return make_result(state, (HParsedToken*)tok);
|
|
||||||
} else // either the parser's missing or the action's missing
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_action(const HParser* p, const HAction a) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_action;
|
|
||||||
HParseAction *env = g_new(HParseAction, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->action = a;
|
|
||||||
res->env = (void*)env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_charset(void *env, HParseState *state) {
|
|
||||||
uint8_t in = h_read_bits(&state->input_stream, 8, false);
|
|
||||||
HCharset cs = (HCharset)env;
|
|
||||||
|
|
||||||
if (charset_isset(cs, in)) {
|
|
||||||
HParsedToken *tok = a_new(HParsedToken, 1);
|
|
||||||
tok->token_type = TT_UINT; tok->uint = in;
|
|
||||||
return make_result(state, tok);
|
|
||||||
} else
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_ch_range(const uint8_t lower, const uint8_t upper) {
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
HCharset cs = new_charset();
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
charset_set(cs, i, (lower <= i) && (i <= upper));
|
|
||||||
ret->fn = parse_charset; ret->env = (void*)cs;
|
|
||||||
return (const HParser*)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const HParser *p;
|
|
||||||
int64_t lower;
|
|
||||||
int64_t upper;
|
|
||||||
} HRange;
|
|
||||||
|
|
||||||
static HParseResult* parse_int_range(void *env, HParseState *state) {
|
|
||||||
HRange *r_env = (HRange*)env;
|
|
||||||
HParseResult *ret = h_do_parse(r_env->p, state);
|
|
||||||
if (!ret || !ret->ast)
|
|
||||||
return NULL;
|
|
||||||
switch(ret->ast->token_type) {
|
|
||||||
case TT_SINT:
|
|
||||||
if (r_env->lower <= ret->ast->sint && r_env->upper >= ret->ast->sint)
|
|
||||||
return ret;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
case TT_UINT:
|
|
||||||
if ((uint64_t)r_env->lower <= ret->ast->uint && (uint64_t)r_env->upper >= ret->ast->uint)
|
|
||||||
return ret;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_int_range(const HParser *p, const int64_t lower, const int64_t upper) {
|
|
||||||
struct bits_env *b_env = p->env;
|
|
||||||
// p must be an integer parser, which means it's using parse_bits
|
|
||||||
assert_message(p->fn == parse_bits, "int_range requires an integer parser");
|
|
||||||
// if it's a uint parser, it can't be uint64
|
|
||||||
assert_message(!(b_env->signedp) ? (b_env->length < 64) : true, "int_range can't use a uint64 parser");
|
|
||||||
// and regardless, the bounds need to fit in the parser in question
|
|
||||||
switch(b_env->length) {
|
|
||||||
case 32:
|
|
||||||
if (b_env->signedp)
|
|
||||||
assert_message(lower >= INT_MIN && upper <= INT_MAX, "bounds for 32-bit signed integer exceeded");
|
|
||||||
else
|
|
||||||
assert_message(lower >= 0 && upper <= UINT_MAX, "bounds for 32-bit unsigned integer exceeded");
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
if (b_env->signedp)
|
|
||||||
assert_message(lower >= SHRT_MIN && upper <= SHRT_MAX, "bounds for 16-bit signed integer exceeded");
|
|
||||||
else
|
|
||||||
assert_message(lower >= 0 && upper <= USHRT_MAX, "bounds for 16-bit unsigned integer exceeded");
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
if (b_env->signedp)
|
|
||||||
assert_message(lower >= SCHAR_MIN && upper <= SCHAR_MAX, "bounds for 8-bit signed integer exceeded");
|
|
||||||
else
|
|
||||||
assert_message(lower >= 0 && upper <= UCHAR_MAX, "bounds for 8-bit unsigned integer exceeded");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// how'd that happen? if we got here, this parser is broken.
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRange *r_env = g_new(HRange, 1);
|
|
||||||
r_env->p = p;
|
|
||||||
r_env->lower = lower;
|
|
||||||
r_env->upper = upper;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_int_range;
|
|
||||||
ret->env = (void*)r_env;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_not_in(const uint8_t *options, int count) {
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
HCharset cs = new_charset();
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
charset_set(cs, i, 1);
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
charset_set(cs, options[i], 0);
|
|
||||||
|
|
||||||
ret->fn = parse_charset; ret->env = (void*)cs;
|
|
||||||
return (const HParser*)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_end(void *env, HParseState *state) {
|
|
||||||
if (state->input_stream.index == state->input_stream.length) {
|
|
||||||
HParseResult *ret = a_new(HParseResult, 1);
|
|
||||||
ret->ast = NULL;
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_end_p() {
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_end; ret->env = NULL;
|
|
||||||
return (const HParser*)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_nothing() {
|
|
||||||
// not a mistake, this parser always fails
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_nothing_p() {
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_nothing; ret->env = NULL;
|
|
||||||
return (const HParser*)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
size_t len;
|
|
||||||
const HParser **p_array;
|
|
||||||
} HSequence;
|
|
||||||
|
|
||||||
static HParseResult* parse_sequence(void *env, HParseState *state) {
|
|
||||||
HSequence *s = (HSequence*)env;
|
|
||||||
HCountedArray *seq = h_carray_new_sized(state->arena, (s->len > 0) ? s->len : 4);
|
|
||||||
for (size_t i=0; i<s->len; ++i) {
|
|
||||||
HParseResult *tmp = h_do_parse(s->p_array[i], state);
|
|
||||||
// if the interim parse fails, the whole thing fails
|
|
||||||
if (NULL == tmp) {
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
if (tmp->ast)
|
|
||||||
h_carray_append(seq, (void*)tmp->ast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HParsedToken *tok = a_new(HParsedToken, 1);
|
|
||||||
tok->token_type = TT_SEQUENCE; tok->seq = seq;
|
|
||||||
return make_result(state, tok);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_sequence(const HParser *p, ...) {
|
|
||||||
va_list ap;
|
|
||||||
size_t len = 0;
|
|
||||||
const HParser *arg;
|
|
||||||
va_start(ap, p);
|
|
||||||
do {
|
|
||||||
len++;
|
|
||||||
arg = va_arg(ap, const HParser *);
|
|
||||||
} while (arg);
|
|
||||||
va_end(ap);
|
|
||||||
HSequence *s = g_new(HSequence, 1);
|
|
||||||
s->p_array = g_new(const HParser *, len);
|
|
||||||
|
|
||||||
va_start(ap, p);
|
|
||||||
s->p_array[0] = p;
|
|
||||||
for (size_t i = 1; i < len; i++) {
|
|
||||||
s->p_array[i] = va_arg(ap, const HParser *);
|
|
||||||
} while (arg);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
s->len = len;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_sequence; ret->env = (void*)s;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_choice(void *env, HParseState *state) {
|
|
||||||
HSequence *s = (HSequence*)env;
|
|
||||||
HInputStream backup = state->input_stream;
|
|
||||||
for (size_t i=0; i<s->len; ++i) {
|
|
||||||
if (i != 0)
|
|
||||||
state->input_stream = backup;
|
|
||||||
HParseResult *tmp = h_do_parse(s->p_array[i], state);
|
|
||||||
if (NULL != tmp)
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
// nothing succeeded, so fail
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_choice(const HParser* p, ...) {
|
|
||||||
va_list ap;
|
|
||||||
size_t len = 0;
|
|
||||||
HSequence *s = g_new(HSequence, 1);
|
|
||||||
|
|
||||||
const HParser *arg;
|
|
||||||
va_start(ap, p);
|
|
||||||
do {
|
|
||||||
len++;
|
|
||||||
arg = va_arg(ap, const HParser *);
|
|
||||||
} while (arg);
|
|
||||||
va_end(ap);
|
|
||||||
s->p_array = g_new(const HParser *, len);
|
|
||||||
|
|
||||||
va_start(ap, p);
|
|
||||||
s->p_array[0] = p;
|
|
||||||
for (size_t i = 1; i < len; i++) {
|
|
||||||
s->p_array[i] = va_arg(ap, const HParser *);
|
|
||||||
} while (arg);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
s->len = len;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_choice; ret->env = (void*)s;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const HParser *p1;
|
const HParser *p1;
|
||||||
const HParser *p2;
|
const HParser *p2;
|
||||||
} HTwoParsers;
|
} HTwoParsers;
|
||||||
|
|
||||||
// return token size in bits...
|
|
||||||
size_t token_length(HParseResult *pr) {
|
|
||||||
if (pr) {
|
|
||||||
return pr->bit_length;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_butnot(void *env, HParseState *state) {
|
|
||||||
HTwoParsers *parsers = (HTwoParsers*)env;
|
|
||||||
// cache the initial state of the input stream
|
|
||||||
HInputStream start_state = state->input_stream;
|
|
||||||
HParseResult *r1 = h_do_parse(parsers->p1, state);
|
|
||||||
// if p1 failed, bail out early
|
|
||||||
if (NULL == r1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
// cache the state after parse #1, since we might have to back up to it
|
|
||||||
HInputStream after_p1_state = state->input_stream;
|
|
||||||
state->input_stream = start_state;
|
|
||||||
HParseResult *r2 = h_do_parse(parsers->p2, state);
|
|
||||||
// TODO(mlp): I'm pretty sure the input stream state should be the post-p1 state in all cases
|
|
||||||
state->input_stream = after_p1_state;
|
|
||||||
// if p2 failed, restore post-p1 state and bail out early
|
|
||||||
if (NULL == r2) {
|
|
||||||
return r1;
|
|
||||||
}
|
|
||||||
size_t r1len = token_length(r1);
|
|
||||||
size_t r2len = token_length(r2);
|
|
||||||
// if both match but p1's text is shorter than than p2's (or the same length), fail
|
|
||||||
if (r1len <= r2len) {
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
return r1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_butnot(const HParser* p1, const HParser* p2) {
|
|
||||||
HTwoParsers *env = g_new(HTwoParsers, 1);
|
|
||||||
env->p1 = p1; env->p2 = p2;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_butnot; ret->env = (void*)env;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_difference(void *env, HParseState *state) {
|
|
||||||
HTwoParsers *parsers = (HTwoParsers*)env;
|
|
||||||
// cache the initial state of the input stream
|
|
||||||
HInputStream start_state = state->input_stream;
|
|
||||||
HParseResult *r1 = h_do_parse(parsers->p1, state);
|
|
||||||
// if p1 failed, bail out early
|
|
||||||
if (NULL == r1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
// cache the state after parse #1, since we might have to back up to it
|
|
||||||
HInputStream after_p1_state = state->input_stream;
|
|
||||||
state->input_stream = start_state;
|
|
||||||
HParseResult *r2 = h_do_parse(parsers->p2, state);
|
|
||||||
// TODO(mlp): I'm pretty sure the input stream state should be the post-p1 state in all cases
|
|
||||||
state->input_stream = after_p1_state;
|
|
||||||
// if p2 failed, restore post-p1 state and bail out early
|
|
||||||
if (NULL == r2) {
|
|
||||||
return r1;
|
|
||||||
}
|
|
||||||
size_t r1len = token_length(r1);
|
|
||||||
size_t r2len = token_length(r2);
|
|
||||||
// if both match but p1's text is shorter than p2's, fail
|
|
||||||
if (r1len < r2len) {
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
return r1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_difference(const HParser* p1, const HParser* p2) {
|
|
||||||
HTwoParsers *env = g_new(HTwoParsers, 1);
|
|
||||||
env->p1 = p1; env->p2 = p2;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_difference; ret->env = (void*)env;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_xor(void *env, HParseState *state) {
|
|
||||||
HTwoParsers *parsers = (HTwoParsers*)env;
|
|
||||||
// cache the initial state of the input stream
|
|
||||||
HInputStream start_state = state->input_stream;
|
|
||||||
HParseResult *r1 = h_do_parse(parsers->p1, state);
|
|
||||||
HInputStream after_p1_state = state->input_stream;
|
|
||||||
// reset input stream, parse again
|
|
||||||
state->input_stream = start_state;
|
|
||||||
HParseResult *r2 = h_do_parse(parsers->p2, state);
|
|
||||||
if (NULL == r1) {
|
|
||||||
if (NULL != r2) {
|
|
||||||
return r2;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (NULL == r2) {
|
|
||||||
state->input_stream = after_p1_state;
|
|
||||||
return r1;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_xor(const HParser* p1, const HParser* p2) {
|
|
||||||
HTwoParsers *env = g_new(HTwoParsers, 1);
|
|
||||||
env->p1 = p1; env->p2 = p2;
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_xor; ret->env = (void*)env;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const HParser *p, *sep;
|
|
||||||
size_t count;
|
|
||||||
bool min_p;
|
|
||||||
} HRepeat;
|
|
||||||
|
|
||||||
static HParseResult *parse_many(void* env, HParseState *state) {
|
|
||||||
HRepeat *env_ = (HRepeat*) env;
|
|
||||||
HCountedArray *seq = h_carray_new_sized(state->arena, (env_->count > 0 ? env_->count : 4));
|
|
||||||
size_t count = 0;
|
|
||||||
HInputStream bak;
|
|
||||||
while (env_->min_p || env_->count > count) {
|
|
||||||
bak = state->input_stream;
|
|
||||||
if (count > 0) {
|
|
||||||
HParseResult *sep = h_do_parse(env_->sep, state);
|
|
||||||
if (!sep)
|
|
||||||
goto err0;
|
|
||||||
}
|
|
||||||
HParseResult *elem = h_do_parse(env_->p, state);
|
|
||||||
if (!elem)
|
|
||||||
goto err0;
|
|
||||||
if (elem->ast)
|
|
||||||
h_carray_append(seq, (void*)elem->ast);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (count < env_->count)
|
|
||||||
goto err;
|
|
||||||
succ:
|
|
||||||
; // necessary for the label to be here...
|
|
||||||
HParsedToken *res = a_new(HParsedToken, 1);
|
|
||||||
res->token_type = TT_SEQUENCE;
|
|
||||||
res->seq = seq;
|
|
||||||
return make_result(state, res);
|
|
||||||
err0:
|
|
||||||
if (count >= env_->count) {
|
|
||||||
state->input_stream = bak;
|
|
||||||
goto succ;
|
|
||||||
}
|
|
||||||
err:
|
|
||||||
state->input_stream = bak;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_many(const HParser* p) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
HRepeat *env = g_new(HRepeat, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->sep = h_epsilon_p();
|
|
||||||
env->count = 0;
|
|
||||||
env->min_p = true;
|
|
||||||
res->fn = parse_many;
|
|
||||||
res->env = env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_many1(const HParser* p) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
HRepeat *env = g_new(HRepeat, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->sep = h_epsilon_p();
|
|
||||||
env->count = 1;
|
|
||||||
env->min_p = true;
|
|
||||||
res->fn = parse_many;
|
|
||||||
res->env = env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_repeat_n(const HParser* p, const size_t n) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
HRepeat *env = g_new(HRepeat, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->sep = h_epsilon_p();
|
|
||||||
env->count = n;
|
|
||||||
env->min_p = false;
|
|
||||||
res->fn = parse_many;
|
|
||||||
res->env = env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_ignore(void* env, HParseState* state) {
|
|
||||||
HParseResult *res0 = h_do_parse((HParser*)env, state);
|
|
||||||
if (!res0)
|
|
||||||
return NULL;
|
|
||||||
HParseResult *res = a_new(HParseResult, 1);
|
|
||||||
res->ast = NULL;
|
|
||||||
res->arena = state->arena;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
const HParser* h_ignore(const HParser* p) {
|
|
||||||
HParser* ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_ignore;
|
|
||||||
ret->env = (void*)p;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_optional(void* env, HParseState* state) {
|
|
||||||
HInputStream bak = state->input_stream;
|
|
||||||
HParseResult *res0 = h_do_parse((HParser*)env, state);
|
|
||||||
if (res0)
|
|
||||||
return res0;
|
|
||||||
state->input_stream = bak;
|
|
||||||
HParsedToken *ast = a_new(HParsedToken, 1);
|
|
||||||
ast->token_type = TT_NONE;
|
|
||||||
return make_result(state, ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_optional(const HParser* p) {
|
|
||||||
assert_message(p->fn != parse_ignore, "Thou shalt ignore an option, rather than the other way 'round.");
|
|
||||||
HParser *ret = g_new(HParser, 1);
|
|
||||||
ret->fn = parse_optional;
|
|
||||||
ret->env = (void*)p;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_sepBy(const HParser* p, const HParser* sep) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
HRepeat *env = g_new(HRepeat, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->sep = sep;
|
|
||||||
env->count = 0;
|
|
||||||
env->min_p = true;
|
|
||||||
res->fn = parse_many;
|
|
||||||
res->env = env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_sepBy1(const HParser* p, const HParser* sep) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
HRepeat *env = g_new(HRepeat, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->sep = sep;
|
|
||||||
env->count = 1;
|
|
||||||
env->min_p = true;
|
|
||||||
res->fn = parse_many;
|
|
||||||
res->env = env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_epsilon(void* env, HParseState* state) {
|
|
||||||
(void)env;
|
|
||||||
HParseResult* res = a_new(HParseResult, 1);
|
|
||||||
res->ast = NULL;
|
|
||||||
res->arena = state->arena;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_epsilon_p() {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_epsilon;
|
|
||||||
res->env = NULL;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_indirect(void* env, HParseState* state) {
|
|
||||||
return h_do_parse(env, state);
|
|
||||||
}
|
|
||||||
void h_bind_indirect(HParser* indirect, HParser* inner) {
|
|
||||||
indirect->env = inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
HParser* h_indirect() {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_indirect;
|
|
||||||
res->env = NULL;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const HParser *p;
|
|
||||||
HPredicate pred;
|
|
||||||
} HAttrBool;
|
|
||||||
|
|
||||||
static HParseResult* parse_attr_bool(void *env, HParseState *state) {
|
|
||||||
HAttrBool *a = (HAttrBool*)env;
|
|
||||||
HParseResult *res = h_do_parse(a->p, state);
|
|
||||||
if (res && res->ast) {
|
|
||||||
if (a->pred(res))
|
|
||||||
return res;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
} else
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_attr_bool(const HParser* p, HPredicate pred) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_attr_bool;
|
|
||||||
HAttrBool *env = g_new(HAttrBool, 1);
|
|
||||||
env->p = p;
|
|
||||||
env->pred = pred;
|
|
||||||
res->env = (void*)env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const HParser *length;
|
|
||||||
const HParser *value;
|
|
||||||
} HLenVal;
|
|
||||||
|
|
||||||
static HParseResult* parse_length_value(void *env, HParseState *state) {
|
|
||||||
HLenVal *lv = (HLenVal*)env;
|
|
||||||
HParseResult *len = h_do_parse(lv->length, state);
|
|
||||||
if (!len)
|
|
||||||
return NULL;
|
|
||||||
if (len->ast->token_type != TT_UINT)
|
|
||||||
errx(1, "Length parser must return an unsigned integer");
|
|
||||||
HParser epsilon_local = {
|
|
||||||
.fn = parse_epsilon,
|
|
||||||
.env = NULL
|
|
||||||
};
|
|
||||||
HRepeat repeat = {
|
|
||||||
.p = lv->value,
|
|
||||||
.sep = &epsilon_local,
|
|
||||||
.count = len->ast->uint,
|
|
||||||
.min_p = false
|
|
||||||
};
|
|
||||||
return parse_many(&repeat, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_length_value(const HParser* length, const HParser* value) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_length_value;
|
|
||||||
HLenVal *env = g_new(HLenVal, 1);
|
|
||||||
env->length = length;
|
|
||||||
env->value = value;
|
|
||||||
res->env = (void*)env;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult *parse_and(void* env, HParseState* state) {
|
|
||||||
HInputStream bak = state->input_stream;
|
|
||||||
HParseResult *res = h_do_parse((HParser*)env, state);
|
|
||||||
state->input_stream = bak;
|
|
||||||
if (res)
|
|
||||||
return make_result(state, NULL);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_and(const HParser* p) {
|
|
||||||
// zero-width postive lookahead
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->env = (void*)p;
|
|
||||||
res->fn = parse_and;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HParseResult* parse_not(void* env, HParseState* state) {
|
|
||||||
HInputStream bak = state->input_stream;
|
|
||||||
if (h_do_parse((HParser*)env, state))
|
|
||||||
return NULL;
|
|
||||||
else {
|
|
||||||
state->input_stream = bak;
|
|
||||||
return make_result(state, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HParser* h_not(const HParser* p) {
|
|
||||||
HParser *res = g_new(HParser, 1);
|
|
||||||
res->fn = parse_not;
|
|
||||||
res->env = (void*)p;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static guint cache_key_hash(gconstpointer key) {
|
static guint cache_key_hash(gconstpointer key) {
|
||||||
return djbhash(key, sizeof(HParserCacheKey));
|
return djbhash(key, sizeof(HParserCacheKey));
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,12 @@ typedef const HParsedToken* (*HAction)(const HParseResult *p);
|
||||||
*/
|
*/
|
||||||
typedef bool (*HPredicate)(HParseResult *p);
|
typedef bool (*HPredicate)(HParseResult *p);
|
||||||
|
|
||||||
|
typedef struct HParserVtable_ {
|
||||||
|
HParseResult* (*parse)(void *env, HParseState *state);
|
||||||
|
} HParserVtable;
|
||||||
|
|
||||||
typedef struct HParser_ {
|
typedef struct HParser_ {
|
||||||
HParseResult* (*fn)(void *env, HParseState *state);
|
const HParserVtable *vtable;
|
||||||
void *env;
|
void *env;
|
||||||
} HParser;
|
} HParser;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ HCountedArray *h_carray_new_sized(HArena * arena, size_t size);
|
||||||
HCountedArray *h_carray_new(HArena * arena);
|
HCountedArray *h_carray_new(HArena * arena);
|
||||||
void h_carray_append(HCountedArray *array, void* item);
|
void h_carray_append(HCountedArray *array, void* item);
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#define arena_malloc(a, s) malloc(s)
|
#define arena_malloc(a, s) malloc(s)
|
||||||
|
|
|
||||||
31
src/parsers/action.c
Normal file
31
src/parsers/action.c
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p;
|
||||||
|
HAction action;
|
||||||
|
} HParseAction;
|
||||||
|
|
||||||
|
static HParseResult* parse_action(void *env, HParseState *state) {
|
||||||
|
HParseAction *a = (HParseAction*)env;
|
||||||
|
if (a->p && a->action) {
|
||||||
|
HParseResult *tmp = h_do_parse(a->p, state);
|
||||||
|
//HParsedToken *tok = a->action(h_do_parse(a->p, state));
|
||||||
|
const HParsedToken *tok = a->action(tmp);
|
||||||
|
return make_result(state, (HParsedToken*)tok);
|
||||||
|
} else // either the parser's missing or the action's missing
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable action_vt = {
|
||||||
|
.parse = parse_action,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_action(const HParser* p, const HAction a) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->vtable = &action_vt;
|
||||||
|
HParseAction *env = g_new(HParseAction, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->action = a;
|
||||||
|
res->env = (void*)env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
22
src/parsers/and.c
Normal file
22
src/parsers/and.c
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult *parse_and(void* env, HParseState* state) {
|
||||||
|
HInputStream bak = state->input_stream;
|
||||||
|
HParseResult *res = h_do_parse((HParser*)env, state);
|
||||||
|
state->input_stream = bak;
|
||||||
|
if (res)
|
||||||
|
return make_result(state, NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable and_vt = {
|
||||||
|
.parse = parse_and,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_and(const HParser* p) {
|
||||||
|
// zero-width postive lookahead
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->env = (void*)p;
|
||||||
|
res->vtable = &and_vt;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
32
src/parsers/attr_bool.c
Normal file
32
src/parsers/attr_bool.c
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p;
|
||||||
|
HPredicate pred;
|
||||||
|
} HAttrBool;
|
||||||
|
|
||||||
|
static HParseResult* parse_attr_bool(void *env, HParseState *state) {
|
||||||
|
HAttrBool *a = (HAttrBool*)env;
|
||||||
|
HParseResult *res = h_do_parse(a->p, state);
|
||||||
|
if (res && res->ast) {
|
||||||
|
if (a->pred(res))
|
||||||
|
return res;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
} else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable attr_bool_vt = {
|
||||||
|
.parse = parse_attr_bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_attr_bool(const HParser* p, HPredicate pred) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->vtable = &attr_bool_vt;
|
||||||
|
HAttrBool *env = g_new(HAttrBool, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->pred = pred;
|
||||||
|
res->env = (void*)env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
43
src/parsers/bits.c
Normal file
43
src/parsers/bits.c
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
struct bits_env {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t signedp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static HParseResult* parse_bits(void* env, HParseState *state) {
|
||||||
|
struct bits_env *env_ = env;
|
||||||
|
HParsedToken *result = a_new(HParsedToken, 1);
|
||||||
|
result->token_type = (env_->signedp ? TT_SINT : TT_UINT);
|
||||||
|
if (env_->signedp)
|
||||||
|
result->sint = h_read_bits(&state->input_stream, env_->length, true);
|
||||||
|
else
|
||||||
|
result->uint = h_read_bits(&state->input_stream, env_->length, false);
|
||||||
|
return make_result(state, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable bits_vt = {
|
||||||
|
.parse = parse_bits,
|
||||||
|
};
|
||||||
|
const HParser* h_bits(size_t len, bool sign) {
|
||||||
|
struct bits_env *env = g_new(struct bits_env, 1);
|
||||||
|
env->length = len;
|
||||||
|
env->signedp = sign;
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->vtable = &bits_vt;
|
||||||
|
res->env = env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SIZED_BITS(name_pre, len, signedp) \
|
||||||
|
const HParser* h_##name_pre##len () { \
|
||||||
|
return h_bits(len, signedp); \
|
||||||
|
}
|
||||||
|
SIZED_BITS(int, 8, true)
|
||||||
|
SIZED_BITS(int, 16, true)
|
||||||
|
SIZED_BITS(int, 32, true)
|
||||||
|
SIZED_BITS(int, 64, true)
|
||||||
|
SIZED_BITS(uint, 8, false)
|
||||||
|
SIZED_BITS(uint, 16, false)
|
||||||
|
SIZED_BITS(uint, 32, false)
|
||||||
|
SIZED_BITS(uint, 64, false)
|
||||||
49
src/parsers/butnot.c
Normal file
49
src/parsers/butnot.c
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p1;
|
||||||
|
const HParser *p2;
|
||||||
|
} HTwoParsers;
|
||||||
|
|
||||||
|
|
||||||
|
static HParseResult* parse_butnot(void *env, HParseState *state) {
|
||||||
|
HTwoParsers *parsers = (HTwoParsers*)env;
|
||||||
|
// cache the initial state of the input stream
|
||||||
|
HInputStream start_state = state->input_stream;
|
||||||
|
HParseResult *r1 = h_do_parse(parsers->p1, state);
|
||||||
|
// if p1 failed, bail out early
|
||||||
|
if (NULL == r1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// cache the state after parse #1, since we might have to back up to it
|
||||||
|
HInputStream after_p1_state = state->input_stream;
|
||||||
|
state->input_stream = start_state;
|
||||||
|
HParseResult *r2 = h_do_parse(parsers->p2, state);
|
||||||
|
// TODO(mlp): I'm pretty sure the input stream state should be the post-p1 state in all cases
|
||||||
|
state->input_stream = after_p1_state;
|
||||||
|
// if p2 failed, restore post-p1 state and bail out early
|
||||||
|
if (NULL == r2) {
|
||||||
|
return r1;
|
||||||
|
}
|
||||||
|
size_t r1len = token_length(r1);
|
||||||
|
size_t r2len = token_length(r2);
|
||||||
|
// if both match but p1's text is shorter than than p2's (or the same length), fail
|
||||||
|
if (r1len <= r2len) {
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return r1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable butnot_vt = {
|
||||||
|
.parse = parse_butnot,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_butnot(const HParser* p1, const HParser* p2) {
|
||||||
|
HTwoParsers *env = g_new(HTwoParsers, 1);
|
||||||
|
env->p1 = p1; env->p2 = p2;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &butnot_vt; ret->env = (void*)env;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
23
src/parsers/ch.c
Normal file
23
src/parsers/ch.c
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_ch(void* env, HParseState *state) {
|
||||||
|
uint8_t c = (uint8_t)GPOINTER_TO_UINT(env);
|
||||||
|
uint8_t r = (uint8_t)h_read_bits(&state->input_stream, 8, false);
|
||||||
|
if (c == r) {
|
||||||
|
HParsedToken *tok = a_new(HParsedToken, 1);
|
||||||
|
tok->token_type = TT_UINT; tok->uint = r;
|
||||||
|
return make_result(state, tok);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable ch_vt = {
|
||||||
|
.parse = parse_ch,
|
||||||
|
};
|
||||||
|
const HParser* h_ch(const uint8_t c) {
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &ch_vt;
|
||||||
|
ret->env = GUINT_TO_POINTER(c);
|
||||||
|
return (const HParser*)ret;
|
||||||
|
}
|
||||||
43
src/parsers/charset.c
Normal file
43
src/parsers/charset.c
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
static HParseResult* parse_charset(void *env, HParseState *state) {
|
||||||
|
uint8_t in = h_read_bits(&state->input_stream, 8, false);
|
||||||
|
HCharset cs = (HCharset)env;
|
||||||
|
|
||||||
|
if (charset_isset(cs, in)) {
|
||||||
|
HParsedToken *tok = a_new(HParsedToken, 1);
|
||||||
|
tok->token_type = TT_UINT; tok->uint = in;
|
||||||
|
return make_result(state, tok);
|
||||||
|
} else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable charset_vt = {
|
||||||
|
.parse = parse_charset,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_ch_range(const uint8_t lower, const uint8_t upper) {
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
HCharset cs = new_charset();
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
charset_set(cs, i, (lower <= i) && (i <= upper));
|
||||||
|
ret->vtable = &charset_vt;
|
||||||
|
ret->env = (void*)cs;
|
||||||
|
return (const HParser*)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const HParser* h_not_in(const uint8_t *options, int count) {
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
HCharset cs = new_charset();
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
charset_set(cs, i, 1);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
charset_set(cs, options[i], 0);
|
||||||
|
|
||||||
|
ret->vtable = &charset_vt;
|
||||||
|
ret->env = (void*)cs;
|
||||||
|
return (const HParser*)ret;
|
||||||
|
}
|
||||||
|
|
||||||
53
src/parsers/choice.c
Normal file
53
src/parsers/choice.c
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t len;
|
||||||
|
const HParser **p_array;
|
||||||
|
} HSequence;
|
||||||
|
|
||||||
|
|
||||||
|
static HParseResult* parse_choice(void *env, HParseState *state) {
|
||||||
|
HSequence *s = (HSequence*)env;
|
||||||
|
HInputStream backup = state->input_stream;
|
||||||
|
for (size_t i=0; i<s->len; ++i) {
|
||||||
|
if (i != 0)
|
||||||
|
state->input_stream = backup;
|
||||||
|
HParseResult *tmp = h_do_parse(s->p_array[i], state);
|
||||||
|
if (NULL != tmp)
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
// nothing succeeded, so fail
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable choice_vt = {
|
||||||
|
.parse = parse_choice,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_choice(const HParser* p, ...) {
|
||||||
|
va_list ap;
|
||||||
|
size_t len = 0;
|
||||||
|
HSequence *s = g_new(HSequence, 1);
|
||||||
|
|
||||||
|
const HParser *arg;
|
||||||
|
va_start(ap, p);
|
||||||
|
do {
|
||||||
|
len++;
|
||||||
|
arg = va_arg(ap, const HParser *);
|
||||||
|
} while (arg);
|
||||||
|
va_end(ap);
|
||||||
|
s->p_array = g_new(const HParser *, len);
|
||||||
|
|
||||||
|
va_start(ap, p);
|
||||||
|
s->p_array[0] = p;
|
||||||
|
for (size_t i = 1; i < len; i++) {
|
||||||
|
s->p_array[i] = va_arg(ap, const HParser *);
|
||||||
|
} while (arg);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
s->len = len;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &choice_vt; ret->env = (void*)s;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
47
src/parsers/difference.c
Normal file
47
src/parsers/difference.c
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p1;
|
||||||
|
const HParser *p2;
|
||||||
|
} HTwoParsers;
|
||||||
|
|
||||||
|
static HParseResult* parse_difference(void *env, HParseState *state) {
|
||||||
|
HTwoParsers *parsers = (HTwoParsers*)env;
|
||||||
|
// cache the initial state of the input stream
|
||||||
|
HInputStream start_state = state->input_stream;
|
||||||
|
HParseResult *r1 = h_do_parse(parsers->p1, state);
|
||||||
|
// if p1 failed, bail out early
|
||||||
|
if (NULL == r1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// cache the state after parse #1, since we might have to back up to it
|
||||||
|
HInputStream after_p1_state = state->input_stream;
|
||||||
|
state->input_stream = start_state;
|
||||||
|
HParseResult *r2 = h_do_parse(parsers->p2, state);
|
||||||
|
// TODO(mlp): I'm pretty sure the input stream state should be the post-p1 state in all cases
|
||||||
|
state->input_stream = after_p1_state;
|
||||||
|
// if p2 failed, restore post-p1 state and bail out early
|
||||||
|
if (NULL == r2) {
|
||||||
|
return r1;
|
||||||
|
}
|
||||||
|
size_t r1len = token_length(r1);
|
||||||
|
size_t r2len = token_length(r2);
|
||||||
|
// if both match but p1's text is shorter than p2's, fail
|
||||||
|
if (r1len < r2len) {
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return r1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HParserVtable difference_vt = {
|
||||||
|
.parse = parse_difference,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_difference(const HParser* p1, const HParser* p2) {
|
||||||
|
HTwoParsers *env = g_new(HTwoParsers, 1);
|
||||||
|
env->p1 = p1; env->p2 = p2;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &difference_vt; ret->env = (void*)env;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
21
src/parsers/end.c
Normal file
21
src/parsers/end.c
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_end(void *env, HParseState *state) {
|
||||||
|
if (state->input_stream.index == state->input_stream.length) {
|
||||||
|
HParseResult *ret = a_new(HParseResult, 1);
|
||||||
|
ret->ast = NULL;
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable end_vt = {
|
||||||
|
.parse = parse_end,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_end_p() {
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &end_vt; ret->env = NULL;
|
||||||
|
return (const HParser*)ret;
|
||||||
|
}
|
||||||
22
src/parsers/epsilon.c
Normal file
22
src/parsers/epsilon.c
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_epsilon(void* env, HParseState* state) {
|
||||||
|
(void)env;
|
||||||
|
HParseResult* res = a_new(HParseResult, 1);
|
||||||
|
res->ast = NULL;
|
||||||
|
res->arena = state->arena;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable epsilon_vt = {
|
||||||
|
.parse = parse_epsilon,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const HParser epsilon_p = {
|
||||||
|
.vtable = &epsilon_vt,
|
||||||
|
.env = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_epsilon_p() {
|
||||||
|
return &epsilon_p;
|
||||||
|
}
|
||||||
22
src/parsers/ignore.c
Normal file
22
src/parsers/ignore.c
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_ignore(void* env, HParseState* state) {
|
||||||
|
HParseResult *res0 = h_do_parse((HParser*)env, state);
|
||||||
|
if (!res0)
|
||||||
|
return NULL;
|
||||||
|
HParseResult *res = a_new(HParseResult, 1);
|
||||||
|
res->ast = NULL;
|
||||||
|
res->arena = state->arena;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable ignore_vt = {
|
||||||
|
.parse = parse_ignore,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_ignore(const HParser* p) {
|
||||||
|
HParser* ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &ignore_vt;
|
||||||
|
ret->env = (void*)p;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
20
src/parsers/indirect.c
Normal file
20
src/parsers/indirect.c
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_indirect(void* env, HParseState* state) {
|
||||||
|
return h_do_parse(env, state);
|
||||||
|
}
|
||||||
|
static const HParserVtable indirect_vt = {
|
||||||
|
.parse = parse_indirect,
|
||||||
|
};
|
||||||
|
|
||||||
|
void h_bind_indirect(HParser* indirect, HParser* inner) {
|
||||||
|
assert_message(indirect->vtable == &indirect_vt, "You can only bind an indirect parser");
|
||||||
|
indirect->env = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
HParser* h_indirect() {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->vtable = &indirect_vt;
|
||||||
|
res->env = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
51
src/parsers/int_range.c
Normal file
51
src/parsers/int_range.c
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p;
|
||||||
|
int64_t lower;
|
||||||
|
int64_t upper;
|
||||||
|
} HRange;
|
||||||
|
|
||||||
|
static HParseResult* parse_int_range(void *env, HParseState *state) {
|
||||||
|
HRange *r_env = (HRange*)env;
|
||||||
|
HParseResult *ret = h_do_parse(r_env->p, state);
|
||||||
|
if (!ret || !ret->ast)
|
||||||
|
return NULL;
|
||||||
|
switch(ret->ast->token_type) {
|
||||||
|
case TT_SINT:
|
||||||
|
if (r_env->lower <= ret->ast->sint && r_env->upper >= ret->ast->sint)
|
||||||
|
return ret;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
case TT_UINT:
|
||||||
|
if ((uint64_t)r_env->lower <= ret->ast->uint && (uint64_t)r_env->upper >= ret->ast->uint)
|
||||||
|
return ret;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable int_range_vt = {
|
||||||
|
.parse = parse_int_range,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_int_range(const HParser *p, const int64_t lower, const int64_t upper) {
|
||||||
|
// p must be an integer parser, which means it's using parse_bits
|
||||||
|
// TODO: re-add this check
|
||||||
|
//assert_message(p->vtable == &bits_vt, "int_range requires an integer parser");
|
||||||
|
|
||||||
|
// and regardless, the bounds need to fit in the parser in question
|
||||||
|
// TODO: check this as well.
|
||||||
|
|
||||||
|
HRange *r_env = g_new(HRange, 1);
|
||||||
|
r_env->p = p;
|
||||||
|
r_env->lower = lower;
|
||||||
|
r_env->upper = upper;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &int_range_vt;
|
||||||
|
ret->env = (void*)r_env;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
145
src/parsers/many.c
Normal file
145
src/parsers/many.c
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
// TODO: split this up.
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p, *sep;
|
||||||
|
size_t count;
|
||||||
|
bool min_p;
|
||||||
|
} HRepeat;
|
||||||
|
|
||||||
|
static HParseResult *parse_many(void* env, HParseState *state) {
|
||||||
|
HRepeat *env_ = (HRepeat*) env;
|
||||||
|
HCountedArray *seq = h_carray_new_sized(state->arena, (env_->count > 0 ? env_->count : 4));
|
||||||
|
size_t count = 0;
|
||||||
|
HInputStream bak;
|
||||||
|
while (env_->min_p || env_->count > count) {
|
||||||
|
bak = state->input_stream;
|
||||||
|
if (count > 0) {
|
||||||
|
HParseResult *sep = h_do_parse(env_->sep, state);
|
||||||
|
if (!sep)
|
||||||
|
goto err0;
|
||||||
|
}
|
||||||
|
HParseResult *elem = h_do_parse(env_->p, state);
|
||||||
|
if (!elem)
|
||||||
|
goto err0;
|
||||||
|
if (elem->ast)
|
||||||
|
h_carray_append(seq, (void*)elem->ast);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count < env_->count)
|
||||||
|
goto err;
|
||||||
|
succ:
|
||||||
|
; // necessary for the label to be here...
|
||||||
|
HParsedToken *res = a_new(HParsedToken, 1);
|
||||||
|
res->token_type = TT_SEQUENCE;
|
||||||
|
res->seq = seq;
|
||||||
|
return make_result(state, res);
|
||||||
|
err0:
|
||||||
|
if (count >= env_->count) {
|
||||||
|
state->input_stream = bak;
|
||||||
|
goto succ;
|
||||||
|
}
|
||||||
|
err:
|
||||||
|
state->input_stream = bak;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable many_vt = {
|
||||||
|
.parse = parse_many,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_many(const HParser* p) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
HRepeat *env = g_new(HRepeat, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->sep = h_epsilon_p();
|
||||||
|
env->count = 0;
|
||||||
|
env->min_p = true;
|
||||||
|
res->vtable = &many_vt;
|
||||||
|
res->env = env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HParser* h_many1(const HParser* p) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
HRepeat *env = g_new(HRepeat, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->sep = h_epsilon_p();
|
||||||
|
env->count = 1;
|
||||||
|
env->min_p = true;
|
||||||
|
res->vtable = &many_vt;
|
||||||
|
res->env = env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HParser* h_repeat_n(const HParser* p, const size_t n) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
HRepeat *env = g_new(HRepeat, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->sep = h_epsilon_p();
|
||||||
|
env->count = n;
|
||||||
|
env->min_p = false;
|
||||||
|
res->vtable = &many_vt;
|
||||||
|
res->env = env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HParser* h_sepBy(const HParser* p, const HParser* sep) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
HRepeat *env = g_new(HRepeat, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->sep = sep;
|
||||||
|
env->count = 0;
|
||||||
|
env->min_p = true;
|
||||||
|
res->vtable = &many_vt;
|
||||||
|
res->env = env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HParser* h_sepBy1(const HParser* p, const HParser* sep) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
HRepeat *env = g_new(HRepeat, 1);
|
||||||
|
env->p = p;
|
||||||
|
env->sep = sep;
|
||||||
|
env->count = 1;
|
||||||
|
env->min_p = true;
|
||||||
|
res->vtable = &many_vt;
|
||||||
|
res->env = env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *length;
|
||||||
|
const HParser *value;
|
||||||
|
} HLenVal;
|
||||||
|
|
||||||
|
static HParseResult* parse_length_value(void *env, HParseState *state) {
|
||||||
|
HLenVal *lv = (HLenVal*)env;
|
||||||
|
HParseResult *len = h_do_parse(lv->length, state);
|
||||||
|
if (!len)
|
||||||
|
return NULL;
|
||||||
|
if (len->ast->token_type != TT_UINT)
|
||||||
|
errx(1, "Length parser must return an unsigned integer");
|
||||||
|
// TODO: allocate this using public functions
|
||||||
|
HRepeat repeat = {
|
||||||
|
.p = lv->value,
|
||||||
|
.sep = h_epsilon_p(),
|
||||||
|
.count = len->ast->uint,
|
||||||
|
.min_p = false
|
||||||
|
};
|
||||||
|
return parse_many(&repeat, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable length_value_vt = {
|
||||||
|
.parse = parse_length_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_length_value(const HParser* length, const HParser* value) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->vtable = &length_value_vt;
|
||||||
|
HLenVal *env = g_new(HLenVal, 1);
|
||||||
|
env->length = length;
|
||||||
|
env->value = value;
|
||||||
|
res->env = (void*)env;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
22
src/parsers/not.c
Normal file
22
src/parsers/not.c
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_not(void* env, HParseState* state) {
|
||||||
|
HInputStream bak = state->input_stream;
|
||||||
|
if (h_do_parse((HParser*)env, state))
|
||||||
|
return NULL;
|
||||||
|
else {
|
||||||
|
state->input_stream = bak;
|
||||||
|
return make_result(state, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable not_vt = {
|
||||||
|
.parse = parse_not,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_not(const HParser* p) {
|
||||||
|
HParser *res = g_new(HParser, 1);
|
||||||
|
res->vtable = ¬_vt;
|
||||||
|
res->env = (void*)p;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
17
src/parsers/nothing.c
Normal file
17
src/parsers/nothing.c
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
static HParseResult* parse_nothing() {
|
||||||
|
// not a mistake, this parser always fails
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable nothing_vt = {
|
||||||
|
.parse = parse_nothing,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_nothing_p() {
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = ¬hing_vt; ret->env = NULL;
|
||||||
|
return (const HParser*)ret;
|
||||||
|
}
|
||||||
26
src/parsers/optional.c
Normal file
26
src/parsers/optional.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_optional(void* env, HParseState* state) {
|
||||||
|
HInputStream bak = state->input_stream;
|
||||||
|
HParseResult *res0 = h_do_parse((HParser*)env, state);
|
||||||
|
if (res0)
|
||||||
|
return res0;
|
||||||
|
state->input_stream = bak;
|
||||||
|
HParsedToken *ast = a_new(HParsedToken, 1);
|
||||||
|
ast->token_type = TT_NONE;
|
||||||
|
return make_result(state, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable optional_vt = {
|
||||||
|
.parse = parse_optional,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_optional(const HParser* p) {
|
||||||
|
// TODO: re-add this
|
||||||
|
//assert_message(p->vtable != &ignore_vt, "Thou shalt ignore an option, rather than the other way 'round.");
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &optional_vt;
|
||||||
|
ret->env = (void*)p;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
27
src/parsers/parser_internal.h
Normal file
27
src/parsers/parser_internal.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef HAMMER_PARSE_INTERNAL__H
|
||||||
|
#define HAMMER_PARSE_INTERNAL__H
|
||||||
|
#include "../hammer.h"
|
||||||
|
#include "../internal.h"
|
||||||
|
|
||||||
|
#define a_new_(arena, typ, count) ((typ*)h_arena_malloc((arena), sizeof(typ)*(count)))
|
||||||
|
#define a_new(typ, count) a_new_(state->arena, typ, count)
|
||||||
|
// we can create a_new0 if necessary. It would allocate some memory and immediately zero it out.
|
||||||
|
|
||||||
|
static inline HParseResult* make_result(HParseState *state, HParsedToken *tok) {
|
||||||
|
HParseResult *ret = a_new(HParseResult, 1);
|
||||||
|
ret->ast = tok;
|
||||||
|
ret->arena = state->arena;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return token size in bits...
|
||||||
|
static inline size_t token_length(HParseResult *pr) {
|
||||||
|
if (pr) {
|
||||||
|
return pr->bit_length;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // HAMMER_PARSE_INTERNAL__H
|
||||||
54
src/parsers/sequence.c
Normal file
54
src/parsers/sequence.c
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t len;
|
||||||
|
const HParser **p_array;
|
||||||
|
} HSequence;
|
||||||
|
|
||||||
|
static HParseResult* parse_sequence(void *env, HParseState *state) {
|
||||||
|
HSequence *s = (HSequence*)env;
|
||||||
|
HCountedArray *seq = h_carray_new_sized(state->arena, (s->len > 0) ? s->len : 4);
|
||||||
|
for (size_t i=0; i<s->len; ++i) {
|
||||||
|
HParseResult *tmp = h_do_parse(s->p_array[i], state);
|
||||||
|
// if the interim parse fails, the whole thing fails
|
||||||
|
if (NULL == tmp) {
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
if (tmp->ast)
|
||||||
|
h_carray_append(seq, (void*)tmp->ast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HParsedToken *tok = a_new(HParsedToken, 1);
|
||||||
|
tok->token_type = TT_SEQUENCE; tok->seq = seq;
|
||||||
|
return make_result(state, tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable sequence_vt = {
|
||||||
|
.parse = parse_sequence,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_sequence(const HParser *p, ...) {
|
||||||
|
va_list ap;
|
||||||
|
size_t len = 0;
|
||||||
|
const HParser *arg;
|
||||||
|
va_start(ap, p);
|
||||||
|
do {
|
||||||
|
len++;
|
||||||
|
arg = va_arg(ap, const HParser *);
|
||||||
|
} while (arg);
|
||||||
|
va_end(ap);
|
||||||
|
HSequence *s = g_new(HSequence, 1);
|
||||||
|
s->p_array = g_new(const HParser *, len);
|
||||||
|
|
||||||
|
va_start(ap, p);
|
||||||
|
s->p_array[0] = p;
|
||||||
|
for (size_t i = 1; i < len; i++) {
|
||||||
|
s->p_array[i] = va_arg(ap, const HParser *);
|
||||||
|
} while (arg);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
s->len = len;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &sequence_vt; ret->env = (void*)s;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
1
src/parsers/template.c
Normal file
1
src/parsers/template.c
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
34
src/parsers/token.c
Normal file
34
src/parsers/token.c
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *str;
|
||||||
|
uint8_t len;
|
||||||
|
} HToken;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static HParseResult* parse_token(void *env, HParseState *state) {
|
||||||
|
HToken *t = (HToken*)env;
|
||||||
|
for (int i=0; i<t->len; ++i) {
|
||||||
|
uint8_t chr = (uint8_t)h_read_bits(&state->input_stream, 8, false);
|
||||||
|
if (t->str[i] != chr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HParsedToken *tok = a_new(HParsedToken, 1);
|
||||||
|
tok->token_type = TT_BYTES; tok->bytes.token = t->str; tok->bytes.len = t->len;
|
||||||
|
return make_result(state, tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
const const HParserVtable token_vt = {
|
||||||
|
.parse = parse_token,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_token(const uint8_t *str, const size_t len) {
|
||||||
|
HToken *t = g_new(HToken, 1);
|
||||||
|
t->str = (uint8_t*)str, t->len = len;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &token_vt;
|
||||||
|
ret->env = t;
|
||||||
|
return (const HParser*)ret;
|
||||||
|
}
|
||||||
26
src/parsers/unimplemented.c
Normal file
26
src/parsers/unimplemented.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_unimplemented(void* env, HParseState *state) {
|
||||||
|
(void) env;
|
||||||
|
(void) state;
|
||||||
|
static HParsedToken token = {
|
||||||
|
.token_type = TT_ERR
|
||||||
|
};
|
||||||
|
static HParseResult result = {
|
||||||
|
.ast = &token
|
||||||
|
};
|
||||||
|
return &result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable unimplemented_vt = {
|
||||||
|
.parse = parse_unimplemented,
|
||||||
|
};
|
||||||
|
|
||||||
|
static HParser unimplemented = {
|
||||||
|
.vtable = &unimplemented_vt,
|
||||||
|
.env = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_unimplemented() {
|
||||||
|
return &unimplemented;
|
||||||
|
}
|
||||||
26
src/parsers/whitespace.c
Normal file
26
src/parsers/whitespace.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
static HParseResult* parse_whitespace(void* env, HParseState *state) {
|
||||||
|
char c;
|
||||||
|
HInputStream bak;
|
||||||
|
do {
|
||||||
|
bak = state->input_stream;
|
||||||
|
c = h_read_bits(&state->input_stream, 8, false);
|
||||||
|
if (state->input_stream.overrun)
|
||||||
|
return NULL;
|
||||||
|
} while (isspace(c));
|
||||||
|
state->input_stream = bak;
|
||||||
|
return h_do_parse((HParser*)env, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable whitespace_vt = {
|
||||||
|
.parse = parse_whitespace,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_whitespace(const HParser* p) {
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &whitespace_vt;
|
||||||
|
ret->env = (void*)p;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
44
src/parsers/xor.c
Normal file
44
src/parsers/xor.c
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include "parser_internal.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HParser *p1;
|
||||||
|
const HParser *p2;
|
||||||
|
} HTwoParsers;
|
||||||
|
|
||||||
|
|
||||||
|
static HParseResult* parse_xor(void *env, HParseState *state) {
|
||||||
|
HTwoParsers *parsers = (HTwoParsers*)env;
|
||||||
|
// cache the initial state of the input stream
|
||||||
|
HInputStream start_state = state->input_stream;
|
||||||
|
HParseResult *r1 = h_do_parse(parsers->p1, state);
|
||||||
|
HInputStream after_p1_state = state->input_stream;
|
||||||
|
// reset input stream, parse again
|
||||||
|
state->input_stream = start_state;
|
||||||
|
HParseResult *r2 = h_do_parse(parsers->p2, state);
|
||||||
|
if (NULL == r1) {
|
||||||
|
if (NULL != r2) {
|
||||||
|
return r2;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (NULL == r2) {
|
||||||
|
state->input_stream = after_p1_state;
|
||||||
|
return r1;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HParserVtable xor_vt = {
|
||||||
|
.parse = parse_xor,
|
||||||
|
};
|
||||||
|
|
||||||
|
const HParser* h_xor(const HParser* p1, const HParser* p2) {
|
||||||
|
HTwoParsers *env = g_new(HTwoParsers, 1);
|
||||||
|
env->p1 = p1; env->p2 = p2;
|
||||||
|
HParser *ret = g_new(HParser, 1);
|
||||||
|
ret->vtable = &xor_vt; ret->env = (void*)env;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue