Merge remote-tracking branch 'mlp/master' into LL
This commit is contained in:
commit
0b98b19ef0
44 changed files with 2785 additions and 992 deletions
|
|
@ -41,7 +41,7 @@ HAMMER_PARTS := \
|
|||
system_allocator.o \
|
||||
benchmark.o \
|
||||
cfgrammar.o \
|
||||
actions.o \
|
||||
glue.o \
|
||||
$(PARSERS:%=parsers/%.o) \
|
||||
$(BACKENDS:%=backends/%.o)
|
||||
|
||||
|
|
@ -50,6 +50,7 @@ TESTS := t_benchmark.o \
|
|||
t_bitwriter.o \
|
||||
t_parser.o \
|
||||
t_grammar.o \
|
||||
t_misc.o \
|
||||
test_suite.o
|
||||
|
||||
OUTPUTS := libhammer.a \
|
||||
|
|
@ -65,12 +66,15 @@ include ../common.mk
|
|||
$(TESTS): CFLAGS += $(TEST_CFLAGS)
|
||||
$(TESTS): LDFLAGS += $(TEST_LDFLAGS)
|
||||
|
||||
CFLAGS += -fPIC
|
||||
|
||||
all: libhammer.a
|
||||
|
||||
libhammer.a: $(HAMMER_PARTS)
|
||||
|
||||
bitreader.o: test_suite.h
|
||||
hammer.o: hammer.h
|
||||
glue.o: hammer.h glue.h
|
||||
|
||||
all: libhammer.a
|
||||
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
// XXX to be consolidated with glue.c when merged upstream
|
||||
|
||||
#include <assert.h>
|
||||
#include "hammer.h"
|
||||
#include "internal.h"
|
||||
#include "parsers/parser_internal.h"
|
||||
|
||||
|
||||
const HParsedToken *h_act_first(const HParseResult *p) {
|
||||
assert(p->ast);
|
||||
assert(p->ast->token_type == TT_SEQUENCE);
|
||||
assert(p->ast->seq->used > 0);
|
||||
|
||||
return p->ast->seq->elements[0];
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_second(const HParseResult *p) {
|
||||
assert(p->ast);
|
||||
assert(p->ast->token_type == TT_SEQUENCE);
|
||||
assert(p->ast->seq->used > 0);
|
||||
|
||||
return p->ast->seq->elements[1];
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_last(const HParseResult *p) {
|
||||
assert(p->ast);
|
||||
assert(p->ast->token_type == TT_SEQUENCE);
|
||||
assert(p->ast->seq->used > 0);
|
||||
|
||||
return p->ast->seq->elements[p->ast->seq->used-1];
|
||||
}
|
||||
|
||||
static void act_flatten_(HCountedArray *seq, const HParsedToken *tok) {
|
||||
if(tok == NULL) {
|
||||
return;
|
||||
} else if(tok->token_type == TT_SEQUENCE) {
|
||||
size_t i;
|
||||
for(i=0; i<tok->seq->used; i++)
|
||||
act_flatten_(seq, tok->seq->elements[i]);
|
||||
} else {
|
||||
h_carray_append(seq, (HParsedToken *)tok);
|
||||
}
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_flatten(const HParseResult *p) {
|
||||
HCountedArray *seq = h_carray_new(p->arena);
|
||||
|
||||
act_flatten_(seq, p->ast);
|
||||
|
||||
HParsedToken *res = a_new_(p->arena, HParsedToken, 1);
|
||||
res->token_type = TT_SEQUENCE;
|
||||
res->seq = seq;
|
||||
res->index = p->ast->index;
|
||||
res->bit_offset = p->ast->bit_offset;
|
||||
return res;
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_ignore(const HParseResult *p) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -86,14 +86,18 @@ HParserCacheValue* recall(HParserCacheKey *k, HParseState *state) {
|
|||
void setupLR(const HParser *p, HParseState *state, HLeftRec *rec_detect) {
|
||||
if (!rec_detect->head) {
|
||||
HRecursionHead *some = a_new(HRecursionHead, 1);
|
||||
some->head_parser = p; some->involved_set = NULL; some->eval_set = NULL;
|
||||
some->head_parser = p;
|
||||
some->involved_set = h_slist_new(state->arena);
|
||||
some->eval_set = NULL;
|
||||
rec_detect->head = some;
|
||||
}
|
||||
assert(state->lr_stack->head != NULL);
|
||||
HLeftRec *lr = state->lr_stack->head->elem;
|
||||
while (lr && lr->rule != p) {
|
||||
HSlistNode *head = state->lr_stack->head;
|
||||
HLeftRec *lr;
|
||||
while (head && (lr = head->elem)->rule != p) {
|
||||
lr->head = rec_detect->head;
|
||||
h_slist_push(lr->head->involved_set, (void*)lr->rule);
|
||||
head = head->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +114,7 @@ HParseResult* grow(HParserCacheKey *k, HParseState *state, HRecursionHead *head)
|
|||
HParseResult *old_res = old_cached->right->result;
|
||||
|
||||
// reset the eval_set of the head of the recursion at each beginning of growth
|
||||
head->eval_set = head->involved_set;
|
||||
head->eval_set = h_slist_copy(head->involved_set);
|
||||
HParseResult *tmp_res = perform_lowlevel_parse(state, k->parser);
|
||||
|
||||
if (tmp_res) {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,26 @@ HSlist* h_slist_new(HArena *arena) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
HSlist* h_slist_copy(HSlist *slist) {
|
||||
HSlist *ret = h_slist_new(slist->arena);
|
||||
HSlistNode *head = slist->head;
|
||||
HSlistNode *tail;
|
||||
if (head != NULL) {
|
||||
h_slist_push(ret, head->elem);
|
||||
tail = ret->head;
|
||||
head = head->next;
|
||||
}
|
||||
while (head != NULL) {
|
||||
// append head item to tail in a new node
|
||||
HSlistNode *node = h_arena_malloc(slist->arena, sizeof(HSlistNode));
|
||||
node->elem = head->elem;
|
||||
node->next = NULL;
|
||||
tail = tail->next = node;
|
||||
head = head->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* h_slist_pop(HSlist *slist) {
|
||||
HSlistNode *head = slist->head;
|
||||
if (!head)
|
||||
|
|
|
|||
221
src/glue.c
Normal file
221
src/glue.c
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
#include <assert.h>
|
||||
#include "glue.h"
|
||||
#include "hammer.h"
|
||||
#include "internal.h" // for h_carray_*
|
||||
#include "parsers/parser_internal.h"
|
||||
|
||||
// Helper to build HAction's that pick one index out of a sequence.
|
||||
const HParsedToken *h_act_index(int i, const HParseResult *p)
|
||||
{
|
||||
if(!p) return NULL;
|
||||
|
||||
const HParsedToken *tok = p->ast;
|
||||
|
||||
if(!tok || tok->token_type != TT_SEQUENCE)
|
||||
return NULL;
|
||||
|
||||
const HCountedArray *seq = tok->seq;
|
||||
size_t n = seq->used;
|
||||
|
||||
if(i<0 || (size_t)i>=n)
|
||||
return NULL;
|
||||
else
|
||||
return tok->seq->elements[i];
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_first(const HParseResult *p) {
|
||||
assert(p->ast);
|
||||
assert(p->ast->token_type == TT_SEQUENCE);
|
||||
assert(p->ast->seq->used > 0);
|
||||
|
||||
return p->ast->seq->elements[0];
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_second(const HParseResult *p) {
|
||||
assert(p->ast);
|
||||
assert(p->ast->token_type == TT_SEQUENCE);
|
||||
assert(p->ast->seq->used > 0);
|
||||
|
||||
return p->ast->seq->elements[1];
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_last(const HParseResult *p) {
|
||||
assert(p->ast);
|
||||
assert(p->ast->token_type == TT_SEQUENCE);
|
||||
assert(p->ast->seq->used > 0);
|
||||
|
||||
return p->ast->seq->elements[p->ast->seq->used-1];
|
||||
}
|
||||
|
||||
static void act_flatten_(HCountedArray *seq, const HParsedToken *tok) {
|
||||
if(tok == NULL) {
|
||||
return;
|
||||
} else if(tok->token_type == TT_SEQUENCE) {
|
||||
size_t i;
|
||||
for(i=0; i<tok->seq->used; i++)
|
||||
act_flatten_(seq, tok->seq->elements[i]);
|
||||
} else {
|
||||
h_carray_append(seq, (HParsedToken *)tok);
|
||||
}
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_flatten(const HParseResult *p) {
|
||||
HCountedArray *seq = h_carray_new(p->arena);
|
||||
|
||||
act_flatten_(seq, p->ast);
|
||||
|
||||
HParsedToken *res = a_new_(p->arena, HParsedToken, 1);
|
||||
res->token_type = TT_SEQUENCE;
|
||||
res->seq = seq;
|
||||
res->index = p->ast->index;
|
||||
res->bit_offset = p->ast->bit_offset;
|
||||
return res;
|
||||
}
|
||||
|
||||
const HParsedToken *h_act_ignore(const HParseResult *p) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Low-level helper for the h_make family.
|
||||
HParsedToken *h_make_(HArena *arena, HTokenType type)
|
||||
{
|
||||
HParsedToken *ret = h_arena_malloc(arena, sizeof(HParsedToken));
|
||||
ret->token_type = type;
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_make(HArena *arena, HTokenType type, void *value)
|
||||
{
|
||||
assert(type >= TT_USER);
|
||||
HParsedToken *ret = h_make_(arena, type);
|
||||
ret->user = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_make_seq(HArena *arena)
|
||||
{
|
||||
HParsedToken *ret = h_make_(arena, TT_SEQUENCE);
|
||||
ret->seq = h_carray_new(arena);
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_make_seqn(HArena *arena, size_t n)
|
||||
{
|
||||
HParsedToken *ret = h_make_(arena, TT_SEQUENCE);
|
||||
ret->seq = h_carray_new_sized(arena, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_make_bytes(HArena *arena, size_t len)
|
||||
{
|
||||
HParsedToken *ret = h_make_(arena, TT_BYTES);
|
||||
ret->bytes.len = len;
|
||||
ret->bytes.token = h_arena_malloc(arena, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_make_sint(HArena *arena, int64_t val)
|
||||
{
|
||||
HParsedToken *ret = h_make_(arena, TT_SINT);
|
||||
ret->sint = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_make_uint(HArena *arena, uint64_t val)
|
||||
{
|
||||
HParsedToken *ret = h_make_(arena, TT_UINT);
|
||||
ret->uint = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// XXX -> internal
|
||||
HParsedToken *h_carray_index(const HCountedArray *a, size_t i)
|
||||
{
|
||||
assert(i < a->used);
|
||||
return a->elements[i];
|
||||
}
|
||||
|
||||
size_t h_seq_len(const HParsedToken *p)
|
||||
{
|
||||
assert(p != NULL);
|
||||
assert(p->token_type == TT_SEQUENCE);
|
||||
return p->seq->used;
|
||||
}
|
||||
|
||||
HParsedToken **h_seq_elements(const HParsedToken *p)
|
||||
{
|
||||
assert(p != NULL);
|
||||
assert(p->token_type == TT_SEQUENCE);
|
||||
return p->seq->elements;
|
||||
}
|
||||
|
||||
HParsedToken *h_seq_index(const HParsedToken *p, size_t i)
|
||||
{
|
||||
assert(p != NULL);
|
||||
assert(p->token_type == TT_SEQUENCE);
|
||||
return h_carray_index(p->seq, i);
|
||||
}
|
||||
|
||||
HParsedToken *h_seq_index_path(const HParsedToken *p, size_t i, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, i);
|
||||
HParsedToken *ret = h_seq_index_vpath(p, i, va);
|
||||
va_end(va);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
HParsedToken *h_seq_index_vpath(const HParsedToken *p, size_t i, va_list va)
|
||||
{
|
||||
HParsedToken *ret = h_seq_index(p, i);
|
||||
int j;
|
||||
|
||||
while((j = va_arg(va, int)) >= 0)
|
||||
ret = h_seq_index(p, j);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void h_seq_snoc(HParsedToken *xs, const HParsedToken *x)
|
||||
{
|
||||
assert(xs != NULL);
|
||||
assert(xs->token_type == TT_SEQUENCE);
|
||||
|
||||
h_carray_append(xs->seq, (HParsedToken *)x);
|
||||
}
|
||||
|
||||
void h_seq_append(HParsedToken *xs, const HParsedToken *ys)
|
||||
{
|
||||
assert(xs != NULL);
|
||||
assert(xs->token_type == TT_SEQUENCE);
|
||||
assert(ys != NULL);
|
||||
assert(ys->token_type == TT_SEQUENCE);
|
||||
|
||||
for(size_t i=0; i<ys->seq->used; i++)
|
||||
h_carray_append(xs->seq, ys->seq->elements[i]);
|
||||
}
|
||||
|
||||
// Flatten nested sequences. Always returns a sequence.
|
||||
// If input element is not a sequence, returns it as a singleton sequence.
|
||||
const HParsedToken *h_seq_flatten(HArena *arena, const HParsedToken *p)
|
||||
{
|
||||
assert(p != NULL);
|
||||
|
||||
HParsedToken *ret = h_make_seq(arena);
|
||||
switch(p->token_type) {
|
||||
case TT_SEQUENCE:
|
||||
// Flatten and append all.
|
||||
for(size_t i; i<p->seq->used; i++) {
|
||||
h_seq_append(ret, h_seq_flatten(arena, h_seq_index(p, i)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Make singleton sequence.
|
||||
h_seq_snoc(ret, p);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
256
src/glue.h
Normal file
256
src/glue.h
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
//
|
||||
// API additions for writing grammar and semantic actions more concisely
|
||||
//
|
||||
//
|
||||
// Quick Overview:
|
||||
//
|
||||
// Grammars can be succinctly specified with the family of H_RULE macros.
|
||||
// H_RULE defines a plain parser variable. H_ARULE additionally attaches a
|
||||
// semantic action; H_VRULE attaches a validation. H_AVRULE and H_VARULE
|
||||
// combine both.
|
||||
//
|
||||
// A few standard semantic actions are defined below. The H_ACT_APPLY macro
|
||||
// allows semantic actions to be defined by "partial application" of
|
||||
// a generic action to fixed paramters.
|
||||
//
|
||||
// The definition of more complex semantic actions will usually consist of
|
||||
// extracting data from the given parse tree and constructing a token of custom
|
||||
// type to represent the result. A number of functions and convenience macros
|
||||
// are provided to capture the most common cases and idioms.
|
||||
//
|
||||
// See the leading comment blocks on the sections below for more details.
|
||||
//
|
||||
|
||||
#ifndef HAMMER_GLUE__H
|
||||
#define HAMMER_GLUE__H
|
||||
|
||||
#include <assert.h>
|
||||
#include "hammer.h"
|
||||
|
||||
|
||||
//
|
||||
// Grammar specification
|
||||
//
|
||||
// H_RULE is simply a short-hand for the typical declaration and definition of
|
||||
// a parser variable. See its plain definition below. The goal is to save
|
||||
// horizontal space as well as to provide a clear and unified look together with
|
||||
// the other macro variants that stays close to an abstract PEG or BNF grammar.
|
||||
// The latter goal is more specifically enabled by H_ARULE, H_VRULE, and their
|
||||
// combinations as they allow the definition of syntax to be given without
|
||||
// intermingling it with the semantic specifications.
|
||||
//
|
||||
// H_ARULE defines a variable just like H_RULE but attaches a semantic action
|
||||
// to the result of the parser via h_action. The action is expected to be
|
||||
// named act_<rulename>.
|
||||
//
|
||||
// H_VRULE is analogous to H_ARULE but attaches a validation via h_attr_bool.
|
||||
// The validation is expected to be named validate_<rulename>.
|
||||
//
|
||||
// H_VARULE combines H_RULE with both an action and a validation. The action is
|
||||
// attached before the validation, i.e. the validation receives as input the
|
||||
// result of the action.
|
||||
//
|
||||
// H_AVRULE is like H_VARULE but the action is attached outside the validation,
|
||||
// i.e. the validation receives the uninterpreted AST as input.
|
||||
//
|
||||
|
||||
|
||||
#define H_RULE(rule, def) const HParser *rule = def
|
||||
#define H_ARULE(rule, def) const HParser *rule = h_action(def, act_ ## rule)
|
||||
#define H_VRULE(rule, def) const HParser *rule = \
|
||||
h_attr_bool(def, validate_ ## rule)
|
||||
#define H_VARULE(rule, def) const HParser *rule = \
|
||||
h_attr_bool(h_action(def, act_ ## rule), validate_ ## rule)
|
||||
#define H_AVRULE(rule, def) const HParser *rule = \
|
||||
h_action(h_attr_bool(def, validate_ ## rule), act_ ## rule)
|
||||
|
||||
|
||||
//
|
||||
// Pre-fab semantic actions
|
||||
//
|
||||
// A collection of generally useful semantic actions is provided.
|
||||
//
|
||||
// h_act_ignore is the action equivalent of the parser combinator h_ignore. It
|
||||
// simply causes the AST it is applied to to be replaced with NULL. This most
|
||||
// importantly causes it to be elided from the result of a surrounding
|
||||
// h_sequence.
|
||||
//
|
||||
// h_act_index is of note as it is not itself suitable to be passed to
|
||||
// h_action. It is parameterized by an index to be picked from a sequence
|
||||
// token. It must be wrapped in a proper HAction to be used. The H_ACT_APPLY
|
||||
// macro provides a concise way to define such a parameter-application wrapper.
|
||||
//
|
||||
// h_act_flatten acts on a token of possibly nested sequences by recursively
|
||||
// flattening it into a single sequence. Cf. h_seq_flatten below.
|
||||
//
|
||||
// H_ACT_APPLY implements "partial application" for semantic actions. It
|
||||
// defines a new action that supplies given parameters to a parameterized
|
||||
// action such as h_act_index.
|
||||
//
|
||||
|
||||
const HParsedToken *h_act_index(int i, const HParseResult *p);
|
||||
const HParsedToken *h_act_first(const HParseResult *p);
|
||||
const HParsedToken *h_act_second(const HParseResult *p);
|
||||
const HParsedToken *h_act_last(const HParseResult *p);
|
||||
const HParsedToken *h_act_flatten(const HParseResult *p);
|
||||
const HParsedToken *h_act_ignore(const HParseResult *p);
|
||||
|
||||
// Define 'myaction' as a specialization of 'paction' by supplying the leading
|
||||
// parameters.
|
||||
#define H_ACT_APPLY(myaction, paction, ...) \
|
||||
const HParsedToken *myaction(const HParseResult *p) { \
|
||||
return paction(__VA_ARGS__, p); \
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Working with HParsedTokens
|
||||
//
|
||||
// The type HParsedToken represents a dynamically-typed universe of values.
|
||||
// Declared below are constructors to turn ordinary values into their
|
||||
// HParsedToken equivalents, extractors to retrieve the original values from
|
||||
// inside an HParsedToken, and functions that inspect and modify tokens of
|
||||
// sequence type directly.
|
||||
//
|
||||
// In addition, there are a number of short-hand macros that work with some
|
||||
// conventions to eliminate common boilerplate. These conventions are listed
|
||||
// below. Be sure to follow them if you want to use the respective macros.
|
||||
//
|
||||
// * The single argument to semantic actions should be called 'p'.
|
||||
//
|
||||
// The H_MAKE macros suppy 'p->arena' to their underlying h_make
|
||||
// counterparts. The H_FIELD macros supply 'p->ast' to their underlying
|
||||
// H_INDEX counterparts.
|
||||
//
|
||||
// * For each custom token type, there should be a typedef for the
|
||||
// corresponding value type.
|
||||
//
|
||||
// H_CAST, H_INDEX and H_FIELD cast the void * user field of such a token to
|
||||
// a pointer to the given type.
|
||||
//
|
||||
// * For each custom token type, say 'foo_t', there must be an integer
|
||||
// constant 'TT_foo_t' to identify the token type. This constant must have a
|
||||
// value greater or equal than TT_USER.
|
||||
//
|
||||
// One idiom is to define an enum for all custom token types and to assign a
|
||||
// value of TT_USER to the first element. This can be viewed as extending
|
||||
// the HTokenType enum.
|
||||
//
|
||||
// The H_MAKE and H_ASSERT macros derive the name of the token type constant
|
||||
// from the given type name.
|
||||
//
|
||||
//
|
||||
// The H_ALLOC macro is useful for allocating values of custom token types.
|
||||
//
|
||||
// The H_MAKE family of macros construct tokens of a given type. The native
|
||||
// token types are indicated by a corresponding suffix such as in H_MAKE_SEQ.
|
||||
// The form with no suffix is used for custom token types. This convention is
|
||||
// also used for other macro and function families.
|
||||
//
|
||||
// The H_ASSERT family simply asserts that a given token has the expected type.
|
||||
// It mainly serves as an implementation aid for H_CAST. Of note in that regard
|
||||
// is that, unlike the standard 'assert' macro, these form _expressions_ that
|
||||
// return the value of their token argument; thus they can be used in a
|
||||
// "pass-through" fashion inside other expressions.
|
||||
//
|
||||
// The H_CAST family combines a type assertion with access to the
|
||||
// statically-typed value inside a token.
|
||||
//
|
||||
// A number of functions h_seq_* operate on and inspect sequence tokens.
|
||||
// Note that H_MAKE_SEQ takes no arguments and constructs an empty sequence.
|
||||
// Therefore there are h_seq_snoc and h_seq_append to build up sequences.
|
||||
//
|
||||
// The macro families H_FIELD and H_INDEX combine index access on a sequence
|
||||
// with a cast to the appropriate result type. H_FIELD is used to access the
|
||||
// elements of the argument token 'p' in an action. H_INDEX allows any sequence
|
||||
// token to be specified. Both macro families take an arbitrary number of index
|
||||
// arguments, giving access to elements in nested sequences by path.
|
||||
// These macros are very useful to avoid spaghetti chains of unchecked pointer
|
||||
// dereferences.
|
||||
//
|
||||
|
||||
// Standard short-hand for arena-allocating a variable in a semantic action.
|
||||
#define H_ALLOC(TYP) ((TYP *) h_arena_malloc(p->arena, sizeof(TYP)))
|
||||
|
||||
// Token constructors...
|
||||
|
||||
HParsedToken *h_make(HArena *arena, HTokenType type, void *value);
|
||||
HParsedToken *h_make_seq(HArena *arena); // Makes empty sequence.
|
||||
HParsedToken *h_make_seqn(HArena *arena, size_t n); // Makes empty sequence of expected size n.
|
||||
HParsedToken *h_make_bytes(HArena *arena, size_t len);
|
||||
HParsedToken *h_make_sint(HArena *arena, int64_t val);
|
||||
HParsedToken *h_make_uint(HArena *arena, uint64_t val);
|
||||
|
||||
// Standard short-hands to make tokens in an action.
|
||||
#define H_MAKE(TYP, VAL) h_make(p->arena, TT_ ## TYP, VAL)
|
||||
#define H_MAKE_SEQ() h_make_seq(p->arena)
|
||||
#define H_MAKE_SEQN(N) h_make_seqn(p->arena, N)
|
||||
#define H_MAKE_BYTES(LEN) h_make_bytes(p->arena, LEN)
|
||||
#define H_MAKE_SINT(VAL) h_make_sint(p->arena, VAL)
|
||||
#define H_MAKE_UINT(VAL) h_make_uint(p->arena, VAL)
|
||||
|
||||
// Extract (cast) type-specific value back from HParsedTokens...
|
||||
|
||||
// Pass-through assertion that a given token has the expected type.
|
||||
#define h_assert_type(T,P) (assert(P->token_type == (HTokenType)T), P)
|
||||
|
||||
// Convenience short-hand forms of h_assert_type.
|
||||
#define H_ASSERT(TYP, TOK) h_assert_type(TT_ ## TYP, TOK)
|
||||
#define H_ASSERT_SEQ(TOK) h_assert_type(TT_SEQUENCE, TOK)
|
||||
#define H_ASSERT_BYTES(TOK) h_assert_type(TT_BYTES, TOK)
|
||||
#define H_ASSERT_SINT(TOK) h_assert_type(TT_SINT, TOK)
|
||||
#define H_ASSERT_UINT(TOK) h_assert_type(TT_UINT, TOK)
|
||||
|
||||
// Assert expected type and return contained value.
|
||||
#define H_CAST(TYP, TOK) ((TYP *) H_ASSERT(TYP, TOK)->user)
|
||||
#define H_CAST_SEQ(TOK) (H_ASSERT_SEQ(TOK)->seq)
|
||||
#define H_CAST_BYTES(TOK) (H_ASSERT_BYTES(TOK)->bytes)
|
||||
#define H_CAST_SINT(TOK) (H_ASSERT_SINT(TOK)->sint)
|
||||
#define H_CAST_UINT(TOK) (H_ASSERT_UINT(TOK)->uint)
|
||||
|
||||
// Sequence access...
|
||||
|
||||
// Return the length of a sequence.
|
||||
size_t h_seq_len(const HParsedToken *p);
|
||||
|
||||
// Access a sequence's element array.
|
||||
HParsedToken **h_seq_elements(const HParsedToken *p);
|
||||
|
||||
// Access a sequence element by index.
|
||||
HParsedToken *h_seq_index(const HParsedToken *p, size_t i);
|
||||
|
||||
// Access an element in a nested sequence by a path of indices.
|
||||
HParsedToken *h_seq_index_path(const HParsedToken *p, size_t i, ...);
|
||||
HParsedToken *h_seq_index_vpath(const HParsedToken *p, size_t i, va_list va);
|
||||
|
||||
// Convenience macros combining (nested) index access and h_cast.
|
||||
#define H_INDEX(TYP, SEQ, ...) H_CAST(TYP, H_INDEX_TOKEN(SEQ, __VA_ARGS__))
|
||||
#define H_INDEX_SEQ(SEQ, ...) H_CAST_SEQ(H_INDEX_TOKEN(SEQ, __VA_ARGS__))
|
||||
#define H_INDEX_BYTES(SEQ, ...) H_CAST_BYTES(H_INDEX_TOKEN(SEQ, __VA_ARGS__))
|
||||
#define H_INDEX_SINT(SEQ, ...) H_CAST_SINT(H_INDEX_TOKEN(SEQ, __VA_ARGS__))
|
||||
#define H_INDEX_UINT(SEQ, ...) H_CAST_UINT(H_INDEX_TOKEN(SEQ, __VA_ARGS__))
|
||||
#define H_INDEX_TOKEN(SEQ, ...) h_seq_index_path(SEQ, __VA_ARGS__, -1)
|
||||
|
||||
// Standard short-hand to access and cast elements on a sequence token.
|
||||
#define H_FIELD(TYP, ...) H_INDEX(TYP, p->ast, __VA_ARGS__)
|
||||
#define H_FIELD_SEQ(...) H_INDEX_SEQ(p->ast, __VA_ARGS__)
|
||||
#define H_FIELD_BYTES(...) H_INDEX_BYTES(p->ast, __VA_ARGS__)
|
||||
#define H_FIELD_SINT(...) H_INDEX_SINT(p->ast, __VA_ARGS__)
|
||||
#define H_FIELD_UINT(...) H_INDEX_UINT(p->ast, __VA_ARGS__)
|
||||
|
||||
// Lower-level helper for h_seq_index.
|
||||
HParsedToken *h_carray_index(const HCountedArray *a, size_t i); // XXX -> internal
|
||||
|
||||
// Sequence modification...
|
||||
|
||||
// Add elements to a sequence.
|
||||
void h_seq_snoc(HParsedToken *xs, const HParsedToken *x); // append one
|
||||
void h_seq_append(HParsedToken *xs, const HParsedToken *ys); // append many
|
||||
|
||||
// XXX TODO: Remove elements from a sequence.
|
||||
|
||||
// Flatten nested sequences into one.
|
||||
const HParsedToken *h_seq_flatten(HArena *arena, const HParsedToken *p);
|
||||
|
||||
|
||||
#endif
|
||||
31
src/hammer.h
31
src/hammer.h
|
|
@ -42,14 +42,15 @@ typedef enum HParserBackend_ {
|
|||
} HParserBackend;
|
||||
|
||||
typedef enum HTokenType_ {
|
||||
TT_NONE,
|
||||
TT_BYTES,
|
||||
TT_SINT,
|
||||
TT_UINT,
|
||||
TT_SEQUENCE,
|
||||
// Before you change the explicit values of these, think of the poor bindings ;_;
|
||||
TT_NONE = 1,
|
||||
TT_BYTES = 2,
|
||||
TT_SINT = 4,
|
||||
TT_UINT = 8,
|
||||
TT_SEQUENCE = 16,
|
||||
TT_RESERVED_1, // reserved for backend-specific internal use
|
||||
TT_ERR = 32,
|
||||
TT_USER = 64,
|
||||
TT_ERR,
|
||||
TT_MAX
|
||||
} HTokenType;
|
||||
|
||||
|
|
@ -60,13 +61,15 @@ typedef struct HCountedArray_ {
|
|||
struct HParsedToken_ **elements;
|
||||
} HCountedArray;
|
||||
|
||||
typedef struct HBytes_ {
|
||||
const uint8_t *token;
|
||||
size_t len;
|
||||
} HBytes;
|
||||
|
||||
typedef struct HParsedToken_ {
|
||||
HTokenType token_type;
|
||||
union {
|
||||
struct {
|
||||
const uint8_t *token;
|
||||
size_t len;
|
||||
} bytes;
|
||||
HBytes bytes;
|
||||
int64_t sint;
|
||||
uint64_t uint;
|
||||
double dbl;
|
||||
|
|
@ -175,14 +178,18 @@ typedef struct HBenchmarkResults_ {
|
|||
rtype_t name(__VA_ARGS__, ...); \
|
||||
rtype_t name##__m(HAllocator* mm__, __VA_ARGS__, ...); \
|
||||
rtype_t name##__mv(HAllocator* mm__, __VA_ARGS__, va_list ap); \
|
||||
rtype_t name##__v(__VA_ARGS__, va_list ap)
|
||||
rtype_t name##__v(__VA_ARGS__, va_list ap); \
|
||||
rtype_t name##__a(void *args[]); \
|
||||
rtype_t name##__ma(HAllocator *mm__, void *args[])
|
||||
|
||||
// Note: this drops the attributes on the floor for the __v versions
|
||||
#define HAMMER_FN_DECL_VARARGS_ATTR(attr, rtype_t, name, ...) \
|
||||
rtype_t name(__VA_ARGS__, ...) attr; \
|
||||
rtype_t name##__m(HAllocator* mm__, __VA_ARGS__, ...) attr; \
|
||||
rtype_t name##__mv(HAllocator* mm__, __VA_ARGS__, va_list ap); \
|
||||
rtype_t name##__v(__VA_ARGS__, va_list ap)
|
||||
rtype_t name##__v(__VA_ARGS__, va_list ap); \
|
||||
rtype_t name##__a(void *args[]); \
|
||||
rtype_t name##__ma(HAllocator *mm__, void *args[])
|
||||
|
||||
// }}}
|
||||
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ HCountedArray *h_carray_new(HArena * arena);
|
|||
void h_carray_append(HCountedArray *array, void* item);
|
||||
|
||||
HSlist* h_slist_new(HArena *arena);
|
||||
HSlist* h_slist_copy(HSlist *slist);
|
||||
void* h_slist_pop(HSlist *slist);
|
||||
void h_slist_push(HSlist *slist, void* item);
|
||||
bool h_slist_find(HSlist *slist, const void* item);
|
||||
|
|
|
|||
|
|
@ -126,3 +126,27 @@ HParser* h_choice__mv(HAllocator* mm__, const HParser* p, va_list ap_) {
|
|||
return h_new_parser(mm__, &choice_vt, s);
|
||||
}
|
||||
|
||||
HParser* h_choice__a(void *args[]) {
|
||||
return h_choice__ma(&system_allocator, args);
|
||||
}
|
||||
|
||||
HParser* h_choice__ma(HAllocator* mm__, void *args[]) {
|
||||
size_t len = -1; // because do...while
|
||||
const HParser *arg;
|
||||
|
||||
do {
|
||||
arg=((HParser **)args)[++len];
|
||||
} while(arg);
|
||||
|
||||
HSequence *s = h_new(HSequence, 1);
|
||||
s->p_array = h_new(const HParser *, len);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
s->p_array[i] = ((HParser **)args)[i];
|
||||
}
|
||||
|
||||
s->len = len;
|
||||
HParser *ret = h_new(HParser, 1);
|
||||
ret->vtable = &choice_vt; ret->env = (void*)s;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,3 +144,28 @@ HParser* h_sequence__mv(HAllocator* mm__, const HParser *p, va_list ap_) {
|
|||
s->len = len;
|
||||
return h_new_parser(mm__, &sequence_vt, s);
|
||||
}
|
||||
|
||||
HParser* h_sequence__a(void *args[]) {
|
||||
return h_sequence__ma(&system_allocator, args);
|
||||
}
|
||||
|
||||
HParser* h_sequence__ma(HAllocator* mm__, void *args[]) {
|
||||
size_t len = -1; // because do...while
|
||||
const HParser *arg;
|
||||
|
||||
do {
|
||||
arg=((HParser **)args)[++len];
|
||||
} while(arg);
|
||||
|
||||
HSequence *s = h_new(HSequence, 1);
|
||||
s->p_array = h_new(const HParser *, len);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
s->p_array[i] = ((HParser **)args)[i];
|
||||
}
|
||||
|
||||
s->len = len;
|
||||
HParser *ret = h_new(HParser, 1);
|
||||
ret->vtable = &sequence_vt; ret->env = (void*)s;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ static HParseResult* parse_whitespace(void* env, HParseState *state) {
|
|||
bak = state->input_stream;
|
||||
c = h_read_bits(&state->input_stream, 8, false);
|
||||
if (state->input_stream.overrun)
|
||||
return NULL;
|
||||
break;
|
||||
} while (isspace(c));
|
||||
state->input_stream = bak;
|
||||
return h_do_parse((HParser*)env, state);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,11 @@ void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta) {
|
|||
fprintf(stream, "%*sUSER\n", indent, "");
|
||||
break;
|
||||
default:
|
||||
assert_message(0, "Should not reach here.");
|
||||
if(tok->token_type > TT_USER) {
|
||||
fprintf(stream, "%*sUSER %d\n", indent, "", tok->token_type-TT_USER);
|
||||
} else {
|
||||
assert_message(0, "Should not reach here.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
16
src/t_misc.c
Normal file
16
src/t_misc.c
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#include <glib.h>
|
||||
#include "test_suite.h"
|
||||
#include "hammer.h"
|
||||
|
||||
static void test_tt_user(void) {
|
||||
g_check_cmpint(TT_USER, >, TT_NONE);
|
||||
g_check_cmpint(TT_USER, >, TT_BYTES);
|
||||
g_check_cmpint(TT_USER, >, TT_SINT);
|
||||
g_check_cmpint(TT_USER, >, TT_UINT);
|
||||
g_check_cmpint(TT_USER, >, TT_SEQUENCE);
|
||||
g_check_cmpint(TT_USER, >, TT_ERR);
|
||||
}
|
||||
|
||||
void register_misc_tests(void) {
|
||||
g_test_add_func("/core/misc/tt_user", test_tt_user);
|
||||
}
|
||||
498
src/t_parser.c
498
src/t_parser.c
|
|
@ -5,154 +5,159 @@
|
|||
#include "test_suite.h"
|
||||
#include "parsers/parser_internal.h"
|
||||
|
||||
static void test_token(void) {
|
||||
static void test_token(gconstpointer backend) {
|
||||
const HParser *token_ = h_token((const uint8_t*)"95\xa2", 3);
|
||||
|
||||
g_check_parse_ok(token_, "95\xa2", 3, "<39.35.a2>");
|
||||
g_check_parse_failed(token_, "95", 2);
|
||||
g_check_parse_ok(token_, (HParserBackend)GPOINTER_TO_INT(backend), "95\xa2", 3, "<39.35.a2>");
|
||||
g_check_parse_failed(token_, (HParserBackend)GPOINTER_TO_INT(backend), "95", 2);
|
||||
}
|
||||
|
||||
static void test_ch(void) {
|
||||
static void test_ch(gconstpointer backend) {
|
||||
const HParser *ch_ = h_ch(0xa2);
|
||||
|
||||
g_check_parse_ok(ch_, "\xa2", 1, "u0xa2");
|
||||
g_check_parse_failed(ch_, "\xa3", 1);
|
||||
g_check_parse_ok(ch_, (HParserBackend)GPOINTER_TO_INT(backend), "\xa2", 1, "u0xa2");
|
||||
g_check_parse_failed(ch_, (HParserBackend)GPOINTER_TO_INT(backend), "\xa3", 1);
|
||||
}
|
||||
|
||||
static void test_ch_range(void) {
|
||||
static void test_ch_range(gconstpointer backend) {
|
||||
const HParser *range_ = h_ch_range('a', 'c');
|
||||
|
||||
g_check_parse_ok(range_, "b", 1, "u0x62");
|
||||
g_check_parse_failed(range_, "d", 1);
|
||||
g_check_parse_ok(range_, (HParserBackend)GPOINTER_TO_INT(backend), "b", 1, "u0x62");
|
||||
g_check_parse_failed(range_, (HParserBackend)GPOINTER_TO_INT(backend), "d", 1);
|
||||
}
|
||||
|
||||
//@MARK_START
|
||||
static void test_int64(void) {
|
||||
static void test_int64(gconstpointer backend) {
|
||||
const HParser *int64_ = h_int64();
|
||||
|
||||
g_check_parse_ok(int64_, "\xff\xff\xff\xfe\x00\x00\x00\x00", 8, "s-0x200000000");
|
||||
g_check_parse_failed(int64_, "\xff\xff\xff\xfe\x00\x00\x00", 7);
|
||||
g_check_parse_ok(int64_, (HParserBackend)GPOINTER_TO_INT(backend), "\xff\xff\xff\xfe\x00\x00\x00\x00", 8, "s-0x200000000");
|
||||
g_check_parse_failed(int64_, (HParserBackend)GPOINTER_TO_INT(backend), "\xff\xff\xff\xfe\x00\x00\x00", 7);
|
||||
}
|
||||
|
||||
static void test_int32(void) {
|
||||
static void test_int32(gconstpointer backend) {
|
||||
const HParser *int32_ = h_int32();
|
||||
|
||||
g_check_parse_ok(int32_, "\xff\xfe\x00\x00", 4, "s-0x20000");
|
||||
g_check_parse_failed(int32_, "\xff\xfe\x00", 3);
|
||||
g_check_parse_ok(int32_, (HParserBackend)GPOINTER_TO_INT(backend), "\xff\xfe\x00\x00", 4, "s-0x20000");
|
||||
g_check_parse_failed(int32_, (HParserBackend)GPOINTER_TO_INT(backend), "\xff\xfe\x00", 3);
|
||||
|
||||
g_check_parse_ok(int32_, "\x00\x02\x00\x00", 4, "s0x20000");
|
||||
g_check_parse_failed(int32_, "\x00\x02\x00", 3);
|
||||
g_check_parse_ok(int32_, (HParserBackend)GPOINTER_TO_INT(backend), "\x00\x02\x00\x00", 4, "s0x20000");
|
||||
g_check_parse_failed(int32_, (HParserBackend)GPOINTER_TO_INT(backend), "\x00\x02\x00", 3);
|
||||
}
|
||||
|
||||
static void test_int16(void) {
|
||||
static void test_int16(gconstpointer backend) {
|
||||
const HParser *int16_ = h_int16();
|
||||
|
||||
g_check_parse_ok(int16_, "\xfe\x00", 2, "s-0x200");
|
||||
g_check_parse_failed(int16_, "\xfe", 1);
|
||||
g_check_parse_ok(int16_, (HParserBackend)GPOINTER_TO_INT(backend), "\xfe\x00", 2, "s-0x200");
|
||||
g_check_parse_failed(int16_, (HParserBackend)GPOINTER_TO_INT(backend), "\xfe", 1);
|
||||
|
||||
g_check_parse_ok(int16_, "\x02\x00", 2, "s0x200");
|
||||
g_check_parse_failed(int16_, "\x02", 1);
|
||||
g_check_parse_ok(int16_, (HParserBackend)GPOINTER_TO_INT(backend), "\x02\x00", 2, "s0x200");
|
||||
g_check_parse_failed(int16_, (HParserBackend)GPOINTER_TO_INT(backend), "\x02", 1);
|
||||
}
|
||||
|
||||
static void test_int8(void) {
|
||||
static void test_int8(gconstpointer backend) {
|
||||
const HParser *int8_ = h_int8();
|
||||
|
||||
g_check_parse_ok(int8_, "\x88", 1, "s-0x78");
|
||||
g_check_parse_failed(int8_, "", 0);
|
||||
g_check_parse_ok(int8_, (HParserBackend)GPOINTER_TO_INT(backend), "\x88", 1, "s-0x78");
|
||||
g_check_parse_failed(int8_, (HParserBackend)GPOINTER_TO_INT(backend), "", 0);
|
||||
}
|
||||
|
||||
static void test_uint64(void) {
|
||||
static void test_uint64(gconstpointer backend) {
|
||||
const HParser *uint64_ = h_uint64();
|
||||
|
||||
g_check_parse_ok(uint64_, "\x00\x00\x00\x02\x00\x00\x00\x00", 8, "u0x200000000");
|
||||
g_check_parse_failed(uint64_, "\x00\x00\x00\x02\x00\x00\x00", 7);
|
||||
g_check_parse_ok(uint64_, (HParserBackend)GPOINTER_TO_INT(backend), "\x00\x00\x00\x02\x00\x00\x00\x00", 8, "u0x200000000");
|
||||
g_check_parse_failed(uint64_, (HParserBackend)GPOINTER_TO_INT(backend), "\x00\x00\x00\x02\x00\x00\x00", 7);
|
||||
}
|
||||
|
||||
static void test_uint32(void) {
|
||||
static void test_uint32(gconstpointer backend) {
|
||||
const HParser *uint32_ = h_uint32();
|
||||
|
||||
g_check_parse_ok(uint32_, "\x00\x02\x00\x00", 4, "u0x20000");
|
||||
g_check_parse_failed(uint32_, "\x00\x02\x00", 3);
|
||||
g_check_parse_ok(uint32_, (HParserBackend)GPOINTER_TO_INT(backend), "\x00\x02\x00\x00", 4, "u0x20000");
|
||||
g_check_parse_failed(uint32_, (HParserBackend)GPOINTER_TO_INT(backend), "\x00\x02\x00", 3);
|
||||
}
|
||||
|
||||
static void test_uint16(void) {
|
||||
static void test_uint16(gconstpointer backend) {
|
||||
const HParser *uint16_ = h_uint16();
|
||||
|
||||
g_check_parse_ok(uint16_, "\x02\x00", 2, "u0x200");
|
||||
g_check_parse_failed(uint16_, "\x02", 1);
|
||||
g_check_parse_ok(uint16_, (HParserBackend)GPOINTER_TO_INT(backend), "\x02\x00", 2, "u0x200");
|
||||
g_check_parse_failed(uint16_, (HParserBackend)GPOINTER_TO_INT(backend), "\x02", 1);
|
||||
}
|
||||
|
||||
static void test_uint8(void) {
|
||||
static void test_uint8(gconstpointer backend) {
|
||||
const HParser *uint8_ = h_uint8();
|
||||
|
||||
g_check_parse_ok(uint8_, "\x78", 1, "u0x78");
|
||||
g_check_parse_failed(uint8_, "", 0);
|
||||
g_check_parse_ok(uint8_, (HParserBackend)GPOINTER_TO_INT(backend), "\x78", 1, "u0x78");
|
||||
g_check_parse_failed(uint8_, (HParserBackend)GPOINTER_TO_INT(backend), "", 0);
|
||||
}
|
||||
//@MARK_END
|
||||
|
||||
static void test_int_range(void) {
|
||||
static void test_int_range(gconstpointer backend) {
|
||||
const HParser *int_range_ = h_int_range(h_uint8(), 3, 10);
|
||||
|
||||
g_check_parse_ok(int_range_, "\x05", 1, "u0x5");
|
||||
g_check_parse_failed(int_range_, "\xb", 1);
|
||||
g_check_parse_ok(int_range_, (HParserBackend)GPOINTER_TO_INT(backend), "\x05", 1, "u0x5");
|
||||
g_check_parse_failed(int_range_, (HParserBackend)GPOINTER_TO_INT(backend), "\xb", 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void test_float64(void) {
|
||||
static void test_float64(gconstpointer backend) {
|
||||
const HParser *float64_ = h_float64();
|
||||
|
||||
g_check_parse_ok(float64_, "\x3f\xf0\x00\x00\x00\x00\x00\x00", 8, 1.0);
|
||||
g_check_parse_failed(float64_, "\x3f\xf0\x00\x00\x00\x00\x00", 7);
|
||||
g_check_parse_ok(float64_, (HParserBackend)GPOINTER_TO_INT(backend), "\x3f\xf0\x00\x00\x00\x00\x00\x00", 8, 1.0);
|
||||
g_check_parse_failed(float64_, (HParserBackend)GPOINTER_TO_INT(backend), "\x3f\xf0\x00\x00\x00\x00\x00", 7);
|
||||
}
|
||||
|
||||
static void test_float32(void) {
|
||||
static void test_float32(gconstpointer backend) {
|
||||
const HParser *float32_ = h_float32();
|
||||
|
||||
g_check_parse_ok(float32_, "\x3f\x80\x00\x00", 4, 1.0);
|
||||
g_check_parse_failed(float32_, "\x3f\x80\x00");
|
||||
g_check_parse_ok(float32_, (HParserBackend)GPOINTER_TO_INT(backend), "\x3f\x80\x00\x00", 4, 1.0);
|
||||
g_check_parse_failed(float32_, (HParserBackend)GPOINTER_TO_INT(backend), "\x3f\x80\x00");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void test_whitespace(void) {
|
||||
static void test_whitespace(gconstpointer backend) {
|
||||
const HParser *whitespace_ = h_whitespace(h_ch('a'));
|
||||
const HParser *whitespace_end = h_whitespace(h_end_p());
|
||||
|
||||
g_check_parse_ok(whitespace_, "a", 1, "u0x61");
|
||||
g_check_parse_ok(whitespace_, " a", 2, "u0x61");
|
||||
g_check_parse_ok(whitespace_, " a", 3, "u0x61");
|
||||
g_check_parse_ok(whitespace_, "\ta", 2, "u0x61");
|
||||
g_check_parse_failed(whitespace_, "_a", 2);
|
||||
g_check_parse_ok(whitespace_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "u0x61");
|
||||
g_check_parse_ok(whitespace_, (HParserBackend)GPOINTER_TO_INT(backend), " a", 2, "u0x61");
|
||||
g_check_parse_ok(whitespace_, (HParserBackend)GPOINTER_TO_INT(backend), " a", 3, "u0x61");
|
||||
g_check_parse_ok(whitespace_, (HParserBackend)GPOINTER_TO_INT(backend), "\ta", 2, "u0x61");
|
||||
g_check_parse_failed(whitespace_, (HParserBackend)GPOINTER_TO_INT(backend), "_a", 2);
|
||||
|
||||
g_check_parse_ok(whitespace_end, (HParserBackend)GPOINTER_TO_INT(backend), "", 0, "NULL");
|
||||
g_check_parse_ok(whitespace_end, (HParserBackend)GPOINTER_TO_INT(backend)," ", 2, "NULL");
|
||||
g_check_parse_failed(whitespace_end, (HParserBackend)GPOINTER_TO_INT(backend)," x", 3);
|
||||
}
|
||||
|
||||
static void test_left(void) {
|
||||
static void test_left(gconstpointer backend) {
|
||||
const HParser *left_ = h_left(h_ch('a'), h_ch(' '));
|
||||
|
||||
g_check_parse_ok(left_, "a ", 2, "u0x61");
|
||||
g_check_parse_failed(left_, "a", 1);
|
||||
g_check_parse_failed(left_, " ", 1);
|
||||
g_check_parse_failed(left_, "ab", 2);
|
||||
g_check_parse_ok(left_, (HParserBackend)GPOINTER_TO_INT(backend), "a ", 2, "u0x61");
|
||||
g_check_parse_failed(left_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
g_check_parse_failed(left_, (HParserBackend)GPOINTER_TO_INT(backend), " ", 1);
|
||||
g_check_parse_failed(left_, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2);
|
||||
}
|
||||
|
||||
static void test_right(void) {
|
||||
static void test_right(gconstpointer backend) {
|
||||
const HParser *right_ = h_right(h_ch(' '), h_ch('a'));
|
||||
|
||||
g_check_parse_ok(right_, " a", 2, "u0x61");
|
||||
g_check_parse_failed(right_, "a", 1);
|
||||
g_check_parse_failed(right_, " ", 1);
|
||||
g_check_parse_failed(right_, "ba", 2);
|
||||
g_check_parse_ok(right_, (HParserBackend)GPOINTER_TO_INT(backend), " a", 2, "u0x61");
|
||||
g_check_parse_failed(right_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
g_check_parse_failed(right_, (HParserBackend)GPOINTER_TO_INT(backend), " ", 1);
|
||||
g_check_parse_failed(right_, (HParserBackend)GPOINTER_TO_INT(backend), "ba", 2);
|
||||
}
|
||||
|
||||
static void test_middle(void) {
|
||||
static void test_middle(gconstpointer backend) {
|
||||
const HParser *middle_ = h_middle(h_ch(' '), h_ch('a'), h_ch(' '));
|
||||
|
||||
g_check_parse_ok(middle_, " a ", 3, "u0x61");
|
||||
g_check_parse_failed(middle_, "a", 1);
|
||||
g_check_parse_failed(middle_, " ", 1);
|
||||
g_check_parse_failed(middle_, " a", 2);
|
||||
g_check_parse_failed(middle_, "a ", 2);
|
||||
g_check_parse_failed(middle_, " b ", 3);
|
||||
g_check_parse_failed(middle_, "ba ", 3);
|
||||
g_check_parse_failed(middle_, " ab", 3);
|
||||
g_check_parse_ok(middle_, (HParserBackend)GPOINTER_TO_INT(backend), " a ", 3, "u0x61");
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), " ", 1);
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), " a", 2);
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), "a ", 2);
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), " b ", 3);
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), "ba ", 3);
|
||||
g_check_parse_failed(middle_, (HParserBackend)GPOINTER_TO_INT(backend), " ab", 3);
|
||||
}
|
||||
|
||||
#include <ctype.h>
|
||||
|
|
@ -189,7 +194,7 @@ const HParsedToken* upcase(const HParseResult *p) {
|
|||
}
|
||||
}
|
||||
|
||||
static void test_action(void) {
|
||||
static void test_action(gconstpointer backend) {
|
||||
const HParser *action_ = h_action(h_sequence(h_choice(h_ch('a'),
|
||||
h_ch('A'),
|
||||
NULL),
|
||||
|
|
@ -199,158 +204,158 @@ static void test_action(void) {
|
|||
NULL),
|
||||
upcase);
|
||||
|
||||
g_check_parse_ok(action_, "ab", 2, "(u0x41 u0x42)");
|
||||
g_check_parse_ok(action_, "AB", 2, "(u0x41 u0x42)");
|
||||
g_check_parse_failed(action_, "XX", 2);
|
||||
g_check_parse_ok(action_, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2, "(u0x41 u0x42)");
|
||||
g_check_parse_ok(action_, (HParserBackend)GPOINTER_TO_INT(backend), "AB", 2, "(u0x41 u0x42)");
|
||||
g_check_parse_failed(action_, (HParserBackend)GPOINTER_TO_INT(backend), "XX", 2);
|
||||
}
|
||||
|
||||
static void test_in(void) {
|
||||
static void test_in(gconstpointer backend) {
|
||||
uint8_t options[3] = { 'a', 'b', 'c' };
|
||||
const HParser *in_ = h_in(options, 3);
|
||||
g_check_parse_ok(in_, "b", 1, "u0x62");
|
||||
g_check_parse_failed(in_, "d", 1);
|
||||
g_check_parse_ok(in_, (HParserBackend)GPOINTER_TO_INT(backend), "b", 1, "u0x62");
|
||||
g_check_parse_failed(in_, (HParserBackend)GPOINTER_TO_INT(backend), "d", 1);
|
||||
|
||||
}
|
||||
|
||||
static void test_not_in(void) {
|
||||
static void test_not_in(gconstpointer backend) {
|
||||
uint8_t options[3] = { 'a', 'b', 'c' };
|
||||
const HParser *not_in_ = h_not_in(options, 3);
|
||||
g_check_parse_ok(not_in_, "d", 1, "u0x64");
|
||||
g_check_parse_failed(not_in_, "a", 1);
|
||||
g_check_parse_ok(not_in_, (HParserBackend)GPOINTER_TO_INT(backend), "d", 1, "u0x64");
|
||||
g_check_parse_failed(not_in_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
|
||||
}
|
||||
|
||||
static void test_end_p(void) {
|
||||
static void test_end_p(gconstpointer backend) {
|
||||
const HParser *end_p_ = h_sequence(h_ch('a'), h_end_p(), NULL);
|
||||
g_check_parse_ok(end_p_, "a", 1, "(u0x61)");
|
||||
g_check_parse_failed(end_p_, "aa", 2);
|
||||
g_check_parse_ok(end_p_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "(u0x61)");
|
||||
g_check_parse_failed(end_p_, (HParserBackend)GPOINTER_TO_INT(backend), "aa", 2);
|
||||
}
|
||||
|
||||
static void test_nothing_p(void) {
|
||||
static void test_nothing_p(gconstpointer backend) {
|
||||
const HParser *nothing_p_ = h_nothing_p();
|
||||
g_check_parse_failed(nothing_p_, "a", 1);
|
||||
g_check_parse_failed(nothing_p_, (HParserBackend)GPOINTER_TO_INT(backend),"a", 1);
|
||||
}
|
||||
|
||||
static void test_sequence(void) {
|
||||
static void test_sequence(gconstpointer backend) {
|
||||
const HParser *sequence_1 = h_sequence(h_ch('a'), h_ch('b'), NULL);
|
||||
const HParser *sequence_2 = h_sequence(h_ch('a'), h_whitespace(h_ch('b')), NULL);
|
||||
|
||||
g_check_parse_ok(sequence_1, "ab", 2, "(u0x61 u0x62)");
|
||||
g_check_parse_failed(sequence_1, "a", 1);
|
||||
g_check_parse_failed(sequence_1, "b", 1);
|
||||
g_check_parse_ok(sequence_2, "ab", 2, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(sequence_2, "a b", 3, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(sequence_2, "a b", 4, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(sequence_1, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2, "(u0x61 u0x62)");
|
||||
g_check_parse_failed(sequence_1, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
g_check_parse_failed(sequence_1, (HParserBackend)GPOINTER_TO_INT(backend), "b", 1);
|
||||
g_check_parse_ok(sequence_2, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(sequence_2, (HParserBackend)GPOINTER_TO_INT(backend), "a b", 3, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(sequence_2, (HParserBackend)GPOINTER_TO_INT(backend), "a b", 4, "(u0x61 u0x62)");
|
||||
}
|
||||
|
||||
static void test_choice(void) {
|
||||
static void test_choice(gconstpointer backend) {
|
||||
const HParser *choice_ = h_choice(h_ch('a'), h_ch('b'), NULL);
|
||||
|
||||
g_check_parse_ok(choice_, "a", 1, "u0x61");
|
||||
g_check_parse_ok(choice_, "b", 1, "u0x62");
|
||||
g_check_parse_failed(choice_, "c", 1);
|
||||
g_check_parse_ok(choice_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "u0x61");
|
||||
g_check_parse_ok(choice_, (HParserBackend)GPOINTER_TO_INT(backend), "b", 1, "u0x62");
|
||||
g_check_parse_failed(choice_, (HParserBackend)GPOINTER_TO_INT(backend), "c", 1);
|
||||
}
|
||||
|
||||
static void test_butnot(void) {
|
||||
static void test_butnot(gconstpointer backend) {
|
||||
const HParser *butnot_1 = h_butnot(h_ch('a'), h_token((const uint8_t*)"ab", 2));
|
||||
const HParser *butnot_2 = h_butnot(h_ch_range('0', '9'), h_ch('6'));
|
||||
|
||||
g_check_parse_ok(butnot_1, "a", 1, "u0x61");
|
||||
g_check_parse_failed(butnot_1, "ab", 2);
|
||||
g_check_parse_ok(butnot_1, "aa", 2, "u0x61");
|
||||
g_check_parse_failed(butnot_2, "6", 1);
|
||||
g_check_parse_ok(butnot_1, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "u0x61");
|
||||
g_check_parse_failed(butnot_1, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2);
|
||||
g_check_parse_ok(butnot_1, (HParserBackend)GPOINTER_TO_INT(backend), "aa", 2, "u0x61");
|
||||
g_check_parse_failed(butnot_2, (HParserBackend)GPOINTER_TO_INT(backend), "6", 1);
|
||||
}
|
||||
|
||||
static void test_difference(void) {
|
||||
static void test_difference(gconstpointer backend) {
|
||||
const HParser *difference_ = h_difference(h_token((const uint8_t*)"ab", 2), h_ch('a'));
|
||||
|
||||
g_check_parse_ok(difference_, "ab", 2, "<61.62>");
|
||||
g_check_parse_failed(difference_, "a", 1);
|
||||
g_check_parse_ok(difference_, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2, "<61.62>");
|
||||
g_check_parse_failed(difference_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
}
|
||||
|
||||
static void test_xor(void) {
|
||||
static void test_xor(gconstpointer backend) {
|
||||
const HParser *xor_ = h_xor(h_ch_range('0', '6'), h_ch_range('5', '9'));
|
||||
|
||||
g_check_parse_ok(xor_, "0", 1, "u0x30");
|
||||
g_check_parse_ok(xor_, "9", 1, "u0x39");
|
||||
g_check_parse_failed(xor_, "5", 1);
|
||||
g_check_parse_failed(xor_, "a", 1);
|
||||
g_check_parse_ok(xor_, (HParserBackend)GPOINTER_TO_INT(backend), "0", 1, "u0x30");
|
||||
g_check_parse_ok(xor_, (HParserBackend)GPOINTER_TO_INT(backend), "9", 1, "u0x39");
|
||||
g_check_parse_failed(xor_, (HParserBackend)GPOINTER_TO_INT(backend), "5", 1);
|
||||
g_check_parse_failed(xor_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1);
|
||||
}
|
||||
|
||||
static void test_many(void) {
|
||||
static void test_many(gconstpointer backend) {
|
||||
const HParser *many_ = h_many(h_choice(h_ch('a'), h_ch('b'), NULL));
|
||||
|
||||
g_check_parse_ok(many_, "", 0, "()");
|
||||
g_check_parse_ok(many_, "adef", 4, "(u0x61)");
|
||||
g_check_parse_ok(many_, "bdef", 4, "(u0x62)");
|
||||
g_check_parse_ok(many_, "aabbabadef", 10, "(u0x61 u0x61 u0x62 u0x62 u0x61 u0x62 u0x61)");
|
||||
g_check_parse_ok(many_, "daabbabadef", 11, "()");
|
||||
g_check_parse_ok(many_, (HParserBackend)GPOINTER_TO_INT(backend), "", 0, "()");
|
||||
g_check_parse_ok(many_, (HParserBackend)GPOINTER_TO_INT(backend), "adef", 4, "(u0x61)");
|
||||
g_check_parse_ok(many_, (HParserBackend)GPOINTER_TO_INT(backend), "bdef", 4, "(u0x62)");
|
||||
g_check_parse_ok(many_, (HParserBackend)GPOINTER_TO_INT(backend), "aabbabadef", 10, "(u0x61 u0x61 u0x62 u0x62 u0x61 u0x62 u0x61)");
|
||||
g_check_parse_ok(many_, (HParserBackend)GPOINTER_TO_INT(backend), "daabbabadef", 11, "()");
|
||||
}
|
||||
|
||||
static void test_many1(void) {
|
||||
static void test_many1(gconstpointer backend) {
|
||||
const HParser *many1_ = h_many1(h_choice(h_ch('a'), h_ch('b'), NULL));
|
||||
|
||||
g_check_parse_failed(many1_, "", 0);
|
||||
g_check_parse_ok(many1_, "adef", 4, "(u0x61)");
|
||||
g_check_parse_ok(many1_, "bdef", 4, "(u0x62)");
|
||||
g_check_parse_ok(many1_, "aabbabadef", 10, "(u0x61 u0x61 u0x62 u0x62 u0x61 u0x62 u0x61)");
|
||||
g_check_parse_failed(many1_, "daabbabadef", 11);
|
||||
g_check_parse_failed(many1_, (HParserBackend)GPOINTER_TO_INT(backend), "", 0);
|
||||
g_check_parse_ok(many1_, (HParserBackend)GPOINTER_TO_INT(backend), "adef", 4, "(u0x61)");
|
||||
g_check_parse_ok(many1_, (HParserBackend)GPOINTER_TO_INT(backend), "bdef", 4, "(u0x62)");
|
||||
g_check_parse_ok(many1_, (HParserBackend)GPOINTER_TO_INT(backend), "aabbabadef", 10, "(u0x61 u0x61 u0x62 u0x62 u0x61 u0x62 u0x61)");
|
||||
g_check_parse_failed(many1_, (HParserBackend)GPOINTER_TO_INT(backend), "daabbabadef", 11);
|
||||
}
|
||||
|
||||
static void test_repeat_n(void) {
|
||||
static void test_repeat_n(gconstpointer backend) {
|
||||
const HParser *repeat_n_ = h_repeat_n(h_choice(h_ch('a'), h_ch('b'), NULL), 2);
|
||||
|
||||
g_check_parse_failed(repeat_n_, "adef", 4);
|
||||
g_check_parse_ok(repeat_n_, "abdef", 5, "(u0x61 u0x62)");
|
||||
g_check_parse_failed(repeat_n_, "dabdef", 6);
|
||||
g_check_parse_failed(repeat_n_, (HParserBackend)GPOINTER_TO_INT(backend), "adef", 4);
|
||||
g_check_parse_ok(repeat_n_, (HParserBackend)GPOINTER_TO_INT(backend), "abdef", 5, "(u0x61 u0x62)");
|
||||
g_check_parse_failed(repeat_n_, (HParserBackend)GPOINTER_TO_INT(backend), "dabdef", 6);
|
||||
}
|
||||
|
||||
static void test_optional(void) {
|
||||
static void test_optional(gconstpointer backend) {
|
||||
const HParser *optional_ = h_sequence(h_ch('a'), h_optional(h_choice(h_ch('b'), h_ch('c'), NULL)), h_ch('d'), NULL);
|
||||
|
||||
g_check_parse_ok(optional_, "abd", 3, "(u0x61 u0x62 u0x64)");
|
||||
g_check_parse_ok(optional_, "acd", 3, "(u0x61 u0x63 u0x64)");
|
||||
g_check_parse_ok(optional_, "ad", 2, "(u0x61 null u0x64)");
|
||||
g_check_parse_failed(optional_, "aed", 3);
|
||||
g_check_parse_failed(optional_, "ab", 2);
|
||||
g_check_parse_failed(optional_, "ac", 2);
|
||||
g_check_parse_ok(optional_, (HParserBackend)GPOINTER_TO_INT(backend), "abd", 3, "(u0x61 u0x62 u0x64)");
|
||||
g_check_parse_ok(optional_, (HParserBackend)GPOINTER_TO_INT(backend), "acd", 3, "(u0x61 u0x63 u0x64)");
|
||||
g_check_parse_ok(optional_, (HParserBackend)GPOINTER_TO_INT(backend), "ad", 2, "(u0x61 null u0x64)");
|
||||
g_check_parse_failed(optional_, (HParserBackend)GPOINTER_TO_INT(backend), "aed", 3);
|
||||
g_check_parse_failed(optional_, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2);
|
||||
g_check_parse_failed(optional_, (HParserBackend)GPOINTER_TO_INT(backend), "ac", 2);
|
||||
}
|
||||
|
||||
static void test_ignore(void) {
|
||||
static void test_ignore(gconstpointer backend) {
|
||||
const HParser *ignore_ = h_sequence(h_ch('a'), h_ignore(h_ch('b')), h_ch('c'), NULL);
|
||||
|
||||
g_check_parse_ok(ignore_, "abc", 3, "(u0x61 u0x63)");
|
||||
g_check_parse_failed(ignore_, "ac", 2);
|
||||
g_check_parse_ok(ignore_, (HParserBackend)GPOINTER_TO_INT(backend), "abc", 3, "(u0x61 u0x63)");
|
||||
g_check_parse_failed(ignore_, (HParserBackend)GPOINTER_TO_INT(backend), "ac", 2);
|
||||
}
|
||||
|
||||
static void test_sepBy(void) {
|
||||
static void test_sepBy(gconstpointer backend) {
|
||||
const HParser *sepBy_ = h_sepBy(h_choice(h_ch('1'), h_ch('2'), h_ch('3'), NULL), h_ch(','));
|
||||
|
||||
g_check_parse_ok(sepBy_, "1,2,3", 5, "(u0x31 u0x32 u0x33)");
|
||||
g_check_parse_ok(sepBy_, "1,3,2", 5, "(u0x31 u0x33 u0x32)");
|
||||
g_check_parse_ok(sepBy_, "1,3", 3, "(u0x31 u0x33)");
|
||||
g_check_parse_ok(sepBy_, "3", 1, "(u0x33)");
|
||||
g_check_parse_ok(sepBy_, "", 0, "()");
|
||||
g_check_parse_ok(sepBy_, (HParserBackend)GPOINTER_TO_INT(backend), "1,2,3", 5, "(u0x31 u0x32 u0x33)");
|
||||
g_check_parse_ok(sepBy_, (HParserBackend)GPOINTER_TO_INT(backend), "1,3,2", 5, "(u0x31 u0x33 u0x32)");
|
||||
g_check_parse_ok(sepBy_, (HParserBackend)GPOINTER_TO_INT(backend), "1,3", 3, "(u0x31 u0x33)");
|
||||
g_check_parse_ok(sepBy_, (HParserBackend)GPOINTER_TO_INT(backend), "3", 1, "(u0x33)");
|
||||
g_check_parse_ok(sepBy_, (HParserBackend)GPOINTER_TO_INT(backend), "", 0, "()");
|
||||
}
|
||||
|
||||
static void test_sepBy1(void) {
|
||||
static void test_sepBy1(gconstpointer backend) {
|
||||
const HParser *sepBy1_ = h_sepBy1(h_choice(h_ch('1'), h_ch('2'), h_ch('3'), NULL), h_ch(','));
|
||||
|
||||
g_check_parse_ok(sepBy1_, "1,2,3", 5, "(u0x31 u0x32 u0x33)");
|
||||
g_check_parse_ok(sepBy1_, "1,3,2", 5, "(u0x31 u0x33 u0x32)");
|
||||
g_check_parse_ok(sepBy1_, "1,3", 3, "(u0x31 u0x33)");
|
||||
g_check_parse_ok(sepBy1_, "3", 1, "(u0x33)");
|
||||
g_check_parse_failed(sepBy1_, "", 0);
|
||||
g_check_parse_ok(sepBy1_, (HParserBackend)GPOINTER_TO_INT(backend), "1,2,3", 5, "(u0x31 u0x32 u0x33)");
|
||||
g_check_parse_ok(sepBy1_, (HParserBackend)GPOINTER_TO_INT(backend), "1,3,2", 5, "(u0x31 u0x33 u0x32)");
|
||||
g_check_parse_ok(sepBy1_, (HParserBackend)GPOINTER_TO_INT(backend), "1,3", 3, "(u0x31 u0x33)");
|
||||
g_check_parse_ok(sepBy1_, (HParserBackend)GPOINTER_TO_INT(backend), "3", 1, "(u0x33)");
|
||||
g_check_parse_failed(sepBy1_, (HParserBackend)GPOINTER_TO_INT(backend), "", 0);
|
||||
}
|
||||
|
||||
static void test_epsilon_p(void) {
|
||||
static void test_epsilon_p(gconstpointer backend) {
|
||||
const HParser *epsilon_p_1 = h_sequence(h_ch('a'), h_epsilon_p(), h_ch('b'), NULL);
|
||||
const HParser *epsilon_p_2 = h_sequence(h_epsilon_p(), h_ch('a'), NULL);
|
||||
const HParser *epsilon_p_3 = h_sequence(h_ch('a'), h_epsilon_p(), NULL);
|
||||
|
||||
g_check_parse_ok(epsilon_p_1, "ab", 2, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(epsilon_p_2, "a", 1, "(u0x61)");
|
||||
g_check_parse_ok(epsilon_p_3, "a", 1, "(u0x61)");
|
||||
g_check_parse_ok(epsilon_p_1, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2, "(u0x61 u0x62)");
|
||||
g_check_parse_ok(epsilon_p_2, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "(u0x61)");
|
||||
g_check_parse_ok(epsilon_p_3, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "(u0x61)");
|
||||
}
|
||||
|
||||
bool validate_test_ab(HParseResult *p) {
|
||||
|
|
@ -363,78 +368,163 @@ bool validate_test_ab(HParseResult *p) {
|
|||
return (p->ast->seq->elements[0]->uint == p->ast->seq->elements[1]->uint);
|
||||
}
|
||||
|
||||
static void test_attr_bool(void) {
|
||||
static void test_attr_bool(gconstpointer backend) {
|
||||
const HParser *ab_ = h_attr_bool(h_many1(h_choice(h_ch('a'), h_ch('b'), NULL)),
|
||||
validate_test_ab);
|
||||
|
||||
g_check_parse_ok(ab_, "aa", 2, "(u0x61 u0x61)");
|
||||
g_check_parse_ok(ab_, "bb", 2, "(u0x62 u0x62)");
|
||||
g_check_parse_failed(ab_, "ab", 2);
|
||||
g_check_parse_ok(ab_, (HParserBackend)GPOINTER_TO_INT(backend), "aa", 2, "(u0x61 u0x61)");
|
||||
g_check_parse_ok(ab_, (HParserBackend)GPOINTER_TO_INT(backend), "bb", 2, "(u0x62 u0x62)");
|
||||
g_check_parse_failed(ab_, (HParserBackend)GPOINTER_TO_INT(backend), "ab", 2);
|
||||
}
|
||||
|
||||
static void test_and(void) {
|
||||
static void test_and(gconstpointer backend) {
|
||||
const HParser *and_1 = h_sequence(h_and(h_ch('0')), h_ch('0'), NULL);
|
||||
const HParser *and_2 = h_sequence(h_and(h_ch('0')), h_ch('1'), NULL);
|
||||
const HParser *and_3 = h_sequence(h_ch('1'), h_and(h_ch('2')), NULL);
|
||||
|
||||
g_check_parse_ok(and_1, "0", 1, "(u0x30)");
|
||||
g_check_parse_failed(and_2, "0", 1);
|
||||
g_check_parse_ok(and_3, "12", 2, "(u0x31)");
|
||||
g_check_parse_ok(and_1, (HParserBackend)GPOINTER_TO_INT(backend), "0", 1, "(u0x30)");
|
||||
g_check_parse_failed(and_2, (HParserBackend)GPOINTER_TO_INT(backend), "0", 1);
|
||||
g_check_parse_ok(and_3, (HParserBackend)GPOINTER_TO_INT(backend), "12", 2, "(u0x31)");
|
||||
}
|
||||
|
||||
static void test_not(void) {
|
||||
static void test_not(gconstpointer backend) {
|
||||
const HParser *not_1 = h_sequence(h_ch('a'), h_choice(h_ch('+'), h_token((const uint8_t*)"++", 2), NULL), h_ch('b'), NULL);
|
||||
const HParser *not_2 = h_sequence(h_ch('a'),
|
||||
h_choice(h_sequence(h_ch('+'), h_not(h_ch('+')), NULL),
|
||||
h_token((const uint8_t*)"++", 2),
|
||||
NULL), h_ch('b'), NULL);
|
||||
|
||||
g_check_parse_ok(not_1, "a+b", 3, "(u0x61 u0x2b u0x62)");
|
||||
g_check_parse_failed(not_1, "a++b", 4);
|
||||
g_check_parse_ok(not_2, "a+b", 3, "(u0x61 (u0x2b) u0x62)");
|
||||
g_check_parse_ok(not_2, "a++b", 4, "(u0x61 <2b.2b> u0x62)");
|
||||
g_check_parse_ok(not_1, (HParserBackend)GPOINTER_TO_INT(backend), "a+b", 3, "(u0x61 u0x2b u0x62)");
|
||||
g_check_parse_failed(not_1, (HParserBackend)GPOINTER_TO_INT(backend), "a++b", 4);
|
||||
g_check_parse_ok(not_2, (HParserBackend)GPOINTER_TO_INT(backend), "a+b", 3, "(u0x61 (u0x2b) u0x62)");
|
||||
g_check_parse_ok(not_2, (HParserBackend)GPOINTER_TO_INT(backend), "a++b", 4, "(u0x61 <2b.2b> u0x62)");
|
||||
}
|
||||
|
||||
static void test_leftrec(gconstpointer backend) {
|
||||
const HParser *a_ = h_ch('a');
|
||||
|
||||
HParser *lr_ = h_indirect();
|
||||
h_bind_indirect(lr_, h_choice(h_sequence(lr_, a_, NULL), a_, NULL));
|
||||
|
||||
g_check_parse_ok(lr_, (HParserBackend)GPOINTER_TO_INT(backend), "a", 1, "u0x61");
|
||||
g_check_parse_ok(lr_, (HParserBackend)GPOINTER_TO_INT(backend), "aa", 2, "(u0x61 u0x61)");
|
||||
g_check_parse_ok(lr_, (HParserBackend)GPOINTER_TO_INT(backend), "aaa", 3, "((u0x61 u0x61) u0x61)");
|
||||
}
|
||||
|
||||
void register_parser_tests(void) {
|
||||
g_test_add_func("/core/parser/token", test_token);
|
||||
g_test_add_func("/core/parser/ch", test_ch);
|
||||
g_test_add_func("/core/parser/ch_range", test_ch_range);
|
||||
g_test_add_func("/core/parser/int64", test_int64);
|
||||
g_test_add_func("/core/parser/int32", test_int32);
|
||||
g_test_add_func("/core/parser/int16", test_int16);
|
||||
g_test_add_func("/core/parser/int8", test_int8);
|
||||
g_test_add_func("/core/parser/uint64", test_uint64);
|
||||
g_test_add_func("/core/parser/uint32", test_uint32);
|
||||
g_test_add_func("/core/parser/uint16", test_uint16);
|
||||
g_test_add_func("/core/parser/uint8", test_uint8);
|
||||
g_test_add_func("/core/parser/int_range", test_int_range);
|
||||
g_test_add_data_func("/core/parser/packrat/token", GINT_TO_POINTER(PB_PACKRAT), test_token);
|
||||
g_test_add_data_func("/core/parser/packrat/ch", GINT_TO_POINTER(PB_PACKRAT), test_ch);
|
||||
g_test_add_data_func("/core/parser/packrat/ch_range", GINT_TO_POINTER(PB_PACKRAT), test_ch_range);
|
||||
g_test_add_data_func("/core/parser/packrat/int64", GINT_TO_POINTER(PB_PACKRAT), test_int64);
|
||||
g_test_add_data_func("/core/parser/packrat/int32", GINT_TO_POINTER(PB_PACKRAT), test_int32);
|
||||
g_test_add_data_func("/core/parser/packrat/int16", GINT_TO_POINTER(PB_PACKRAT), test_int16);
|
||||
g_test_add_data_func("/core/parser/packrat/int8", GINT_TO_POINTER(PB_PACKRAT), test_int8);
|
||||
g_test_add_data_func("/core/parser/packrat/uint64", GINT_TO_POINTER(PB_PACKRAT), test_uint64);
|
||||
g_test_add_data_func("/core/parser/packrat/uint32", GINT_TO_POINTER(PB_PACKRAT), test_uint32);
|
||||
g_test_add_data_func("/core/parser/packrat/uint16", GINT_TO_POINTER(PB_PACKRAT), test_uint16);
|
||||
g_test_add_data_func("/core/parser/packrat/uint8", GINT_TO_POINTER(PB_PACKRAT), test_uint8);
|
||||
g_test_add_data_func("/core/parser/packrat/int_range", GINT_TO_POINTER(PB_PACKRAT), test_int_range);
|
||||
#if 0
|
||||
g_test_add_func("/core/parser/float64", test_float64);
|
||||
g_test_add_func("/core/parser/float32", test_float32);
|
||||
g_test_add_data_func("/core/parser/packrat/float64", GINT_TO_POINTER(PB_PACKRAT), test_float64);
|
||||
g_test_add_data_func("/core/parser/packrat/float32", GINT_TO_POINTER(PB_PACKRAT), test_float32);
|
||||
#endif
|
||||
g_test_add_func("/core/parser/whitespace", test_whitespace);
|
||||
g_test_add_func("/core/parser/left", test_left);
|
||||
g_test_add_func("/core/parser/right", test_right);
|
||||
g_test_add_func("/core/parser/middle", test_middle);
|
||||
g_test_add_func("/core/parser/action", test_action);
|
||||
g_test_add_func("/core/parser/in", test_in);
|
||||
g_test_add_func("/core/parser/not_in", test_not_in);
|
||||
g_test_add_func("/core/parser/end_p", test_end_p);
|
||||
g_test_add_func("/core/parser/nothing_p", test_nothing_p);
|
||||
g_test_add_func("/core/parser/sequence", test_sequence);
|
||||
g_test_add_func("/core/parser/choice", test_choice);
|
||||
g_test_add_func("/core/parser/butnot", test_butnot);
|
||||
g_test_add_func("/core/parser/difference", test_difference);
|
||||
g_test_add_func("/core/parser/xor", test_xor);
|
||||
g_test_add_func("/core/parser/many", test_many);
|
||||
g_test_add_func("/core/parser/many1", test_many1);
|
||||
g_test_add_func("/core/parser/repeat_n", test_repeat_n);
|
||||
g_test_add_func("/core/parser/optional", test_optional);
|
||||
g_test_add_func("/core/parser/sepBy", test_sepBy);
|
||||
g_test_add_func("/core/parser/sepBy1", test_sepBy1);
|
||||
g_test_add_func("/core/parser/epsilon_p", test_epsilon_p);
|
||||
g_test_add_func("/core/parser/attr_bool", test_attr_bool);
|
||||
g_test_add_func("/core/parser/and", test_and);
|
||||
g_test_add_func("/core/parser/not", test_not);
|
||||
g_test_add_func("/core/parser/ignore", test_ignore);
|
||||
g_test_add_data_func("/core/parser/packrat/whitespace", GINT_TO_POINTER(PB_PACKRAT), test_whitespace);
|
||||
g_test_add_data_func("/core/parser/packrat/left", GINT_TO_POINTER(PB_PACKRAT), test_left);
|
||||
g_test_add_data_func("/core/parser/packrat/right", GINT_TO_POINTER(PB_PACKRAT), test_right);
|
||||
g_test_add_data_func("/core/parser/packrat/middle", GINT_TO_POINTER(PB_PACKRAT), test_middle);
|
||||
g_test_add_data_func("/core/parser/packrat/action", GINT_TO_POINTER(PB_PACKRAT), test_action);
|
||||
g_test_add_data_func("/core/parser/packrat/in", GINT_TO_POINTER(PB_PACKRAT), test_in);
|
||||
g_test_add_data_func("/core/parser/packrat/not_in", GINT_TO_POINTER(PB_PACKRAT), test_not_in);
|
||||
g_test_add_data_func("/core/parser/packrat/end_p", GINT_TO_POINTER(PB_PACKRAT), test_end_p);
|
||||
g_test_add_data_func("/core/parser/packrat/nothing_p", GINT_TO_POINTER(PB_PACKRAT), test_nothing_p);
|
||||
g_test_add_data_func("/core/parser/packrat/sequence", GINT_TO_POINTER(PB_PACKRAT), test_sequence);
|
||||
g_test_add_data_func("/core/parser/packrat/choice", GINT_TO_POINTER(PB_PACKRAT), test_choice);
|
||||
g_test_add_data_func("/core/parser/packrat/butnot", GINT_TO_POINTER(PB_PACKRAT), test_butnot);
|
||||
g_test_add_data_func("/core/parser/packrat/difference", GINT_TO_POINTER(PB_PACKRAT), test_difference);
|
||||
g_test_add_data_func("/core/parser/packrat/xor", GINT_TO_POINTER(PB_PACKRAT), test_xor);
|
||||
g_test_add_data_func("/core/parser/packrat/many", GINT_TO_POINTER(PB_PACKRAT), test_many);
|
||||
g_test_add_data_func("/core/parser/packrat/many1", GINT_TO_POINTER(PB_PACKRAT), test_many1);
|
||||
g_test_add_data_func("/core/parser/packrat/repeat_n", GINT_TO_POINTER(PB_PACKRAT), test_repeat_n);
|
||||
g_test_add_data_func("/core/parser/packrat/optional", GINT_TO_POINTER(PB_PACKRAT), test_optional);
|
||||
g_test_add_data_func("/core/parser/packrat/sepBy", GINT_TO_POINTER(PB_PACKRAT), test_sepBy);
|
||||
g_test_add_data_func("/core/parser/packrat/sepBy1", GINT_TO_POINTER(PB_PACKRAT), test_sepBy1);
|
||||
g_test_add_data_func("/core/parser/packrat/epsilon_p", GINT_TO_POINTER(PB_PACKRAT), test_epsilon_p);
|
||||
g_test_add_data_func("/core/parser/packrat/attr_bool", GINT_TO_POINTER(PB_PACKRAT), test_attr_bool);
|
||||
g_test_add_data_func("/core/parser/packrat/and", GINT_TO_POINTER(PB_PACKRAT), test_and);
|
||||
g_test_add_data_func("/core/parser/packrat/not", GINT_TO_POINTER(PB_PACKRAT), test_not);
|
||||
g_test_add_data_func("/core/parser/packrat/ignore", GINT_TO_POINTER(PB_PACKRAT), test_ignore);
|
||||
g_test_add_data_func("/core/parser/leftrec", GINT_TO_POINTER(PB_PACKRAT), test_leftrec);
|
||||
|
||||
g_test_add_data_func("/core/parser/llk/token", GINT_TO_POINTER(PB_LLk), test_token);
|
||||
g_test_add_data_func("/core/parser/llk/ch", GINT_TO_POINTER(PB_LLk), test_ch);
|
||||
g_test_add_data_func("/core/parser/llk/ch_range", GINT_TO_POINTER(PB_LLk), test_ch_range);
|
||||
g_test_add_data_func("/core/parser/llk/int64", GINT_TO_POINTER(PB_LLk), test_int64);
|
||||
g_test_add_data_func("/core/parser/llk/int32", GINT_TO_POINTER(PB_LLk), test_int32);
|
||||
g_test_add_data_func("/core/parser/llk/int16", GINT_TO_POINTER(PB_LLk), test_int16);
|
||||
g_test_add_data_func("/core/parser/llk/int8", GINT_TO_POINTER(PB_LLk), test_int8);
|
||||
g_test_add_data_func("/core/parser/llk/uint64", GINT_TO_POINTER(PB_LLk), test_uint64);
|
||||
g_test_add_data_func("/core/parser/llk/uint32", GINT_TO_POINTER(PB_LLk), test_uint32);
|
||||
g_test_add_data_func("/core/parser/llk/uint16", GINT_TO_POINTER(PB_LLk), test_uint16);
|
||||
g_test_add_data_func("/core/parser/llk/uint8", GINT_TO_POINTER(PB_LLk), test_uint8);
|
||||
g_test_add_data_func("/core/parser/llk/int_range", GINT_TO_POINTER(PB_LLk), test_int_range);
|
||||
#if 0
|
||||
g_test_add_data_func("/core/parser/llk/float64", GINT_TO_POINTER(PB_LLk), test_float64);
|
||||
g_test_add_data_func("/core/parser/llk/float32", GINT_TO_POINTER(PB_LLk), test_float32);
|
||||
#endif
|
||||
g_test_add_data_func("/core/parser/llk/whitespace", GINT_TO_POINTER(PB_LLk), test_whitespace);
|
||||
g_test_add_data_func("/core/parser/llk/left", GINT_TO_POINTER(PB_LLk), test_left);
|
||||
g_test_add_data_func("/core/parser/llk/right", GINT_TO_POINTER(PB_LLk), test_right);
|
||||
g_test_add_data_func("/core/parser/llk/middle", GINT_TO_POINTER(PB_LLk), test_middle);
|
||||
g_test_add_data_func("/core/parser/llk/action", GINT_TO_POINTER(PB_LLk), test_action);
|
||||
g_test_add_data_func("/core/parser/llk/in", GINT_TO_POINTER(PB_LLk), test_in);
|
||||
g_test_add_data_func("/core/parser/llk/not_in", GINT_TO_POINTER(PB_LLk), test_not_in);
|
||||
g_test_add_data_func("/core/parser/llk/end_p", GINT_TO_POINTER(PB_LLk), test_end_p);
|
||||
g_test_add_data_func("/core/parser/llk/nothing_p", GINT_TO_POINTER(PB_LLk), test_nothing_p);
|
||||
g_test_add_data_func("/core/parser/llk/sequence", GINT_TO_POINTER(PB_LLk), test_sequence);
|
||||
g_test_add_data_func("/core/parser/llk/choice", GINT_TO_POINTER(PB_LLk), test_choice);
|
||||
g_test_add_data_func("/core/parser/llk/many", GINT_TO_POINTER(PB_LLk), test_many);
|
||||
g_test_add_data_func("/core/parser/llk/many1", GINT_TO_POINTER(PB_LLk), test_many1);
|
||||
g_test_add_data_func("/core/parser/llk/optional", GINT_TO_POINTER(PB_LLk), test_optional);
|
||||
g_test_add_data_func("/core/parser/llk/sepBy", GINT_TO_POINTER(PB_LLk), test_sepBy);
|
||||
g_test_add_data_func("/core/parser/llk/sepBy1", GINT_TO_POINTER(PB_LLk), test_sepBy1);
|
||||
g_test_add_data_func("/core/parser/llk/epsilon_p", GINT_TO_POINTER(PB_LLk), test_epsilon_p);
|
||||
g_test_add_data_func("/core/parser/llk/attr_bool", GINT_TO_POINTER(PB_LLk), test_attr_bool);
|
||||
g_test_add_data_func("/core/parser/llk/ignore", GINT_TO_POINTER(PB_LLk), test_ignore);
|
||||
g_test_add_data_func("/core/parser/leftrec", GINT_TO_POINTER(PB_LLk), test_leftrec);
|
||||
|
||||
g_test_add_data_func("/core/parser/regex/token", GINT_TO_POINTER(PB_REGULAR), test_token);
|
||||
g_test_add_data_func("/core/parser/regex/ch", GINT_TO_POINTER(PB_REGULAR), test_ch);
|
||||
g_test_add_data_func("/core/parser/regex/ch_range", GINT_TO_POINTER(PB_REGULAR), test_ch_range);
|
||||
g_test_add_data_func("/core/parser/regex/int64", GINT_TO_POINTER(PB_REGULAR), test_int64);
|
||||
g_test_add_data_func("/core/parser/regex/int32", GINT_TO_POINTER(PB_REGULAR), test_int32);
|
||||
g_test_add_data_func("/core/parser/regex/int16", GINT_TO_POINTER(PB_REGULAR), test_int16);
|
||||
g_test_add_data_func("/core/parser/regex/int8", GINT_TO_POINTER(PB_REGULAR), test_int8);
|
||||
g_test_add_data_func("/core/parser/regex/uint64", GINT_TO_POINTER(PB_REGULAR), test_uint64);
|
||||
g_test_add_data_func("/core/parser/regex/uint32", GINT_TO_POINTER(PB_REGULAR), test_uint32);
|
||||
g_test_add_data_func("/core/parser/regex/uint16", GINT_TO_POINTER(PB_REGULAR), test_uint16);
|
||||
g_test_add_data_func("/core/parser/regex/uint8", GINT_TO_POINTER(PB_REGULAR), test_uint8);
|
||||
g_test_add_data_func("/core/parser/regex/int_range", GINT_TO_POINTER(PB_REGULAR), test_int_range);
|
||||
#if 0
|
||||
g_test_add_data_func("/core/parser/regex/float64", GINT_TO_POINTER(PB_REGULAR), test_float64);
|
||||
g_test_add_data_func("/core/parser/regex/float32", GINT_TO_POINTER(PB_REGULAR), test_float32);
|
||||
#endif
|
||||
g_test_add_data_func("/core/parser/regex/whitespace", GINT_TO_POINTER(PB_REGULAR), test_whitespace);
|
||||
g_test_add_data_func("/core/parser/regex/left", GINT_TO_POINTER(PB_REGULAR), test_left);
|
||||
g_test_add_data_func("/core/parser/regex/right", GINT_TO_POINTER(PB_REGULAR), test_right);
|
||||
g_test_add_data_func("/core/parser/regex/middle", GINT_TO_POINTER(PB_REGULAR), test_middle);
|
||||
g_test_add_data_func("/core/parser/regex/action", GINT_TO_POINTER(PB_REGULAR), test_action);
|
||||
g_test_add_data_func("/core/parser/regex/in", GINT_TO_POINTER(PB_REGULAR), test_in);
|
||||
g_test_add_data_func("/core/parser/regex/not_in", GINT_TO_POINTER(PB_REGULAR), test_not_in);
|
||||
g_test_add_data_func("/core/parser/regex/end_p", GINT_TO_POINTER(PB_REGULAR), test_end_p);
|
||||
g_test_add_data_func("/core/parser/regex/nothing_p", GINT_TO_POINTER(PB_REGULAR), test_nothing_p);
|
||||
g_test_add_data_func("/core/parser/regex/sequence", GINT_TO_POINTER(PB_REGULAR), test_sequence);
|
||||
g_test_add_data_func("/core/parser/regex/choice", GINT_TO_POINTER(PB_REGULAR), test_choice);
|
||||
g_test_add_data_func("/core/parser/regex/many", GINT_TO_POINTER(PB_REGULAR), test_many);
|
||||
g_test_add_data_func("/core/parser/regex/many1", GINT_TO_POINTER(PB_REGULAR), test_many1);
|
||||
g_test_add_data_func("/core/parser/regex/optional", GINT_TO_POINTER(PB_REGULAR), test_optional);
|
||||
g_test_add_data_func("/core/parser/regex/sepBy", GINT_TO_POINTER(PB_REGULAR), test_sepBy);
|
||||
g_test_add_data_func("/core/parser/regex/sepBy1", GINT_TO_POINTER(PB_REGULAR), test_sepBy1);
|
||||
g_test_add_data_func("/core/parser/regex/epsilon_p", GINT_TO_POINTER(PB_REGULAR), test_epsilon_p);
|
||||
g_test_add_data_func("/core/parser/regex/attr_bool", GINT_TO_POINTER(PB_REGULAR), test_attr_bool);
|
||||
g_test_add_data_func("/core/parser/regex/ignore", GINT_TO_POINTER(PB_REGULAR), test_ignore);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ extern void register_bitreader_tests();
|
|||
extern void register_bitwriter_tests();
|
||||
extern void register_parser_tests();
|
||||
extern void register_grammar_tests();
|
||||
extern void register_misc_tests();
|
||||
extern void register_benchmark_tests();
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
@ -33,6 +34,7 @@ int main(int argc, char** argv) {
|
|||
register_bitwriter_tests();
|
||||
register_parser_tests();
|
||||
register_grammar_tests();
|
||||
register_misc_tests();
|
||||
register_benchmark_tests();
|
||||
|
||||
g_test_run();
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@
|
|||
} \
|
||||
} while(0)
|
||||
|
||||
#define g_check_parse_failed(parser, input, inp_len) do { \
|
||||
int skip = h_compile((HParser *)(parser), PB_LLk, NULL); \
|
||||
#define g_check_parse_failed(parser, backend, input, inp_len) do { \
|
||||
int skip = h_compile((HParser *)(parser), (HParserBackend)backend, NULL); \
|
||||
if(skip != 0) { \
|
||||
g_test_message("Backend not applicable, skipping test"); \
|
||||
break; \
|
||||
|
|
@ -96,9 +96,9 @@
|
|||
} \
|
||||
} while(0)
|
||||
|
||||
#define g_check_parse_ok(parser, input, inp_len, result) do { \
|
||||
int skip = h_compile((HParser *)(parser), PB_LLk, NULL); \
|
||||
if(skip) { \
|
||||
#define g_check_parse_ok(parser, backend, input, inp_len, result) do { \
|
||||
int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \
|
||||
if(skip) { \
|
||||
g_test_message("Backend not applicable, skipping test"); \
|
||||
break; \
|
||||
} \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue