"); return; }
+ if (p->retained && !force) { printf(""); return; }
if (p->type == MPC_TYPE_UNDEFINED) { printf(""); }
if (p->type == MPC_TYPE_PASS) { printf(""); }
if (p->type == MPC_TYPE_FAIL) { printf(""); }
if (p->type == MPC_TYPE_LIFT) { printf(""); }
- if (p->type == MPC_TYPE_EXPECT) { mpc_print_unretained(p->data.expect.x, false); }
+ if (p->type == MPC_TYPE_EXPECT) {
+ printf(p->data.expect.m);
+ /*mpc_print_unretained(p->data.expect.x, false);*/
+ }
if (p->type == MPC_TYPE_ANY) { printf(""); }
if (p->type == MPC_TYPE_SATISFY) { printf("", p->data.satisfy.f); }
@@ -1388,16 +1731,17 @@ static void mpc_print_unretained(mpc_parser_t* p, bool force) {
}
if (p->type == MPC_TYPE_APPLY) { mpc_print_unretained(p->data.apply.x, false); }
+ if (p->type == MPC_TYPE_NOT) { printf("!"); mpc_print_unretained(p->data.not.x, false); }
if (p->type == MPC_TYPE_MAYBE) { printf("("); mpc_print_unretained(p->data.repeat.x, false); printf(")?"); }
if (p->type == MPC_TYPE_MANY) { printf("("); mpc_print_unretained(p->data.repeat.x, false); printf(")*"); }
if (p->type == MPC_TYPE_MANY1) { printf("("); mpc_print_unretained(p->data.repeat.x, false); printf(")+"); }
if (p->type == MPC_TYPE_COUNT) { printf("("); mpc_print_unretained(p->data.repeat.x, false); printf("){%i}", p->data.repeat.n); }
- if (p->type == MPC_TYPE_EITHER) {
+ if (p->type == MPC_TYPE_ELSE) {
printf("(");
- mpc_print_unretained(p->data.either.x, false);
+ mpc_print_unretained(p->data.orelse.x, false);
printf(" | ");
- mpc_print_unretained(p->data.either.y, false);
+ mpc_print_unretained(p->data.orelse.y, false);
printf(")");
}
@@ -1438,31 +1782,51 @@ void mpc_print(mpc_parser_t* p) {
** Testing
*/
-bool mpc_test(mpc_parser_t* p, const char* s, void* data,
- bool(*tester)(void*, void*),
- void(*destructor)(void*),
+
+bool mpc_unmatch(mpc_parser_t* p, const char* s, void* d,
+ bool(*tester)(void*, void*),
+ mpc_dtor_t destructor,
void(*printer)(void*)) {
-
- printf("== Test ==: '%s'\n", s);
-
+
mpc_result_t r;
- if (mpc_parse(s, p, &r)) {
+ if (mpc_parse("", s, p, &r)) {
+
+ if (tester(r.output, d)) {
+ destructor(r.output);
+ return false;
+ } else {
+ destructor(r.output);
+ return true;
+ }
+
+ } else {
+ mpc_err_delete(r.error);
+ return true;
+ }
+
+}
+
+bool mpc_match(mpc_parser_t* p, const char* s, void* d,
+ bool(*tester)(void*, void*),
+ mpc_dtor_t destructor,
+ void(*printer)(void*)) {
+
+ mpc_result_t r;
+ if (mpc_parse("", s, p, &r)) {
- if (tester(r.output, data)) {
- printf("Passed with "); printer(r.output); printf("\n");
+ if (tester(r.output, d)) {
+ /*printf("Passed with "); printer(r.output); printf("\n");*/
destructor(r.output);
return true;
} else {
printf("Failed!\n");
printf("Got "); printer(r.output); printf("\n");
- printf("Expected "); printer(data); printf("\n");
+ printf("Expected "); printer(d); printf("\n");
destructor(r.output);
return false;
}
- } else {
-
- printf("Failed!\n");
+ } else {
mpc_err_print(r.error);
mpc_err_delete(r.error);
return false;
@@ -1471,3 +1835,321 @@ bool mpc_test(mpc_parser_t* p, const char* s, void* data,
}
+
+/*
+** AST
+*/
+
+void mpc_ast_delete(mpc_ast_t* a) {
+
+ int i;
+ for (i = 0; i < a->children_num; i++) {
+ mpc_ast_delete(a->children[i]);
+ }
+
+ free(a->children);
+ free(a->contents);
+ free(a);
+
+}
+
+mpc_ast_t* mpc_ast_new(char* contents) {
+
+ mpc_ast_t* a = malloc(sizeof(mpc_ast_t));
+ a->tag = 0;
+ a->contents = calloc(1, 1);
+ a->children_num = 0;
+ a->children = NULL;
+ return a;
+
+}
+
+mpc_ast_t* mpc_ast_empty(void) {
+ return mpc_ast_new("");
+}
+
+void mpc_ast_add_child(mpc_ast_t* r, mpc_ast_t* a) {
+
+ a->children_num++;
+ a->children = realloc(a->children, sizeof(mpc_ast_t*) * a->children_num);
+ a->children[a->children_num-1] = a;
+
+}
+
+mpc_ast_t* mpc_ast_tag(mpc_ast_t* a, int t) {
+ a->tag = t;
+ return a;
+}
+
+static void mpc_ast_print_depth(mpc_ast_t* a, int d) {
+
+ int i;
+ for (i = 0; i < d; i++) { printf("\t"); }
+
+ printf("-> %s", a->contents);
+
+ for (i = 0; i < a->children_num; i++) {
+ mpc_ast_print_depth(a->children[i], d+1);
+ }
+
+}
+
+void mpc_ast_print(mpc_ast_t* a) {
+ mpc_ast_print_depth(a, 0);
+}
+
+mpc_val_t* mpcf_fold_ast(mpc_val_t* a, mpc_val_t* b) {
+
+ if (a == NULL) { return b; }
+ if (b == NULL) { return a; }
+
+ mpc_ast_t* r = mpc_ast_empty();
+ mpc_ast_add_child(r, a);
+ mpc_ast_add_child(r, b);
+ return r;
+}
+
+mpc_val_t* mpcf_afold_ast(int n, mpc_val_t** as) {
+
+ mpc_val_t* t = NULL;
+ int i;
+ for (i = 0; i < n; i++) {
+ t = mpcf_fold_ast(t, as[i]);
+ }
+
+ return t;
+}
+
+mpc_val_t* mpcf_apply_str_ast(mpc_val_t* c) {
+ mpc_ast_t* a = mpc_ast_new(c);
+ free(c);
+ return a;
+}
+
+mpc_val_t* mpcf_lift_ast(void) {
+ return mpc_ast_empty();
+}
+
+mpc_parser_t* mpc_ast(mpc_parser_t* a) {
+ return mpc_apply(a, mpcf_apply_str_ast);
+}
+
+mpc_parser_t* mpca_not(mpc_parser_t* a) { return mpc_not_else(a, (mpc_dtor_t)mpc_ast_delete, mpcf_lift_ast); }
+mpc_parser_t* mpca_maybe(mpc_parser_t* a) { return mpc_maybe_else(a, mpcf_lift_ast); }
+mpc_parser_t* mpca_many(mpc_parser_t* a) { return mpc_many_else(a, mpcf_fold_ast, mpcf_lift_ast); }
+mpc_parser_t* mpca_many1(mpc_parser_t* a) { return mpc_many1(a, mpcf_fold_ast); }
+mpc_parser_t* mpca_count(mpc_parser_t* a, int n) { return mpc_count_else(a, (mpc_dtor_t)mpc_ast_delete, mpcf_fold_ast, n, mpcf_lift_ast); }
+mpc_parser_t* mpca_else(mpc_parser_t* a, mpc_parser_t* b) { return mpc_else(a, b); }
+mpc_parser_t* mpca_also(mpc_parser_t* a, mpc_parser_t* b) { return mpc_also(a, b, (mpc_dtor_t)mpc_ast_delete, mpcf_fold_ast); }
+mpc_parser_t* mpca_bind(mpc_parser_t* a, mpc_parser_t* b) { return mpca_also(a, b); }
+
+mpc_parser_t* mpca_or(int n, ...) {
+ va_list va;
+ va_start(va, n);
+ mpc_parser_t* p = mpc_ast(mpc_or_va(n, va));
+ va_end(va);
+ return p;
+}
+
+mpc_parser_t* mpca_and(int n, ...) {
+
+ va_list va;
+ va_start(va, n);
+
+ mpc_parser_t* p = mpc_undefined();
+
+ p->type = MPC_TYPE_AND;
+ p->data.and.n = n;
+ p->data.and.f = mpcf_afold_ast;
+ p->data.and.xs = malloc(sizeof(mpc_parser_t*) * n);
+ p->data.and.dxs = malloc(sizeof(mpc_dtor_t) * (n-1));
+
+ int i;
+ for (i = 0; i < n; i++) {
+ p->data.and.xs[i] = va_arg(va, mpc_parser_t*);
+ }
+ for (i = 0; i < (n-1); i++) {
+ p->data.and.dxs[i] = (mpc_dtor_t)mpc_ast_delete;
+ }
+
+ va_end(va);
+
+ return mpc_ast(p);
+}
+
+mpc_parser_t* mpca_ends(mpc_parser_t* a) { return mpc_ends(a, (mpc_dtor_t)mpc_ast_delete); }
+
+/*
+** Grammar Parser
+*/
+
+/*
+** This is another interesting bootstrapping.
+**
+** Having a general purpose AST type allows
+** users to specify the grammar alone and
+** let all fold rules be automatically taken
+** care of by existing functions.
+**
+** You don't get to control the type spat
+** out but this means you can make a nice
+** parser to take in some grammar in nice
+** syntax and spit out a parser that works.
+**
+** The grammar for this looks surprisingly
+** like regex but the main difference is that
+** it is now whitespace insensitive and the
+** base type takes literals of some form.
+*/
+
+/*
+**
+** ### Grammar Grammar
+**
+** : ( "|" ) |
+**
+** : *
+**
+** :
+** | "*"
+** | "+"
+** | "?"
+** | "{" "}"
+**
+** : "<" ">"
+** |
+** |
+** |
+** | "(" ")"
+*/
+
+static mpc_val_t* mpca_grammar_afold_or(int n, mpc_val_t** xs) {
+ free(xs[1]);
+ return mpca_else(xs[0], xs[2]);
+}
+
+static mpc_val_t* mpc_grammar_fold_many(mpc_val_t* x, mpc_val_t* y) {
+ if (x == NULL) { return y; }
+ if (y == NULL) { return x; }
+ return mpca_also(x, y);
+}
+
+static mpc_val_t* mpca_grammar_lift(void) {
+ return mpc_lift(mpcf_lift_ast);
+}
+
+static mpc_val_t* mpca_grammar_fold_repeat(mpc_val_t* x, mpc_val_t* y) {
+
+ printf("Got Repeat '%s'\n", (char*)y);
+
+ if (strcmp(y, "*") == 0) { free(y); return mpca_many(x); }
+ if (strcmp(y, "+") == 0) { free(y); return mpca_many1(x); }
+ if (strcmp(y, "?") == 0) { free(y); return mpca_maybe(x); }
+ int n = *((int*)y);
+ free(y);
+ return mpca_count(x, n);
+}
+
+static mpc_val_t* mpc_grammar_apply_string(mpc_val_t* x) {
+ mpc_parser_t* p = mpc_ast(mpc_string(mpcf_unescape(x)));
+ free(x);
+ return p;
+}
+
+static mpc_val_t* mpc_grammar_apply_char(mpc_val_t* x) {
+ mpc_parser_t* p = mpc_ast(mpc_char(*(char*)mpcf_unescape(x)));
+ free(x);
+ return p;
+}
+
+static mpc_val_t* mpc_grammar_apply_regex(mpc_val_t* x) {
+ /* TODO: Unescape Regex */
+ mpc_parser_t* p = mpc_ast(mpc_re(x));
+ free(x);
+ return p;
+}
+
+typedef struct {
+ va_list* va;
+ int parsers_num;
+ mpc_parser_t** parsers;
+} mpc_grammar_st_t;
+
+static mpc_val_t* mpc_grammar_apply_id(mpc_val_t* x, void* y) {
+ int i = *((int*)x);
+ mpc_grammar_st_t* st = y;
+
+ while (st->parsers_num <= i) {
+ st->parsers_num++;
+ st->parsers = realloc(st->parsers, sizeof(mpc_parser_t*) * st->parsers_num);
+ st->parsers[st->parsers_num-1] = va_arg(*st->va, mpc_parser_t*);
+ }
+
+ return st->parsers[i];
+}
+
+static void mpc_soft_delete(mpc_val_t* x) {
+ mpc_undefine_unretained(x, false);
+}
+
+mpc_parser_t* mpca_grammar(const char* grammar, ...) {
+
+ mpc_parser_t* Grammar = mpc_new();
+ mpc_parser_t* Term = mpc_new();
+ mpc_parser_t* Factor = mpc_new();
+ mpc_parser_t* Base = mpc_new();
+
+ mpc_define(Grammar, mpc_else(
+ mpc_and(3, mpca_grammar_afold_or, Term, mpc_sym("|"), Grammar, mpc_soft_delete, free),
+ Term
+ ));
+
+ mpc_define(Term, mpc_many_else(Factor, mpc_grammar_fold_many, mpca_grammar_lift));
+
+ mpc_define(Factor, mpc_or(5,
+ mpc_also(Base, mpc_sym("*"), mpc_soft_delete, mpca_grammar_fold_repeat),
+ mpc_also(Base, mpc_sym("+"), mpc_soft_delete, mpca_grammar_fold_repeat),
+ mpc_also(Base, mpc_sym("?"), mpc_soft_delete, mpca_grammar_fold_repeat),
+ mpc_also(Base, mpc_tok(mpc_brackets(mpc_int(), free)), mpc_soft_delete, mpca_grammar_fold_repeat),
+ Base
+ ));
+
+ va_list va;
+ va_start(va, grammar);
+
+ mpc_grammar_st_t st = { &va, 0, NULL };
+
+ mpc_define(Base, mpc_or(5,
+ mpc_apply(mpc_tok(mpc_string_lit()), mpc_grammar_apply_string),
+ mpc_apply(mpc_tok(mpc_char_lit()), mpc_grammar_apply_char),
+ mpc_apply(mpc_tok(mpc_regex_lit()), mpc_grammar_apply_regex),
+ mpc_apply_to(mpc_tok(mpc_braces(mpc_int(), free)), mpc_grammar_apply_id, &st),
+ mpc_tok(mpc_parens(Grammar, mpc_soft_delete))
+ ));
+
+ mpc_result_t r;
+ bool res = mpc_parse("", grammar, mpc_ends(Grammar, mpc_soft_delete), &r);
+
+ free(st.parsers);
+ va_end(va);
+
+ mpc_undefine(Grammar);
+ mpc_undefine(Term);
+ mpc_undefine(Factor);
+ mpc_undefine(Base);
+
+ mpc_delete(Grammar);
+ mpc_delete(Term);
+ mpc_delete(Factor);
+ mpc_delete(Base);
+
+ if (res) {
+ return r.output;
+ } else {
+ fprintf(stderr, "\nError Compiling Grammar: '%s' ", grammar);
+ mpc_err_print(r.error);
+ abort();
+ }
+
+}
+
diff --git a/mpc.h b/mpc.h
index a37b6a6..13d3bdc 100644
--- a/mpc.h
+++ b/mpc.h
@@ -22,6 +22,7 @@ int mpc_err_line(mpc_err_t* x);
int mpc_err_column(mpc_err_t* x);
char mpc_err_unexpected(mpc_err_t* x);
char** mpc_err_expected(mpc_err_t* x, int* num);
+char* mpc_err_filename(mpc_err_t* x);
void mpc_err_delete(mpc_err_t* x);
void mpc_err_print(mpc_err_t* x);
@@ -42,10 +43,21 @@ typedef union {
struct mpc_parser_t;
typedef struct mpc_parser_t mpc_parser_t;
-bool mpc_parse(const char* s, mpc_parser_t* p, mpc_result_t* r);
-bool mpc_parse_file(FILE* f, mpc_parser_t* p, mpc_result_t* r);
+bool mpc_parse(const char* filename, const char* s, mpc_parser_t* p, mpc_result_t* r);
+bool mpc_parse_file(const char* filename, FILE* f, mpc_parser_t* p, mpc_result_t* r);
bool mpc_parse_filename(const char* filename, mpc_parser_t* p, mpc_result_t* r);
+/*
+** Function Types
+*/
+
+typedef void(*mpc_dtor_t)(mpc_val_t*);
+typedef mpc_val_t*(*mpc_apply_t)(mpc_val_t*);
+typedef mpc_val_t*(*mpc_apply_to_t)(mpc_val_t*,void*);
+typedef mpc_val_t*(*mpc_fold_t)(mpc_val_t*,mpc_val_t*);
+typedef mpc_val_t*(*mpc_afold_t)(int,mpc_val_t**);
+typedef mpc_val_t*(*mpc_lift_t)(void);
+
/*
** Building a Parser
*/
@@ -56,11 +68,11 @@ mpc_parser_t* mpc_new(void);
mpc_parser_t* mpc_define(mpc_parser_t* p, mpc_parser_t* a);
mpc_parser_t* mpc_undefine(mpc_parser_t* p);
-mpc_parser_t* mpc_expect(mpc_parser_t* a, const char* expected);
-
mpc_parser_t* mpc_pass(void);
mpc_parser_t* mpc_fail(void);
-mpc_parser_t* mpc_lift(mpc_val_t* x);
+mpc_parser_t* mpc_lift(mpc_lift_t f);
+mpc_parser_t* mpc_lift_val(mpc_val_t* x);
+mpc_parser_t* mpc_expect(mpc_parser_t* a, const char* expected);
/*
** Basic Parsers
@@ -74,31 +86,28 @@ mpc_parser_t* mpc_noneof(const char* s);
mpc_parser_t* mpc_satisfy(bool(*f)(char));
mpc_parser_t* mpc_string(const char* s);
-/*
-** Function Types
-*/
-
-typedef void (*mpc_dtor_t)(mpc_val_t*);
-typedef mpc_val_t*(*mpc_apply_t)(mpc_val_t*);
-typedef mpc_val_t*(*mpc_fold_t)(mpc_val_t*,mpc_val_t*);
-typedef mpc_val_t*(*mpc_afold_t)(int,mpc_val_t**);
-
-void mpc_dtor_null(mpc_val_t* x);
-
/*
** Core Parsers
*/
mpc_parser_t* mpc_apply(mpc_parser_t* a, mpc_apply_t f);
+mpc_parser_t* mpc_apply_to(mpc_parser_t* a, mpc_apply_to_t f, void* x);
+mpc_parser_t* mpc_not(mpc_parser_t* a, mpc_dtor_t da);
+mpc_parser_t* mpc_not_else(mpc_parser_t* a, mpc_dtor_t da, mpc_lift_t lf);
mpc_parser_t* mpc_maybe(mpc_parser_t* a);
+mpc_parser_t* mpc_maybe_else(mpc_parser_t* a, mpc_lift_t lf);
mpc_parser_t* mpc_many(mpc_parser_t* a, mpc_fold_t f);
+mpc_parser_t* mpc_many_else(mpc_parser_t* a, mpc_fold_t f, mpc_lift_t lf);
mpc_parser_t* mpc_many1(mpc_parser_t* a, mpc_fold_t f);
mpc_parser_t* mpc_count(mpc_parser_t* a, mpc_dtor_t da, mpc_fold_t f, int n);
-mpc_parser_t* mpc_either(mpc_parser_t* a, mpc_parser_t* b);
+mpc_parser_t* mpc_count_else(mpc_parser_t* a, mpc_dtor_t da, mpc_fold_t f, int n, mpc_lift_t lf);
+mpc_parser_t* mpc_else(mpc_parser_t* a, mpc_parser_t* b);
mpc_parser_t* mpc_also(mpc_parser_t* a, mpc_parser_t* b, mpc_dtor_t da, mpc_fold_t f);
mpc_parser_t* mpc_bind(mpc_parser_t* a, mpc_parser_t* b, mpc_dtor_t da, mpc_fold_t f);
mpc_parser_t* mpc_or(int n, ...);
mpc_parser_t* mpc_and(int n, mpc_afold_t f, ...);
+mpc_parser_t* mpc_or_va(int n, va_list va);
+mpc_parser_t* mpc_and_va(int n, mpc_afold_t f, va_list va);
/*
** Common Parsers
@@ -124,12 +133,14 @@ mpc_parser_t* mpc_lower(void);
mpc_parser_t* mpc_upper(void);
mpc_parser_t* mpc_alpha(void);
mpc_parser_t* mpc_underscore(void);
+mpc_parser_t* mpc_alphanum(void);
mpc_parser_t* mpc_int(void);
mpc_parser_t* mpc_hex(void);
mpc_parser_t* mpc_oct(void);
mpc_parser_t* mpc_number(void);
+mpc_parser_t* mpc_real(void);
mpc_parser_t* mpc_float(void);
mpc_parser_t* mpc_semi(void);
@@ -168,11 +179,17 @@ mpc_parser_t* mpc_re(const char* re);
** Common Fold Functions
*/
+void mpcf_dtor_null(mpc_val_t* x);
+mpc_val_t* mpcf_lift_null(void);
+mpc_val_t* mpcf_lift_emptystr(void);
+
mpc_val_t* mpcf_free(mpc_val_t* x);
mpc_val_t* mpcf_int(mpc_val_t* x);
mpc_val_t* mpcf_hex(mpc_val_t* x);
mpc_val_t* mpcf_oct(mpc_val_t* x);
+mpc_val_t* mpcf_float(mpc_val_t* x);
mpc_val_t* mpcf_escape(mpc_val_t* x);
+mpc_val_t* mpcf_unescape(mpc_val_t* x);
mpc_val_t* mpcf_fst(mpc_val_t* x, mpc_val_t* y);
mpc_val_t* mpcf_snd(mpc_val_t* x, mpc_val_t* y);
@@ -183,6 +200,7 @@ mpc_val_t* mpcf_snd_free(mpc_val_t* x, mpc_val_t* y);
mpc_val_t* mpcf_freefold(mpc_val_t* t, mpc_val_t* x);
mpc_val_t* mpcf_strfold(mpc_val_t* t, mpc_val_t* x);
+mpc_val_t* mpcf_astrfold(int n, mpc_val_t** xs);
mpc_val_t* mpcf_between_free(int n, mpc_val_t** xs);
mpc_val_t* mpcf_maths(int n, mpc_val_t** xs);
@@ -193,13 +211,59 @@ mpc_val_t* mpcf_maths(int n, mpc_val_t** xs);
void mpc_print(mpc_parser_t* p);
+
/*
** Testing
*/
-bool mpc_test(mpc_parser_t* p, const char* input, void* data,
- bool(*tester)(void*, void*),
- void(*destructor)(void*),
+bool mpc_unmatch(mpc_parser_t* p, const char* s, void* d,
+ bool(*tester)(void*, void*),
+ mpc_dtor_t destructor,
void(*printer)(void*));
+bool mpc_match(mpc_parser_t* p, const char* s, void* d,
+ bool(*tester)(void*, void*),
+ mpc_dtor_t destructor,
+ void(*printer)(void*));
+
+
+/*
+** AST
+*/
+
+typedef struct mpc_ast_t {
+ int tag;
+ char* contents;
+ int children_num;
+ struct mpc_ast_t** children;
+} mpc_ast_t;
+
+void mpc_ast_delete(mpc_ast_t* a);
+mpc_ast_t* mpc_ast_empty(void);
+mpc_ast_t* mpc_ast_new(char* contents);
+
+void mpc_ast_add_child(mpc_ast_t* r, mpc_ast_t* a);
+mpc_ast_t* mpc_ast_tag(mpc_ast_t* a, int t);
+void mpc_ast_print(mpc_ast_t* a);
+
+mpc_val_t* mpcf_fold_ast(mpc_val_t* a, mpc_val_t* b);
+mpc_val_t* mpcf_afold_ast(int n, mpc_val_t** as);
+mpc_val_t* mpcf_apply_str_ast(mpc_val_t* c);
+mpc_val_t* mpcf_lift_ast(void);
+
+mpc_parser_t* mpc_ast(mpc_parser_t* a);
+
+mpc_parser_t* mpca_not(mpc_parser_t* a);
+mpc_parser_t* mpca_maybe(mpc_parser_t* a);
+mpc_parser_t* mpca_many(mpc_parser_t* a);
+mpc_parser_t* mpca_many1(mpc_parser_t* a);
+mpc_parser_t* mpca_count(mpc_parser_t* a, int n);
+mpc_parser_t* mpca_else(mpc_parser_t* a, mpc_parser_t* b);
+mpc_parser_t* mpca_also(mpc_parser_t* a, mpc_parser_t* b);
+mpc_parser_t* mpca_bind(mpc_parser_t* a, mpc_parser_t* b);
+mpc_parser_t* mpca_or(int n, ...);
+mpc_parser_t* mpca_and(int n, ...);
+mpc_parser_t* mpca_ends(mpc_parser_t* a);
+mpc_parser_t* mpca_grammar(const char* grammar, ...);
+
#endif
\ No newline at end of file
diff --git a/tests/core.c b/tests/core.c
new file mode 100644
index 0000000..9fdf062
--- /dev/null
+++ b/tests/core.c
@@ -0,0 +1,81 @@
+#include "ptest.h"
+#include "../mpc.h"
+
+#include
+#include
+
+static bool int_eq(void* x, void* y) { return (*(int*)x == *(int*)y); }
+static void int_print(void* x) { printf("'%i'", *((int*)x)); }
+static bool string_eq(void* x, void* y) { return (strcmp(x, y) == 0); }
+static void string_print(void* x) { printf("'%s'", (char*)x); }
+
+void test_ident(void) {
+
+ /* ^[a-zA-Z_][a-zA-Z0-9_]*$ */
+
+ mpc_parser_t* Ident = mpc_ends(
+ mpc_also(
+ mpc_else(mpc_alpha(), mpc_underscore()),
+ mpc_many1(mpc_or(3, mpc_alpha(), mpc_underscore(), mpc_digit()), mpcf_strfold),
+ free, mpcf_strfold),
+ free
+ );
+
+ PT_ASSERT(mpc_match(Ident, "test", "test", string_eq, free, string_print));
+ PT_ASSERT(mpc_unmatch(Ident, " blah", "", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(Ident, "anoth21er", "anoth21er", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(Ident, "du__de", "du__de", string_eq, free, string_print));
+ PT_ASSERT(mpc_unmatch(Ident, "some spaces", "", string_eq, free, string_print));
+ PT_ASSERT(mpc_unmatch(Ident, "", "", string_eq, free, string_print));
+ PT_ASSERT(mpc_unmatch(Ident, "18nums", "", string_eq, free, string_print));
+
+ mpc_delete(Ident);
+
+}
+
+void test_maths(void) {
+
+ mpc_parser_t* Expr = mpc_new();
+ mpc_parser_t* Factor = mpc_new();
+ mpc_parser_t* Term = mpc_new();
+ mpc_parser_t* Maths = mpc_new();
+
+ mpc_define(Expr, mpc_else(
+ mpc_and(3, mpcf_maths, Factor, mpc_oneof("*/"), Factor, free, free),
+ Factor
+ ));
+
+ mpc_define(Factor, mpc_else(
+ mpc_and(3, mpcf_maths, Term, mpc_oneof("+-"), Term, free, free),
+ Term
+ ));
+
+ mpc_define(Term, mpc_else(
+ mpc_int(),
+ mpc_parens(Expr, free)
+ ));
+
+ mpc_define(Maths, mpc_ends(Expr, free));
+
+ PT_ASSERT(mpc_match(Maths, "1", (int[]){ 1 }, int_eq, free, int_print));
+ PT_ASSERT(mpc_match(Maths, "(5)", (int[]){ 5 }, int_eq, free, int_print));
+ PT_ASSERT(mpc_match(Maths, "(4*2)+5", (int[]){ 13 }, int_eq, free, int_print));
+ PT_ASSERT(mpc_unmatch(Maths, "a", (int[]){ 0 }, int_eq, free, int_print));
+ PT_ASSERT(mpc_unmatch(Maths, "2b+4", (int[]){ 2 }, int_eq, free, int_print));
+
+ mpc_undefine(Expr);
+ mpc_undefine(Factor);
+ mpc_undefine(Term);
+ mpc_undefine(Maths);
+
+ mpc_delete(Expr);
+ mpc_delete(Factor);
+ mpc_delete(Term);
+ mpc_delete(Maths);
+
+}
+
+void suite_core(void) {
+ pt_add_test(test_ident, "Test Ident", "Suite Core");
+ pt_add_test(test_maths, "Test Maths", "Suite Core");
+}
diff --git a/tests/grammar.c b/tests/grammar.c
new file mode 100644
index 0000000..d49d733
--- /dev/null
+++ b/tests/grammar.c
@@ -0,0 +1,56 @@
+#include "ptest.h"
+#include "../mpc.h"
+
+bool ast_eq(void* x, void* y) {
+ return false;
+}
+
+void test_grammar(void) {
+
+ mpc_parser_t* Test = mpc_new();
+
+ mpc_define(Test, mpca_grammar("'c'*"));
+
+ mpc_print(Test);
+
+ mpc_undefine(Test);
+
+ mpc_delete(Test);
+
+ mpc_parser_t* Expression = mpc_new();
+ mpc_parser_t* Product = mpc_new();
+ mpc_parser_t* Value = mpc_new();
+
+ mpc_define(Expression, mpca_grammar("<0> (('+' | '-') <0>)*", Product));
+ mpc_define(Product, mpca_grammar("<0> (('*' | '/') <0>)*", Value));
+ mpc_define(Value, mpca_grammar("/[0-9]/ | '(' <0> ')'", Expression));
+
+ mpc_print(Expression);
+ mpc_print(Product);
+ mpc_print(Value);
+
+ mpc_ast_t* empty = mpc_ast_empty();
+
+ /*
+ PT_ASSERT(mpc_match(Expression, "1", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
+ PT_ASSERT(mpc_match(Expression, "(5)", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
+ PT_ASSERT(mpc_match(Expression, "(4*2)+5", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
+ PT_ASSERT(mpc_match(Expression, "a", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
+ PT_ASSERT(mpc_match(Expression, "2b+4", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
+ */
+
+ mpc_ast_delete(empty);
+
+ mpc_undefine(Expression);
+ mpc_undefine(Product);
+ mpc_undefine(Value);
+
+ mpc_delete(Expression);
+ mpc_delete(Product);
+ mpc_delete(Value);
+
+}
+
+void suite_grammar(void) {
+ pt_add_test(test_grammar, "Test Grammar", "Suite Grammar");
+}
\ No newline at end of file
diff --git a/tests/ptest.c b/tests/ptest.c
new file mode 100644
index 0000000..61d2d8a
--- /dev/null
+++ b/tests/ptest.c
@@ -0,0 +1,301 @@
+#include "ptest.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/* Globals */
+
+#define MAX_NAME 512
+#define MAX_ERROR 2048
+#define MAX_TESTS 2048
+
+static bool test_passing = false;
+static bool suite_passing = false;
+
+/* Colors */
+
+enum {
+ BLACK = 0x0,
+ BLUE = 0x1,
+ GREEN = 0x2,
+ AQUA = 0x3,
+ RED = 0x4,
+ PURPLE = 0x5,
+ YELLOW = 0x6,
+ WHITE = 0x7,
+ GRAY = 0x8,
+ LIGHT_BLUE = 0x9,
+ LIGHT_GREEN = 0xA,
+ LIGHT_AQUA = 0xB,
+ LIGHT_RED = 0xC,
+ LIGHT_PURPLE = 0xD,
+ LIGHT_YELLOW = 0xE,
+ LIGHT_WHITE = 0xF,
+};
+
+#ifdef _WIN32
+
+ #include
+
+ static void pt_color(int color) {
+ HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleTextAttribute(hCon, color);
+ }
+
+#else
+
+static const char* colors[] = {
+ "\x1B[0m",
+ "\x1B[34m",
+ "\x1B[32m",
+ "\x1B[36m",
+ "\x1B[31m",
+ "\x1B[35m",
+ "\x1B[33m",
+ "\x1B[37m",
+ "",
+ "\x1B[34m",
+ "\x1B[32m",
+ "\x1B[36m",
+ "\x1B[31m",
+ "\x1B[35m",
+ "\x1B[33m",
+ "\x1B[37m"
+};
+
+ static void pt_color(int color) {
+
+ printf("%s", colors[color]);
+
+ }
+
+#endif
+
+/* Asserts */
+
+static int num_asserts = 0;
+static int num_assert_passes = 0;
+static int num_assert_fails = 0;
+
+static char assert_err[MAX_ERROR];
+static char assert_err_buff[MAX_ERROR];
+static int assert_err_num = 0;
+
+void pt_assert_run(bool result, const char* expr, const char* func, const char* file, int line) {
+
+ num_asserts++;
+ test_passing = test_passing && result;
+
+ if (result) {
+ num_assert_passes++;
+ } else {
+ sprintf(assert_err_buff, " %i. Assert [ %s ] (%s:%i)\n", assert_err_num+1, expr, file, line );
+ strcat(assert_err, assert_err_buff);
+ assert_err_num++;
+ num_assert_fails++;
+ }
+
+}
+
+static void ptest_signal(int sig) {
+
+ test_passing = false;
+
+ switch( sig ) {
+ case SIGFPE: sprintf(assert_err_buff, " %i. Division by Zero\n", assert_err_num+1); break;
+ case SIGILL: sprintf(assert_err_buff, " %i. Illegal Instruction\n", assert_err_num+1); break;
+ case SIGSEGV: sprintf(assert_err_buff, " %i. Segmentation Fault\n", assert_err_num+1); break;
+ }
+
+ assert_err_num++;
+ strcat(assert_err, assert_err_buff);
+
+ pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
+
+ printf(" | Stopping Execution.\n");
+ fflush(stdout);
+ exit(0);
+
+}
+
+/* Tests */
+
+static void pt_title_case(char* output, const char* input) {
+
+ bool space = true;
+
+ strcpy(output, input);
+
+ unsigned int i;
+ for(i = 0; i < strlen(output); i++) {
+
+ if (output[i] == '_' || output[i] == ' ') {
+ space = true;
+ output[i] = ' ';
+ continue;
+ }
+
+ if (space && output[i] >= 'a' && output[i] <= 'z') {
+ output[i] = output[i] - 32;
+ continue;
+ }
+
+ space = false;
+ }
+
+}
+
+typedef struct {
+ void (*func)(void);
+ char name[MAX_NAME];
+ char suite[MAX_NAME];
+} test_t;
+
+static test_t tests[MAX_TESTS];
+
+static int num_tests = 0;
+static int num_tests_passes = 0;
+static int num_tests_fails = 0;
+
+void pt_add_test(void (*func)(void), const char* name, const char* suite) {
+
+ if (num_tests == MAX_TESTS) {
+ printf("ERROR: Exceeded maximum test count of %i!\n", MAX_TESTS); abort();
+ }
+
+ if (strlen(name) >= MAX_NAME) {
+ printf("ERROR: Test name '%s' too long (Maximum is %i characters)\n", name, MAX_NAME); abort();
+ }
+
+ if (strlen(suite) >= MAX_NAME) {
+ printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n", suite, MAX_NAME); abort();
+ }
+
+ test_t test;
+ test.func = func;
+ pt_title_case(test.name, name);
+ pt_title_case(test.suite, suite);
+
+ tests[num_tests] = test;
+ num_tests++;
+
+}
+
+/* Suites */
+
+static int num_suites = 0;
+static int num_suites_passes = 0;
+static int num_suites_fails = 0;
+
+void pt_add_suite(void (*func)(void)) {
+ num_suites++;
+ func();
+}
+
+/* Running */
+
+static clock_t start, end;
+static char current_suite[MAX_NAME];
+
+int pt_run(void) {
+
+ printf(" \n");
+ printf(" +-------------------------------------------+\n");
+ printf(" | ptest MicroTesting Magic for C |\n");
+ printf(" | |\n");
+ printf(" | http://github.com/orangeduck/ptest |\n");
+ printf(" | |\n");
+ printf(" | Daniel Holden (contact@theorangeduck.com) |\n");
+ printf(" +-------------------------------------------+\n");
+
+ signal(SIGFPE, ptest_signal);
+ signal(SIGILL, ptest_signal);
+ signal(SIGSEGV, ptest_signal);
+
+ start = clock();
+ strcpy(current_suite, "");
+
+ unsigned int i;
+ for(i = 0; i < num_tests; i++) {
+
+ test_t test = tests[i];
+
+ /* Check for transition to a new suite */
+ if (strcmp(test.suite, current_suite)) {
+
+ /* Don't increment any counter for first entrance */
+ if (strcmp(current_suite, "")) {
+ if (suite_passing) {
+ num_suites_passes++;
+ } else {
+ num_suites_fails++;
+ }
+ }
+
+ suite_passing = true;
+ strcpy(current_suite, test.suite);
+ printf("\n\n ===== %s =====\n\n", current_suite);
+ }
+
+ /* Run Test */
+
+ test_passing = true;
+ strcpy(assert_err, "");
+ strcpy(assert_err_buff, "");
+ assert_err_num = 0;
+ printf(" | %s ... ", test.name);
+
+ test.func();
+
+ suite_passing = suite_passing && test_passing;
+
+ if (test_passing) {
+ num_tests_passes++;
+ pt_color(GREEN); printf("Passed! \n"); pt_color(WHITE);
+ } else {
+ num_tests_fails++;
+ pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
+ }
+
+ }
+
+ if (suite_passing) {
+ num_suites_passes++;
+ } else {
+ num_suites_fails++;
+ }
+
+ end = clock();
+
+ printf(" \n");
+ printf(" +---------------------------------------------------+\n");
+ printf(" | Summary |\n");
+ printf(" +---------++------------+-------------+-------------+\n");
+
+ printf(" | Suites ||");
+ pt_color(YELLOW); printf(" Total %4d ", num_suites); pt_color(WHITE); printf("|");
+ pt_color(GREEN); printf(" Passed %4d ", num_suites_passes); pt_color(WHITE); printf("|");
+ pt_color(RED); printf(" Failed %4d ", num_suites_fails); pt_color(WHITE); printf("|\n");
+
+ printf(" | Tests ||");
+ pt_color(YELLOW); printf(" Total %4d ", num_tests); pt_color(WHITE); printf("|");
+ pt_color(GREEN); printf(" Passed %4d ", num_tests_passes); pt_color(WHITE); printf("|");
+ pt_color(RED); printf(" Failed %4d ", num_tests_fails); pt_color(WHITE); printf("|\n");
+
+ printf(" | Asserts ||");
+ pt_color(YELLOW); printf(" Total %4d ", num_asserts); pt_color(WHITE); printf("|");
+ pt_color(GREEN); printf(" Passed %4d ", num_assert_passes); pt_color(WHITE); printf("|");
+ pt_color(RED); printf(" Failed %4d ", num_assert_fails); pt_color(WHITE); printf("|\n");
+
+ printf(" +---------++------------+-------------+-------------+\n");
+ printf(" \n");
+
+ double total = (double)(end - start) / CLOCKS_PER_SEC;
+
+ printf(" Total Running Time: %0.3fs\n\n", total);
+
+ if (num_suites_fails > 0) { return 1; } else { return 0; }
+}
diff --git a/tests/ptest.h b/tests/ptest.h
new file mode 100644
index 0000000..27fde9c
--- /dev/null
+++ b/tests/ptest.h
@@ -0,0 +1,19 @@
+#ifndef ptest_h
+#define ptest_h
+
+#include
+#include
+
+#define PT_SUITE(name) void name(void)
+#define PT_TEST(name) auto void name(void); pt_add_test(name, #name, __func__); void name(void)
+
+#define PT_ASSERT(expr) pt_assert_run((bool)(expr), #expr, __func__, __FILE__, __LINE__)
+#define PT_ASSERT_STR_EQ(fst, snd) pt_assert_run(strcmp(fst, snd) == 0, "strcmp( " #fst ", " #snd " ) == 0", __func__, __FILE__, __LINE__)
+
+void pt_assert_run(bool result, const char* expr, const char* func, const char* file, int line);
+
+void pt_add_test(void (*func)(void), const char* name, const char* suite);
+void pt_add_suite(void (*func)(void));
+int pt_run(void);
+
+#endif
\ No newline at end of file
diff --git a/tests/regex.c b/tests/regex.c
index a700099..8288e5b 100644
--- a/tests/regex.c
+++ b/tests/regex.c
@@ -1,27 +1,29 @@
+#include "ptest.h"
#include "../mpc.h"
#include
+#include
-/*
static bool string_eq(void* x, void* y) { return (strcmp(x, y) == 0); }
static void string_print(void* x) { printf("'%s'", (char*)x); }
-*/
-bool suite_regex(void) {
-
+void test_regex_basic(void) {
+
mpc_parser_t* re0 = mpc_re("abc|bcd");
mpc_parser_t* re1 = mpc_re("abc|bcd|e");
- mpc_parser_t* re2 = mpc_re("abc(ab)*");
+ mpc_parser_t* re2 = mpc_re("ab()c(ab)*");
mpc_parser_t* re3 = mpc_re("abc(abdd)?");
mpc_parser_t* re4 = mpc_re("ab|c(abdd)?");
mpc_parser_t* re5 = mpc_re("abc(ab|dd)+g$");
- mpc_print(re0);
- mpc_print(re1);
- mpc_print(re2);
- mpc_print(re3);
- mpc_print(re4);
- mpc_print(re5);
+ PT_ASSERT(mpc_match(re0, "abc", "abc", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(re0, "bcd", "bcd", string_eq, free, string_print));
+ PT_ASSERT(mpc_unmatch(re0, "bc", "bc", string_eq, free, string_print));
+ PT_ASSERT(mpc_unmatch(re0, "ab", "ab", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(re1, "e", "e", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(re2, "abc", "abc", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(re2, "abcabab", "abcabab", string_eq, free, string_print));
+ PT_ASSERT(mpc_match(re2, "abcababd", "abcabab", string_eq, free, string_print));
mpc_delete(re0);
mpc_delete(re1);
@@ -29,6 +31,29 @@ bool suite_regex(void) {
mpc_delete(re3);
mpc_delete(re4);
mpc_delete(re5);
+
+}
+
+void test_regex_range(void) {
+
+ mpc_parser_t* re0 = mpc_re("abg[abcdef]");
+ mpc_parser_t* re1 = mpc_re("y*[a-z]");
+ mpc_parser_t* re2 = mpc_re("zz(p+)?[A-Z_0\\]123]*");
+ mpc_parser_t* re3 = mpc_re("[^56hy].*$");
+
+ mpc_print(re0);
+ mpc_print(re1);
+ mpc_print(re2);
+ mpc_print(re3);
- return true;
+ mpc_delete(re0);
+ mpc_delete(re1);
+ mpc_delete(re2);
+ mpc_delete(re3);
+
+}
+
+void suite_regex(void) {
+ pt_add_test(test_regex_basic, "Test Regex Basic", "Suite Regex");
+ pt_add_test(test_regex_range, "Test Regex Range", "Suite Regex");
}
\ No newline at end of file
diff --git a/tests/test.c b/tests/test.c
index 3346f7f..39015ba 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -1,15 +1,12 @@
-#include
+#include "ptest.h"
-bool suite_ident(void);
-bool suite_math(void);
-bool suite_regex(void);
+void suite_core(void);
+void suite_regex(void);
+void suite_grammar(void);
int main(int argc, char** argv) {
-
- suite_ident();
- suite_math();
- suite_regex();
-
- return 0;
-
+ pt_add_suite(suite_core);
+ pt_add_suite(suite_regex);
+ pt_add_suite(suite_grammar);
+ return pt_run();
}
\ No newline at end of file