Merge github.com:orangeduck/mpc

This commit is contained in:
Daniel Holden
2020-07-19 08:54:16 -04:00
11 changed files with 830 additions and 758 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ examples/maths
examples/smallc
examples/foobar
examples/tree_traversal
build/*

View File

@@ -1,7 +1,10 @@
CC = gcc
STND=-ansi
CFLAGS = $(STND) -pedantic -O3 -g -Wall -Werror -Wextra -Wformat=2 -Wshadow \
PROJ = mpc
CC ?= gcc
STD ?= -ansi
DIST = build
MKDIR ?= mkdir -p
PREFIX ?= /usr/local
CFLAGS ?= $(STD) -pedantic -O3 -g -Wall -Werror -Wextra -Wformat=2 -Wshadow \
-Wno-long-long -Wno-overlength-strings -Wno-format-nonliteral -Wcast-align \
-Wwrite-strings -Wstrict-prototypes -Wold-style-definition -Wredundant-decls \
-Wnested-externs -Wmissing-include-dirs -Wswitch-default
@@ -10,15 +13,59 @@ TESTS = $(wildcard tests/*.c)
EXAMPLES = $(wildcard examples/*.c)
EXAMPLESEXE = $(EXAMPLES:.c=)
all: $(EXAMPLESEXE) check
.PHONY: all check clean libs $(DIST)/$(PROJ).pc
check: $(TESTS) mpc.c
$(CC) $(filter-out -Werror, $(CFLAGS)) $^ -lm -o test
./test
all: $(EXAMPLESEXE) check
$(DIST):
$(MKDIR) $(DIST)/examples
check: $(DIST)/test-file $(DIST)/test-static $(DIST)/test-dynamic
./$(DIST)/test-file
./$(DIST)/test-static
LD_LIBRARY_PATH=$(DIST) ./$(DIST)/test-dynamic
$(DIST)/test-file: $(TESTS) $(PROJ).c $(PROJ).h tests/ptest.h
$(CC) $(filter-out -Werror, $(CFLAGS)) $(TESTS) $(PROJ).c -lm -o $(DIST)/test-file
$(DIST)/test-dynamic: $(TESTS) $(DIST)/lib$(PROJ).so $(PROJ).h tests/ptest.h
$(CC) $(filter-out -Werror, $(CFLAGS)) $(TESTS) -lm -L$(DIST) -l$(PROJ) -o $(DIST)/test-dynamic
$(DIST)/test-static: $(TESTS) $(DIST)/lib$(PROJ).a $(PROJ).h tests/ptest.h
$(CC) $(filter-out -Werror, $(CFLAGS)) $(TESTS) -lm -L$(DIST) -l$(PROJ) -static -o $(DIST)/test-static
examples/%: $(DIST) examples/%.c $(PROJ).c $(PROJ).h
$(CC) $(CFLAGS) $(filter-out $(DIST) $(PROJ).h, $^) -lm -o $(DIST)/$@
$(DIST)/lib$(PROJ).so: $(PROJ).c $(PROJ).h
ifneq ($(OS),Windows_NT)
$(CC) $(CFLAGS) -fPIC -shared $(PROJ).c -o $(DIST)/lib$(PROJ).so
else
$(CC) $(CFLAGS) -shared $(PROJ).c -o $(DIST)/lib$(PROJ).so
endif
$(DIST)/lib$(PROJ).a: $(PROJ).c $(PROJ).h
$(CC) $(CFLAGS) -c $(PROJ).c -o $(DIST)/$(PROJ).o
$(AR) rcs $(DIST)/lib$(PROJ).a $(DIST)/$(PROJ).o
libs: $(DIST)/lib$(PROJ).so $(DIST)/lib$(PROJ).a
$(DIST)/$(PROJ).pc: $(DIST) $(PROJ).pc
cp $(PROJ).pc $(DIST)/$(PROJ).pc
sed -i '1i\prefix=$(PREFIX)/' $(DIST)/$(PROJ).pc
examples/%: examples/%.c mpc.c
$(CC) $(CFLAGS) $^ -lm -o $@
clean:
rm -rf test examples/doge examples/lispy examples/maths examples/smallc \
examples/foobar examples/tree_traversal
rm -rf -- $(DIST)
install: all
install -d -m644 $(DESTDIR)$(PREFIX)/{include,lib/pkgconfig,share/$(PROJ)}
install -m755 -t $(DESTDIR)$(PREFIX)/lib $(DIST)/lib*
install -m644 -t $(DESTDIR)$(PREFIX)/share/$(PROJ) $(PROJ).{c,h}
install -m644 $(PROJ).h $(DESTDIR)$(PREFIX)/include/$(PROJ).h
install -m644 $(DIST)/$(PROJ).pc $(DESTDIR)$(PREFIX)/lib/pkgconfig/$(PROJ).pc
uninstall:
rm -rf -- \
$(DESTDIR)$(PREFIX)/include/$(PROJ).h \
$(DESTDIR)$(PREFIX)/share/$(PROJ)/$(PROJ).{c,h} \
$(DESTDIR)$(PREFIX)/lib/lib$(PROJ).{so,a}

View File

@@ -1,7 +1,7 @@
Micro Parser Combinators
========================
Version 0.8.8
Version 0.9.0
About
@@ -116,7 +116,7 @@ Basic Parsers
All the following functions construct new basic parsers of the type `mpc_parser_t *`. All of those parsers return a newly allocated `char *` with the character(s) they manage to match. If unsuccessful they will return an error. They have the following functionality.
* * *
* * *
```c
mpc_parser_t *mpc_any(void);
@@ -124,7 +124,7 @@ mpc_parser_t *mpc_any(void);
Matches any individual character
* * *
* * *
```c
mpc_parser_t *mpc_char(char c);
@@ -283,7 +283,7 @@ Run a parser on the contents of some file.
Combinators
-----------
Combinators are functions that take one or more parsers and return a new parser of some given functionality.
Combinators are functions that take one or more parsers and return a new parser of some given functionality.
These combinators work independently of exactly what data type the parser(s) supplied as input return. In languages such as Haskell ensuring you don't input one type of data into a parser requiring a different type is done by the compiler. But in C we don't have that luxury. So it is at the discretion of the programmer to ensure that he or she deals correctly with the outputs of different parser types.
@@ -312,13 +312,13 @@ Returns a parser that applies function `f` (optionality taking extra input `x`)
* * *
```c
mpc_parser_t *mpc_check(mpc_parser_t *a, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_check_with_t f, void *x, const char *fmt, ...);
mpc_parser_t *mpc_check(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *fmt, ...);
```
Returns a parser that applies function `f` (optionally taking extra input `x`) to the result of parser `a`. If `f` returns non-zero, then the parser succeeds and returns the value of `a` (possibly modified by `f`). If `f` returns zero, then the parser fails with message `e`.
Returns a parser that applies function `f` (optionally taking extra input `x`) to the result of parser `a`. If `f` returns non-zero, then the parser succeeds and returns the value of `a` (possibly modified by `f`). If `f` returns zero, then the parser fails with message `e`, and the result of `a` is destroyed with the destructor `da`.
* * *
@@ -556,9 +556,9 @@ To ease the task of undefining and then deleting parsers `mpc_cleanup` can be us
mpc_parser_t *mpc_copy(mpc_parser_t *a);
```
This function makes a copy of a parser `a`. This can be useful when you want to
use a parser as input for some other parsers multiple times without retaining
it.
This function makes a copy of a parser `a`. This can be useful when you want to
use a parser as input for some other parsers multiple times without retaining
it.
* * *
@@ -567,11 +567,11 @@ mpc_parser_t *mpc_re(const char *re);
mpc_parser_t *mpc_re_mode(const char *re, int mode);
```
This function takes as input the regular expression `re` and builds a parser
for it. With the `mpc_re_mode` function optional mode flags can also be given.
Available flags are `MPC_RE_MULTILINE` / `MPC_RE_M` where the start of input
character `^` also matches the beginning of new lines and the end of input `$`
character also matches new lines, and `MPC_RE_DOTALL` / `MPC_RE_S` where the
This function takes as input the regular expression `re` and builds a parser
for it. With the `mpc_re_mode` function optional mode flags can also be given.
Available flags are `MPC_RE_MULTILINE` / `MPC_RE_M` where the start of input
character `^` also matches the beginning of new lines and the end of input `$`
character also matches new lines, and `MPC_RE_DOTALL` / `MPC_RE_S` where the
any character token `.` also matches newlines (by default it doesn't).
@@ -626,7 +626,7 @@ Useful Parsers
<tr><td><code>mpc_startswith(mpc_parser_t *a);</code></td><td>Matches the start of input followed by <code>a</code></td></tr>
<tr><td><code>mpc_endswith(mpc_parser_t *a, mpc_dtor_t da);</code></td><td>Matches <code>a</code> followed by the end of input</td></tr>
<tr><td><code>mpc_whole(mpc_parser_t *a, mpc_dtor_t da);</code></td><td>Matches the start of input, <code>a</code>, and the end of input</td></tr>
<tr><td><code>mpc_whole(mpc_parser_t *a, mpc_dtor_t da);</code></td><td>Matches the start of input, <code>a</code>, and the end of input</td></tr>
<tr><td><code>mpc_stripl(mpc_parser_t *a);</code></td><td>Matches <code>a</code> first consuming any whitespace to the left</td></tr>
<tr><td><code>mpc_stripr(mpc_parser_t *a);</code></td><td>Matches <code>a</code> then consumes any whitespace to the right</td></tr>
<tr><td><code>mpc_strip(mpc_parser_t *a);</code></td><td>Matches <code>a</code> consuming any surrounding whitespace</td></tr>
@@ -707,17 +707,17 @@ We start with a fold function that will fold two `int *` into a new `int *` base
```c
mpc_val_t *fold_maths(int n, mpc_val_t **xs) {
int **vs = (int**)xs;
if (strcmp(xs[1], "*") == 0) { *vs[0] *= *vs[2]; }
if (strcmp(xs[1], "/") == 0) { *vs[0] /= *vs[2]; }
if (strcmp(xs[1], "%") == 0) { *vs[0] %= *vs[2]; }
if (strcmp(xs[1], "+") == 0) { *vs[0] += *vs[2]; }
if (strcmp(xs[1], "-") == 0) { *vs[0] -= *vs[2]; }
free(xs[1]); free(xs[2]);
return xs[0];
}
```
@@ -730,14 +730,14 @@ mpc_parser_t *Factor = mpc_new("factor");
mpc_parser_t *Term = mpc_new("term");
mpc_parser_t *Maths = mpc_new("maths");
mpc_define(Expr, mpc_or(2,
mpc_define(Expr, mpc_or(2,
mpc_and(3, fold_maths,
Factor, mpc_oneof("+-"), Factor,
free, free),
Factor
));
mpc_define(Factor, mpc_or(2,
mpc_define(Factor, mpc_or(2,
mpc_and(3, fold_maths,
Term, mpc_oneof("*/"), Term,
free, free),
@@ -781,6 +781,8 @@ The syntax for this is defined as follows.
<tr><td><code>'a' | 'b'</code></td><td>Either <code>'a'</code> is required, or <code>'b'</code> is required.</td></tr>
<tr><td><code>'a'*</code></td><td>Zero or more <code>'a'</code> are required.</td></tr>
<tr><td><code>'a'+</code></td><td>One or more <code>'a'</code> are required.</td></tr>
<tr><td><code>'a'?</code></td><td>Zero or one <code>'a'</code> is required.</td></tr>
<tr><td><code>'a'{x}</code></td><td>Exactly <code>x</code> (integer) copies of <code>'a'</code> are required.</td></tr>
<tr><td><code>&lt;abba&gt;</code></td><td>The rule called <code>abba</code> is required.</td></tr>
</table>
@@ -825,17 +827,17 @@ This opens and reads in the contents of the file given by `filename` and passes
Case Study - Tokenizer
======================
Another common task we might be interested in doing is tokenizing some block of
Another common task we might be interested in doing is tokenizing some block of
text (splitting the text into individual elements) and performing some function
on each one of these elements as it is read. We can do this with `mpc` too.
First, we can build a regular expression which parses an individual token. For
example if our tokens are identifiers, integers, commas, periods and colons we
could build something like this `mpc_re("\\s*([a-zA-Z_]+|[0-9]+|,|\\.|:)")`.
Next we can strip any whitespace, and add a callback function using `mpc_apply`
which gets called every time this regex is parsed successfully
`mpc_apply(mpc_strip(mpc_re("\\s*([a-zA-Z_]+|[0-9]+|,|\\.|:)")), print_token)`.
Finally we can surround all of this in `mpc_many` to parse it zero or more
First, we can build a regular expression which parses an individual token. For
example if our tokens are identifiers, integers, commas, periods and colons we
could build something like this `mpc_re("\\s*([a-zA-Z_]+|[0-9]+|,|\\.|:)")`.
Next we can strip any whitespace, and add a callback function using `mpc_apply`
which gets called every time this regex is parsed successfully
`mpc_apply(mpc_strip(mpc_re("\\s*([a-zA-Z_]+|[0-9]+|,|\\.|:)")), print_token)`.
Finally we can surround all of this in `mpc_many` to parse it zero or more
times. The final code might look something like this:
```c
@@ -847,16 +849,16 @@ static mpc_val_t *print_token(mpc_val_t *x) {
int main(int argc, char **argv) {
const char *input = " hello 4352 , \n foo.bar \n\n test:ing ";
mpc_parser_t* Tokens = mpc_many(
mpcf_all_free,
mpcf_all_free,
mpc_apply(mpc_strip(mpc_re("\\s*([a-zA-Z_]+|[0-9]+|,|\\.|:)")), print_token));
mpc_result_t r;
mpc_parse("input", input, Tokens, &r);
mpc_delete(Tokens);
return 0;
}
```
@@ -875,7 +877,7 @@ Token: ':'
Token: 'ing'
```
By extending the regex we can easily extend this to parse many more types of
By extending the regex we can easily extend this to parse many more types of
tokens and quickly and easily build a tokenizer for whatever language we are
interested in.
@@ -991,5 +993,3 @@ When parsing from a grammar, the abstract syntax tree is tagged with different t
If you have a rule in your grammar called `string`, `char` or `regex`, you may encounter some confusion. This is because nodes will be tagged with (for example) `string` _either_ if they are a string primitive, _or_ if they were parsed via your `string` rule. If you are detecting node type using something like `strstr`, in this situation it might break. One solution to this is to always check that `string` is the innermost tag to test for string primitives, or to rename your rule called `string` to something that doesn't conflict.
Yes it is annoying but its probably not going to change!

View File

@@ -31,4 +31,4 @@ int main(int argc, char **argv) {
mpc_delete(Line);
return 0;
}
}

1115
mpc.c

File diff suppressed because it is too large Load Diff

19
mpc.h
View File

@@ -43,7 +43,7 @@ typedef struct {
char *filename;
char *failure;
char **expected;
char recieved;
char received;
} mpc_err_t;
void mpc_err_delete(mpc_err_t *e);
@@ -129,10 +129,10 @@ mpc_parser_t *mpc_expect(mpc_parser_t *a, const char *e);
mpc_parser_t *mpc_expectf(mpc_parser_t *a, const char *fmt, ...);
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_check(mpc_parser_t *a, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_check_with_t f, void *x, const char *fmt, ...);
mpc_parser_t *mpc_check(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *fmt, ...);
mpc_parser_t *mpc_not(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_not_lift(mpc_parser_t *a, mpc_dtor_t da, mpc_ctor_t lf);
@@ -204,7 +204,7 @@ mpc_parser_t *mpc_whole(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_stripl(mpc_parser_t *a);
mpc_parser_t *mpc_stripr(mpc_parser_t *a);
mpc_parser_t *mpc_strip(mpc_parser_t *a);
mpc_parser_t *mpc_tok(mpc_parser_t *a);
mpc_parser_t *mpc_tok(mpc_parser_t *a);
mpc_parser_t *mpc_sym(const char *s);
mpc_parser_t *mpc_total(mpc_parser_t *a, mpc_dtor_t da);
@@ -258,6 +258,7 @@ mpc_val_t *mpcf_snd_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_trd_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_all_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_freefold(int n, mpc_val_t** xs);
mpc_val_t *mpcf_strfold(int n, mpc_val_t** xs);
mpc_val_t *mpcf_maths(int n, mpc_val_t** xs);
@@ -275,7 +276,7 @@ enum {
mpc_parser_t *mpc_re(const char *re);
mpc_parser_t *mpc_re_mode(const char *re, int mode);
/*
** AST
*/
@@ -373,8 +374,8 @@ void mpc_optimise(mpc_parser_t *p);
void mpc_stats(mpc_parser_t *p);
int mpc_test_pass(mpc_parser_t *p, const char *s, const void *d,
int(*tester)(const void*, const void*),
mpc_dtor_t destructor,
int(*tester)(const void*, const void*),
mpc_dtor_t destructor,
void(*printer)(const void*));
int mpc_test_fail(mpc_parser_t *p, const char *s, const void *d,

8
mpc.pc Normal file
View File

@@ -0,0 +1,8 @@
libdir=${prefix}/lib
includedir=${prefix}/include
Name: mpc
Description: Library for creating parser combinators
Version: 0.9.0
Libs: -L${libdir} -lmpc
Cflags: -I${includedir}

View File

@@ -12,7 +12,7 @@ static int check_is(mpc_val_t** x, void* t) {
void test_check(void) {
int success;
mpc_result_t r;
mpc_parser_t* p = mpc_check(mpc_or(2, mpc_char('a'), mpc_char('b')), check_is_a, "Expected 'a'");
mpc_parser_t* p = mpc_check(mpc_or(2, mpc_char('a'), mpc_char('b')), free, check_is_a, "Expected 'a'");
success = mpc_parse("test", "a", p, &r);
PT_ASSERT(success);
@@ -30,7 +30,7 @@ void test_check(void) {
void test_check_with(void) {
int success;
mpc_result_t r;
mpc_parser_t* p = mpc_check_with(mpc_or(2, mpc_char('a'), mpc_char('b')), check_is, (void*)"a", "Expected 'a'");
mpc_parser_t* p = mpc_check_with(mpc_or(2, mpc_char('a'), mpc_char('b')), free, check_is, (void*)"a", "Expected 'a'");
success = mpc_parse("test", "a", p, &r);
PT_ASSERT(success);
@@ -48,7 +48,7 @@ void test_check_with(void) {
void test_checkf(void) {
int success;
mpc_result_t r;
mpc_parser_t* p = mpc_checkf(mpc_or(2, mpc_char('a'), mpc_char('b')), check_is_a, "Expected '%s'", "a");
mpc_parser_t* p = mpc_checkf(mpc_or(2, mpc_char('a'), mpc_char('b')), free, check_is_a, "Expected '%s'", "a");
success = mpc_parse("test", "a", p, &r);
PT_ASSERT(success);
@@ -66,7 +66,7 @@ void test_checkf(void) {
void test_check_withf(void) {
int success;
mpc_result_t r;
mpc_parser_t* p = mpc_check_withf(mpc_or(2, mpc_char('a'), mpc_char('b')), check_is, (void*)"a", "Expected '%s'", "a");
mpc_parser_t* p = mpc_check_withf(mpc_or(2, mpc_char('a'), mpc_char('b')), free, check_is, (void*)"a", "Expected '%s'", "a");
success = mpc_parse("test", "a", p, &r);
PT_ASSERT(success);

View File

@@ -12,7 +12,7 @@ static void strprint(const void* x) { printf("'%s'", (char*)x); }
void test_ident(void) {
/* ^[a-zA-Z_][a-zA-Z0-9_]*$ */
mpc_parser_t* Ident = mpc_whole(
mpc_and(2, mpcf_strfold,
mpc_or(2, mpc_alpha(), mpc_underscore()),
@@ -20,7 +20,7 @@ void test_ident(void) {
free),
free
);
PT_ASSERT(mpc_test_pass(Ident, "test", "test", streq, free, strprint));
PT_ASSERT(mpc_test_fail(Ident, " blah", "", streq, free, strprint));
PT_ASSERT(mpc_test_pass(Ident, "anoth21er", "anoth21er", streq, free, strprint));
@@ -28,99 +28,99 @@ void test_ident(void) {
PT_ASSERT(mpc_test_fail(Ident, "some spaces", "", streq, free, strprint));
PT_ASSERT(mpc_test_fail(Ident, "", "", streq, free, strprint));
PT_ASSERT(mpc_test_fail(Ident, "18nums", "", streq, free, strprint));
mpc_delete(Ident);
}
void test_maths(void) {
mpc_parser_t *Expr, *Factor, *Term, *Maths;
mpc_parser_t *Expr, *Factor, *Term, *Maths;
int r0 = 1, r1 = 5, r2 = 13, r3 = 0, r4 = 2;
Expr = mpc_new("expr");
Factor = mpc_new("factor");
Term = mpc_new("term");
Maths = mpc_new("maths");
mpc_define(Expr, mpc_or(2,
mpc_define(Expr, mpc_or(2,
mpc_and(3, mpcf_maths, Factor, mpc_oneof("*/"), Factor, free, free),
Factor
));
mpc_define(Factor, mpc_or(2,
mpc_define(Factor, mpc_or(2,
mpc_and(3, mpcf_maths, Term, mpc_oneof("+-"), Term, free, free),
Term
));
mpc_define(Term, mpc_or(2,
mpc_define(Term, mpc_or(2,
mpc_int(),
mpc_parens(Expr, free)
));
mpc_define(Maths, mpc_whole(Expr, free));
PT_ASSERT(mpc_test_pass(Maths, "1", &r0, int_eq, free, int_print));
PT_ASSERT(mpc_test_pass(Maths, "(5)", &r1, int_eq, free, int_print));
PT_ASSERT(mpc_test_pass(Maths, "(4*2)+5", &r2, int_eq, free, int_print));
PT_ASSERT(mpc_test_fail(Maths, "a", &r3, int_eq, free, int_print));
PT_ASSERT(mpc_test_fail(Maths, "2b+4", &r4, int_eq, free, int_print));
mpc_cleanup(4, Expr, Factor, Term, Maths);
}
void test_strip(void) {
mpc_parser_t *Stripperl = mpc_apply(mpc_many(mpcf_strfold, mpc_any()), mpcf_strtriml);
mpc_parser_t *Stripperr = mpc_apply(mpc_many(mpcf_strfold, mpc_any()), mpcf_strtrimr);
mpc_parser_t *Stripper = mpc_apply(mpc_many(mpcf_strfold, mpc_any()), mpcf_strtrim);
PT_ASSERT(mpc_test_pass(Stripperl, " asdmlm dasd ", "asdmlm dasd ", streq, free, strprint));
PT_ASSERT(mpc_test_pass(Stripperr, " asdmlm dasd ", " asdmlm dasd", streq, free, strprint));
PT_ASSERT(mpc_test_pass(Stripper, " asdmlm dasd ", "asdmlm dasd", streq, free, strprint));
mpc_delete(Stripperl);
mpc_delete(Stripperr);
mpc_delete(Stripper);
}
void test_repeat(void) {
int success;
mpc_result_t r;
mpc_parser_t *p = mpc_count(3, mpcf_strfold, mpc_digit(), free);
success = mpc_parse("test", "046", p, &r);
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "046");
free(r.output);
success = mpc_parse("test", "046aa", p, &r);
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "046");
free(r.output);
success = mpc_parse("test", "04632", p, &r);
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "046");
free(r.output);
success = mpc_parse("test", "04", p, &r);
PT_ASSERT(!success);
mpc_err_delete(r.error);
mpc_delete(p);
}
void test_copy(void) {
int success;
mpc_result_t r;
mpc_parser_t* p = mpc_or(2, mpc_char('a'), mpc_char('b'));
mpc_parser_t* q = mpc_and(2, mpcf_strfold, p, mpc_copy(p), free);
success = mpc_parse("test", "aa", q, &r);
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "aa");
@@ -130,26 +130,26 @@ void test_copy(void) {
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "bb");
free(r.output);
success = mpc_parse("test", "ab", q, &r);
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "ab");
free(r.output);
success = mpc_parse("test", "ba", q, &r);
PT_ASSERT(success);
PT_ASSERT_STR_EQ(r.output, "ba");
free(r.output);
success = mpc_parse("test", "c", p, &r);
PT_ASSERT(!success);
mpc_err_delete(r.error);
mpc_delete(mpc_copy(p));
mpc_delete(mpc_copy(q));
mpc_delete(q);
}
static int line_count = 0;
@@ -162,23 +162,23 @@ static mpc_val_t* read_line(mpc_val_t* line) {
void test_reader(void) {
mpc_parser_t* Line = mpc_many(
mpcf_strfold,
mpcf_strfold,
mpc_apply(mpc_re("[^\\n]*(\\n|$)"), read_line));
line_count = 0;
PT_ASSERT(mpc_test_pass(Line,
"hello\nworld\n\nthis\nis\ndan",
PT_ASSERT(mpc_test_pass(Line,
"hello\nworld\n\nthis\nis\ndan",
"hello\nworld\n\nthis\nis\ndan", streq, free, strprint));
PT_ASSERT(line_count == 6);
line_count = 0;
PT_ASSERT(mpc_test_pass(Line,
"abcHVwufvyuevuy3y436782\n\n\nrehre\nrew\n-ql.;qa\neg",
PT_ASSERT(mpc_test_pass(Line,
"abcHVwufvyuevuy3y436782\n\n\nrehre\nrew\n-ql.;qa\neg",
"abcHVwufvyuevuy3y436782\n\n\nrehre\nrew\n-ql.;qa\neg", streq, free, strprint));
PT_ASSERT(line_count == 7);
mpc_delete(Line);
@@ -194,32 +194,32 @@ static mpc_val_t *print_token(mpc_val_t *x) {
}
void test_tokens(void) {
mpc_parser_t* Tokens = mpc_many(
mpcf_strfold,
mpcf_strfold,
mpc_apply(mpc_strip(mpc_re("\\s*([a-zA-Z_]+|[0-9]+|,|\\.|:)")), print_token));
token_count = 0;
PT_ASSERT(mpc_test_pass(Tokens,
" hello 4352 , \n foo.bar \n\n test:ing ",
PT_ASSERT(mpc_test_pass(Tokens,
" hello 4352 , \n foo.bar \n\n test:ing ",
"hello4352,foo.bartest:ing", streq, free, strprint));
PT_ASSERT(token_count == 9);
mpc_delete(Tokens);
}
void test_eoi(void) {
mpc_parser_t* Line = mpc_re("[^\\n]*$");
PT_ASSERT(mpc_test_pass(Line, "blah", "blah", streq, free, strprint));
PT_ASSERT(mpc_test_pass(Line, "blah\n", "blah\n", streq, free, strprint));
mpc_delete(Line);
}
void suite_core(void) {

View File

@@ -35,7 +35,7 @@ enum {
YELLOW = 6,
WHITE = 7,
GRAY = 8,
LIGHT_BLUE = 9,
LIGHT_GREEN = 10,
LIGHT_AQUA = 11,
@@ -43,7 +43,7 @@ enum {
LIGHT_PURPLE = 13,
LIGHT_YELLOW = 14,
LIGHT_WHITE = 15,
DEFAULT = 16
};
@@ -55,16 +55,16 @@ static WORD defaults;
static int defaults_loaded = 0;
static void pt_color(int color) {
HANDLE cnsl = GetStdHandle(STD_OUTPUT_HANDLE);
if (!defaults_loaded) {
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(cnsl, &info);
defaults = info.wAttributes;
defaults_loaded = 1;
}
SetConsoleTextAttribute(cnsl, color == DEFAULT ? defaults : color);
}
@@ -90,7 +90,7 @@ static const char* colors[] = {
"\x1B[39m",
};
static void pt_color(int color) {
static void pt_color(int color) {
printf("%s", colors[color]);
}
@@ -107,27 +107,27 @@ static char assert_err_buff[MAX_ERROR];
static int assert_err_num = 0;
void pt_assert_run(int result, const char* expr, 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",
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 = 0;
switch( sig ) {
case SIGFPE: sprintf(assert_err_buff,
" %i. Division by Zero\n", assert_err_num+1);
@@ -140,18 +140,18 @@ static void ptest_signal(int sig) {
break;
default: break;
}
assert_err_num++;
strcat(assert_err, assert_err_buff);
pt_color(RED);
pt_color(RED);
printf("Failed! \n\n%s\n", assert_err);
pt_color(DEFAULT);
puts(" | Stopping Execution.");
fflush(stdout);
exit(0);
}
/* Tests */
@@ -160,27 +160,27 @@ static void pt_title_case(char* output, const char* input) {
int space = 1;
unsigned int i;
strcpy(output, input);
for(i = 0; i < strlen(output); i++) {
if (output[i] == '_' || output[i] == ' ') {
space = 1;
output[i] = ' ';
continue;
}
}
if (space && output[i] >= 'a' && output[i] <= 'z') {
space = 0;
output[i] = output[i] - 32;
continue;
}
space = 0;
}
}
typedef struct {
@@ -200,24 +200,24 @@ void pt_add_test(void (*func)(void), const char* name, const char* suite) {
test_t test;
if (num_tests == MAX_TESTS) {
printf("ERROR: Exceeded maximum test count of %i!\n",
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",
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",
printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n",
suite, MAX_NAME); abort();
}
test.func = func;
pt_title_case(test.name, name);
pt_title_case(test.suite, suite);
tests[num_tests] = test;
num_tests++;
}
@@ -239,7 +239,7 @@ static clock_t start, end;
static char current_suite[MAX_NAME];
int pt_run(void) {
int i;
double total;
test_t test;
@@ -252,18 +252,18 @@ int pt_run(void) {
puts(" | |");
puts(" | Daniel Holden (contact@theorangeduck.com) |");
puts(" +-------------------------------------------+");
signal(SIGFPE, ptest_signal);
signal(SIGILL, ptest_signal);
signal(SIGSEGV, ptest_signal);
start = clock();
strcpy(current_suite, "");
for(i = 0; i < num_tests; i++) {
test = tests[i];
/* Check for transition to a new suite */
if (strcmp(test.suite, current_suite)) {
@@ -275,25 +275,25 @@ int pt_run(void) {
num_suites_fails++;
}
}
suite_passing = 1;
strcpy(current_suite, test.suite);
printf("\n\n ===== %s =====\n\n", current_suite);
}
/* Run Test */
test_passing = 1;
strcpy(assert_err, "");
strcpy(assert_err_buff, "");
assert_err_num = 0;
printf(" | %s ... ", test.name);
fflush(stdout);
test.func();
suite_passing = suite_passing && test_passing;
if (test_passing) {
num_tests_passes++;
pt_color(GREEN);
@@ -301,56 +301,56 @@ int pt_run(void) {
pt_color(DEFAULT);
} else {
num_tests_fails++;
pt_color(RED);
pt_color(RED);
printf("Failed! \n\n%s\n", assert_err);
pt_color(DEFAULT);
}
}
if (suite_passing) {
num_suites_passes++;
} else {
num_suites_fails++;
}
end = clock();
puts("");
puts(" +---------------------------------------------------+");
puts(" | Summary |");
puts(" +---------++------------+-------------+-------------+");
printf(" | Suites ||");
pt_color(YELLOW); printf(" Total %4d ", num_suites);
pt_color(YELLOW); printf(" Total %4d ", num_suites);
pt_color(DEFAULT); putchar('|');
pt_color(GREEN); printf(" Passed %4d ", num_suites_passes);
pt_color(GREEN); printf(" Passed %4d ", num_suites_passes);
pt_color(DEFAULT); putchar('|');
pt_color(RED); printf(" Failed %4d ", num_suites_fails);
pt_color(RED); printf(" Failed %4d ", num_suites_fails);
pt_color(DEFAULT); puts("|");
printf(" | Tests ||");
pt_color(YELLOW); printf(" Total %4d ", num_tests);
pt_color(YELLOW); printf(" Total %4d ", num_tests);
pt_color(DEFAULT); putchar('|');
pt_color(GREEN); printf(" Passed %4d ", num_tests_passes);
pt_color(GREEN); printf(" Passed %4d ", num_tests_passes);
pt_color(DEFAULT); putchar('|');
pt_color(RED); printf(" Failed %4d ", num_tests_fails);
pt_color(RED); printf(" Failed %4d ", num_tests_fails);
pt_color(DEFAULT); puts("|");
printf(" | Asserts ||");
pt_color(YELLOW); printf(" Total %4d ", num_asserts);
pt_color(YELLOW); printf(" Total %4d ", num_asserts);
pt_color(DEFAULT); putchar('|');
pt_color(GREEN); printf(" Passed %4d ", num_assert_passes);
pt_color(GREEN); printf(" Passed %4d ", num_assert_passes);
pt_color(DEFAULT); putchar('|');
pt_color(RED); printf(" Failed %4d ", num_assert_fails);
pt_color(RED); printf(" Failed %4d ", num_assert_fails);
pt_color(DEFAULT); puts("|");
puts(" +---------++------------+-------------+-------------+");
puts("");
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; }
}

View File

@@ -25,7 +25,7 @@ void test_regex_basic(void) {
re3 = mpc_re("abc(abdd)?");
re4 = mpc_re("ab|c(abdd)?");
re5 = mpc_re("abc(ab|dd)+g$");
PT_ASSERT(regex_test_pass(re0, "abc", "abc"));
PT_ASSERT(regex_test_pass(re0, "bcd", "bcd"));
PT_ASSERT(regex_test_fail(re0, "bc", "bc"));
@@ -35,7 +35,7 @@ void test_regex_basic(void) {
PT_ASSERT(regex_test_pass(re2, "abcabab", "abcabab"));
PT_ASSERT(regex_test_pass(re2, "abcababd", "abcabab"));
PT_ASSERT(regex_test_pass(re5, "abcddg", "abcddg"));
mpc_delete(re0);
mpc_delete(re1);
mpc_delete(re2);
@@ -57,47 +57,47 @@ void test_regex_boundary(void) {
PT_ASSERT(regex_test_pass(re0, "foo.", "foo"));
PT_ASSERT(regex_test_pass(re0, "foo)", "foo"));
PT_ASSERT(regex_test_pass(re0, "foo baz", "foo"));
PT_ASSERT(regex_test_fail(re0, "foobar", "foo"));
PT_ASSERT(regex_test_fail(re0, "foo3", "foo"));
PT_ASSERT(regex_test_pass(re1, "foo", "foo"));
PT_ASSERT(regex_test_pass(re1, " foo", " foo"));
PT_ASSERT(regex_test_fail(re1, "wfoo", "foo"));
PT_ASSERT(regex_test_pass(re2, "python", "python"));
PT_ASSERT(regex_test_pass(re2, "py3", "py3"));
PT_ASSERT(regex_test_pass(re2, "py2", "py2"));
PT_ASSERT(regex_test_fail(re2, "py", "py"));
PT_ASSERT(regex_test_fail(re2, "py.", "py."));
PT_ASSERT(regex_test_fail(re2, "py!", "py!"));
mpc_delete(re0);
mpc_delete(re1);
mpc_delete(re2);
}
void test_regex_range(void) {
mpc_parser_t *re0, *re1, *re2, *re3;
re0 = mpc_re("abg[abcdef]");
re1 = mpc_re("y*[a-z]");
re2 = mpc_re("zz(p+)?[A-Z_0\\]123]*");
re3 = mpc_re("^[^56hy].*$");
/* TODO: Testing */
mpc_delete(re0);
mpc_delete(re1);
mpc_delete(re2);
mpc_delete(re3);
}
void test_regex_string(void) {
mpc_parser_t *re0 = mpc_re("\"(\\\\.|[^\"])*\"");
PT_ASSERT(regex_test_pass(re0, "\"there\"", "\"there\""));
@@ -110,26 +110,26 @@ void test_regex_string(void) {
}
void test_regex_lisp_comment(void) {
mpc_parser_t *re0 = mpc_re(";[^\\n\\r]*");
PT_ASSERT(regex_test_pass(re0, ";comment", ";comment"));
PT_ASSERT(regex_test_pass(re0, ";i am the\nman", ";i am the"));
mpc_delete(re0);
}
void test_regex_newline(void) {
mpc_parser_t *re0 = mpc_re("[a-z]*");
PT_ASSERT(regex_test_pass(re0, "hi", "hi"));
PT_ASSERT(regex_test_pass(re0, "hi\nk", "hi"));
PT_ASSERT(regex_test_fail(re0, "hi\nk", "hi\nk"));
mpc_delete(re0);
}
void test_regex_multiline(void) {
@@ -142,13 +142,13 @@ void test_regex_multiline(void) {
PT_ASSERT(regex_test_fail(re0, "45234", "45234"));
PT_ASSERT(regex_test_fail(re0, "\n45234", "\n45234"));
PT_ASSERT(regex_test_pass(re0, "\n45234", "\n"));
mpc_delete(re0);
}
void test_regex_dotall(void) {
mpc_parser_t *re0 = mpc_re_mode("^.*$", MPC_RE_DEFAULT);
mpc_parser_t *re1 = mpc_re_mode("^.*$", MPC_RE_DOTALL);
@@ -157,16 +157,16 @@ void test_regex_dotall(void) {
PT_ASSERT(regex_test_fail(re0, "he\nllo\n", "he"));
PT_ASSERT(regex_test_pass(re0, "34njaksdklmasd", "34njaksdklmasd"));
PT_ASSERT(regex_test_fail(re0, "34njaksd\nklmasd", "34njaksd"));
PT_ASSERT(regex_test_pass(re1, "hello", "hello"));
PT_ASSERT(regex_test_pass(re1, "hello\n", "hello\n"));
PT_ASSERT(regex_test_pass(re1, "he\nllo\n", "he\nllo\n"));
PT_ASSERT(regex_test_pass(re1, "34njaksdklmasd", "34njaksdklmasd"));
PT_ASSERT(regex_test_pass(re1, "34njaksd\nklmasd", "34njaksd\nklmasd"));
mpc_delete(re0);
mpc_delete(re1);
}
void suite_regex(void) {