Merge branch 'master' of https://github.com/orangeduck/mpc into nparse
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,7 +2,9 @@
|
||||
*.exe
|
||||
*.dSYM
|
||||
test
|
||||
doge
|
||||
lispy
|
||||
maths
|
||||
smallc
|
||||
examples/doge
|
||||
examples/lispy
|
||||
examples/maths
|
||||
examples/smallc
|
||||
examples/foobar
|
||||
examples/tree_traversal
|
||||
|
@@ -1,17 +0,0 @@
|
||||
# Oct. 17, 2014; Daniel Holden
|
||||
- Reverted state to use long type
|
||||
|
||||
# Oct. 16, 2014; Daniel Holden
|
||||
- Removed comments describing changes
|
||||
- Fixed warnings reported by gcc in test suite under the new compilation flags
|
||||
|
||||
# Oct. 14, 2014; Dalton Woodard
|
||||
- Fixed all compilation warnings in `mpc.h` and `mpc.c`; both now compile successfully on OS X under clang-600.0.51
|
||||
and gcc 4.9.1 with the following flags:
|
||||
```
|
||||
-std=c11 -O3 -g -Werror -Wall -Wextra -Wformat=2 -Wshadow -Wno-format-nonliteral -Wcast-align -Wwrite-strings
|
||||
-Wstrict-prototypes -Wold-style-definition -Wredundant-decls -Wnested-externs -Wmissing-include-dirs -Wswitch-default
|
||||
```
|
||||
- Changed compilation standard from ansi to c11.
|
||||
- Further small changes in source (documented in-line).
|
||||
|
11
Makefile
11
Makefile
@@ -1,10 +1,10 @@
|
||||
|
||||
CC = gcc
|
||||
STND=-ansi
|
||||
CFLAGS = $(STND) -pedantic -O3 -g -Werror -Wall -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
|
||||
CFLAGS = $(STND) -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
|
||||
|
||||
TESTS = $(wildcard tests/*.c)
|
||||
EXAMPLES = $(wildcard examples/*.c)
|
||||
@@ -20,4 +20,5 @@ examples/%: examples/%.c mpc.c
|
||||
$(CC) $(CFLAGS) $^ -lm -o $@
|
||||
|
||||
clean:
|
||||
rm -rf test examples/doge examples/lispy examples/maths examples/smallc
|
||||
rm -rf test examples/doge examples/lispy examples/maths examples/smallc \
|
||||
examples/foobar examples/tree_traversal
|
||||
|
73
README.md
73
README.md
@@ -1,7 +1,7 @@
|
||||
Micro Parser Combinators
|
||||
========================
|
||||
|
||||
Version 0.8.5
|
||||
Version 0.8.8
|
||||
|
||||
|
||||
About
|
||||
@@ -530,6 +530,16 @@ void mpc_cleanup(int n, ...);
|
||||
|
||||
To ease the task of undefining and then deleting parsers `mpc_cleanup` can be used. It takes `n` parsers as input, and undefines them all, before deleting them all.
|
||||
|
||||
* * *
|
||||
|
||||
```c
|
||||
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.
|
||||
|
||||
|
||||
Library Reference
|
||||
=================
|
||||
@@ -555,9 +565,9 @@ Common Parsers
|
||||
<tr><td><code>mpc_digits</code></td><td>Matches one or more digit</td></tr>
|
||||
<tr><td><code>mpc_hexdigits</code></td><td>Matches one or more hexdigit</td></tr>
|
||||
<tr><td><code>mpc_octdigits</code></td><td>Matches one or more octdigit</td></tr>
|
||||
<tr><td><code>mpc_lower</code></td><td>Matches and lower case character</td></tr>
|
||||
<tr><td><code>mpc_lower</code></td><td>Matches any lower case character</td></tr>
|
||||
<tr><td><code>mpc_upper</code></td><td>Matches any upper case character</td></tr>
|
||||
<tr><td><code>mpc_alpha</code></td><td>Matches and alphabet character</td></tr>
|
||||
<tr><td><code>mpc_alpha</code></td><td>Matches any alphabet character</td></tr>
|
||||
<tr><td><code>mpc_underscore</code></td><td>Matches <code>'_'</code></td></tr>
|
||||
<tr><td><code>mpc_alphanum</code></td><td>Matches any alphabet character, underscore or digit</td></tr>
|
||||
<tr><td><code>mpc_int</code></td><td>Matches digits and returns an <code>int*</code></td></tr>
|
||||
@@ -582,22 +592,22 @@ 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_stripl(mpc_parser_t *a);</code></td><td>Matches <code>a</code> striping any whitespace to the left</td></tr>
|
||||
<tr><td><code>mpc_stripr(mpc_parser_t *a);</code></td><td>Matches <code>a</code> striping any whitespace to the right</td></tr>
|
||||
<tr><td><code>mpc_strip(mpc_parser_t *a);</code></td><td>Matches <code>a</code> striping any surrounding whitespace</td></tr>
|
||||
<tr><td><code>mpc_tok(mpc_parser_t *a);</code></td><td>Matches <code>a</code> and strips any trailing whitespace</td></tr>
|
||||
<tr><td><code>mpc_sym(const char *s);</code></td><td>Matches string <code>s</code> and strips any trailing whitespace</td></tr>
|
||||
<tr><td><code>mpc_total(mpc_parser_t *a, mpc_dtor_t da);</code></td><td>Matches the whitespace stripped <code>a</code>, enclosed in the start and 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>
|
||||
<tr><td><code>mpc_tok(mpc_parser_t *a);</code></td><td>Matches <code>a</code> and consumes any trailing whitespace</td></tr>
|
||||
<tr><td><code>mpc_sym(const char *s);</code></td><td>Matches string <code>s</code> and consumes any trailing whitespace</td></tr>
|
||||
<tr><td><code>mpc_total(mpc_parser_t *a, mpc_dtor_t da);</code></td><td>Matches the whitespace consumed <code>a</code>, enclosed in the start and end of input</td></tr>
|
||||
<tr><td><code>mpc_between(mpc_parser_t *a, mpc_dtor_t ad, <br /> const char *o, const char *c);</code></td><td> Matches <code>a</code> between strings <code>o</code> and <code>c</code></td></tr>
|
||||
<tr><td><code>mpc_parens(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between <code>"("</code> and <code>")"</code></td></tr>
|
||||
<tr><td><code>mpc_braces(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between <code>"<"</code> and <code>">"</code></td></tr>
|
||||
<tr><td><code>mpc_brackets(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between <code>"{"</code> and <code>"}"</code></td></tr>
|
||||
<tr><td><code>mpc_squares(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between <code>"["</code> and <code>"]"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_between(mpc_parser_t *a, mpc_dtor_t ad, <br /> const char *o, const char *c);</code></td><td>Matches <code>a</code> between <code>o</code> and <code>c</code>, where <code>o</code> and <code>c</code> have their trailing whitespace striped.</td></tr>
|
||||
<tr><td><code>mpc_tok_parens(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace stripped <code>"("</code> and <code>")"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_braces(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace stripped <code>"<"</code> and <code>">"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_brackets(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace stripped <code>"{"</code> and <code>"}"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_squares(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace stripped <code>"["</code> and <code>"]"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_parens(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace consumed <code>"("</code> and <code>")"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_braces(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace consumed <code>"<"</code> and <code>">"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_brackets(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace consumed <code>"{"</code> and <code>"}"</code></td></tr>
|
||||
<tr><td><code>mpc_tok_squares(mpc_parser_t *a, mpc_dtor_t ad);</code></td><td>Matches <code>a</code> between trailing whitespace consumed <code>"["</code> and <code>"]"</code></td></tr>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -623,7 +633,9 @@ Apply Functions
|
||||
<tr><td><code>mpc_val_t *mpcf_unescape_regex(mpc_val_t *x);</code></td><td>Converts a regex <code>x</code> to an unescaped version</td></tr>
|
||||
<tr><td><code>mpc_val_t *mpcf_unescape_string_raw(mpc_val_t *x);</code></td><td>Converts a raw string <code>x</code> to an unescaped version</td></tr>
|
||||
<tr><td><code>mpc_val_t *mpcf_unescape_char_raw(mpc_val_t *x);</code></td><td>Converts a raw character <code>x</code> to an unescaped version</td></tr>
|
||||
|
||||
<tr><td><code>mpc_val_t *mpcf_strtriml(mpc_val_t *x);</code></td><td>Trims whitespace from the left of string <code>x</code></td></tr>
|
||||
<tr><td><code>mpc_val_t *mpcf_strtrimr(mpc_val_t *x);</code></td><td>Trims whitespace from the right of string <code>x</code></td></tr>
|
||||
<tr><td><code>mpc_val_t *mpcf_strtrim(mpc_val_t *x);</code></td><td>Trims whitespace from either side of string <code>x</code></td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -684,14 +696,14 @@ mpc_parser_t *Maths = mpc_new("maths");
|
||||
|
||||
mpc_define(Expr, mpc_or(2,
|
||||
mpc_and(3, fold_maths,
|
||||
Factor, mpc_oneof("*/"), Factor,
|
||||
Factor, mpc_oneof("+-"), Factor,
|
||||
free, free),
|
||||
Factor
|
||||
));
|
||||
|
||||
mpc_define(Factor, mpc_or(2,
|
||||
mpc_and(3, fold_maths,
|
||||
Term, mpc_oneof("+-"), Term,
|
||||
Term, mpc_oneof("*/"), Term,
|
||||
free, free),
|
||||
Term
|
||||
));
|
||||
@@ -784,6 +796,35 @@ _mpc_ provides some automatic generation of error messages. These can be enhance
|
||||
<test>:0:3: error: expected one or more of 'a' or 'd' at 'k'
|
||||
```
|
||||
|
||||
Misc
|
||||
====
|
||||
|
||||
Here are some other misc functions that mpc provides. These functions are susceptible to change between versions so use them with some care.
|
||||
|
||||
* * *
|
||||
|
||||
```c
|
||||
void mpc_print(mpc_parser_t *p);
|
||||
```
|
||||
|
||||
Prints out a parser in some weird format. This is generally used for debugging so don't expect to be able to understand the output right away without looking at the source code a little bit.
|
||||
|
||||
* * *
|
||||
|
||||
```c
|
||||
void mpc_stats(mpc_parser_t *p);
|
||||
```
|
||||
|
||||
Prints out some basic stats about a parser. Again used for debugging and optimisation.
|
||||
|
||||
* * *
|
||||
|
||||
```c
|
||||
void mpc_optimise(mpc_parser_t *p);
|
||||
```
|
||||
|
||||
Performs some basic optimisations on a parser to reduce it's size and increase its running speed.
|
||||
|
||||
|
||||
Limitations & FAQ
|
||||
=================
|
||||
|
@@ -1,6 +1,8 @@
|
||||
#include "../mpc.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
mpc_result_t r;
|
||||
|
||||
mpc_parser_t* Adjective = mpc_new("adjective");
|
||||
mpc_parser_t* Noun = mpc_new("noun");
|
||||
@@ -13,10 +15,9 @@ int main(int argc, char **argv) {
|
||||
" phrase : <adjective> <noun>; "
|
||||
" doge : /^/ <phrase>* /$/; ",
|
||||
Adjective, Noun, Phrase, Doge, NULL);
|
||||
|
||||
|
||||
if (argc > 1) {
|
||||
|
||||
mpc_result_t r;
|
||||
if (mpc_parse_contents(argv[1], Doge, &r)) {
|
||||
mpc_ast_print(r.output);
|
||||
mpc_ast_delete(r.output);
|
||||
@@ -24,10 +25,9 @@ int main(int argc, char **argv) {
|
||||
mpc_err_print(r.error);
|
||||
mpc_err_delete(r.error);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
mpc_result_t r;
|
||||
if (mpc_parse_pipe("<stdin>", stdin, Doge, &r)) {
|
||||
mpc_ast_print(r.output);
|
||||
mpc_ast_delete(r.output);
|
||||
|
28
examples/foobar.c
Normal file
28
examples/foobar.c
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "../mpc.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
mpc_result_t r;
|
||||
mpc_parser_t* Foobar;
|
||||
|
||||
if (argc != 2) {
|
||||
printf("Usage: ./foobar <foo/bar>\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Foobar = mpc_new("foobar");
|
||||
mpca_lang(MPCA_LANG_DEFAULT, "foobar : \"foo\" | \"bar\";", Foobar);
|
||||
|
||||
if (mpc_parse("<stdin>", argv[1], Foobar, &r)) {
|
||||
mpc_ast_print(r.output);
|
||||
mpc_ast_delete(r.output);
|
||||
} else {
|
||||
mpc_err_print(r.error);
|
||||
mpc_err_delete(r.error);
|
||||
}
|
||||
|
||||
mpc_cleanup(1, Foobar);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
mpc_result_t r;
|
||||
|
||||
mpc_parser_t* Number = mpc_new("number");
|
||||
mpc_parser_t* Symbol = mpc_new("symbol");
|
||||
mpc_parser_t* String = mpc_new("string");
|
||||
@@ -22,10 +24,9 @@ int main(int argc, char **argv) {
|
||||
" | <comment> | <sexpr> | <qexpr> ; "
|
||||
" lispy : /^/ <expr>* /$/ ; ",
|
||||
Number, Symbol, String, Comment, Sexpr, Qexpr, Expr, Lispy, NULL);
|
||||
|
||||
|
||||
if (argc > 1) {
|
||||
|
||||
mpc_result_t r;
|
||||
|
||||
if (mpc_parse_contents(argv[1], Lispy, &r)) {
|
||||
mpc_ast_print(r.output);
|
||||
mpc_ast_delete(r.output);
|
||||
@@ -33,10 +34,9 @@ int main(int argc, char **argv) {
|
||||
mpc_err_print(r.error);
|
||||
mpc_err_delete(r.error);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
mpc_result_t r;
|
||||
if (mpc_parse_pipe("<stdin>", stdin, Lispy, &r)) {
|
||||
mpc_ast_print(r.output);
|
||||
mpc_ast_delete(r.output);
|
||||
|
2009
examples/so_c.doge
Normal file
2009
examples/so_c.doge
Normal file
File diff suppressed because it is too large
Load Diff
119
examples/tree_traversal.c
Normal file
119
examples/tree_traversal.c
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "../mpc.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
mpc_parser_t *Input = mpc_new("input");
|
||||
mpc_parser_t *Node = mpc_new("node");
|
||||
mpc_parser_t *Leaf = mpc_new("leaf");
|
||||
mpc_ast_t *ast, *tree, *child, *child_sub, *ast_next;
|
||||
mpc_ast_trav_t *trav;
|
||||
mpc_result_t r;
|
||||
int index, lb, i;
|
||||
|
||||
mpca_lang(MPCA_LANG_PREDICTIVE,
|
||||
" node : '(' <node> ',' /foo/ ',' <node> ')' | <leaf>;"
|
||||
" leaf : /bar/;"
|
||||
" input : /^/ <node> /$/;",
|
||||
Node, Leaf, Input, NULL);
|
||||
|
||||
if (argc > 1) {
|
||||
|
||||
if (mpc_parse_contents(argv[1], Input, &r)) {
|
||||
ast = r.output;
|
||||
} else {
|
||||
mpc_err_print(r.error);
|
||||
mpc_err_delete(r.error);
|
||||
mpc_cleanup(3, Node, Leaf, Input);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (mpc_parse_pipe("<stdin>", stdin, Input, &r)) {
|
||||
ast = r.output;
|
||||
} else {
|
||||
mpc_err_print(r.error);
|
||||
mpc_err_delete(r.error);
|
||||
mpc_cleanup(3, Node, Leaf, Input);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Get index or child of tree */
|
||||
tree = ast->children[1];
|
||||
|
||||
index = mpc_ast_get_index(tree, "node|>");
|
||||
child = mpc_ast_get_child(tree, "node|>");
|
||||
|
||||
if(child == NULL) {
|
||||
mpc_cleanup(3, Node, Leaf, Input);
|
||||
mpc_ast_delete(ast);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Index: %d; Child: \"%s\"\n", index, child->tag);
|
||||
|
||||
/* Get multiple indexes or children of trees */
|
||||
index = mpc_ast_get_index_lb(child, "node|leaf|regex", 0);
|
||||
child_sub = mpc_ast_get_child_lb(child, "node|leaf|regex", 0);
|
||||
|
||||
while(index != -1) {
|
||||
printf("-- Index: %d; Child: \"%s\"\n", index, child_sub->tag);
|
||||
|
||||
lb = index + 1;
|
||||
index = mpc_ast_get_index_lb(child, "node|leaf|regex", lb);
|
||||
child_sub = mpc_ast_get_child_lb(child, "node|leaf|regex", lb);
|
||||
}
|
||||
|
||||
/* Traversal */
|
||||
printf("Pre order tree traversal.\n");
|
||||
trav = mpc_ast_traverse_start(ast, mpc_ast_trav_order_pre);
|
||||
|
||||
ast_next = mpc_ast_traverse_next(&trav);
|
||||
|
||||
while(ast_next != NULL) {
|
||||
printf("Tag: %s; Contents: %s\n",
|
||||
ast_next->tag,
|
||||
ast_next->contents);
|
||||
ast_next = mpc_ast_traverse_next(&trav);
|
||||
}
|
||||
|
||||
mpc_ast_traverse_free(&trav);
|
||||
|
||||
printf("Post order tree traversal.\n");
|
||||
|
||||
trav = mpc_ast_traverse_start(ast, mpc_ast_trav_order_post);
|
||||
|
||||
ast_next = mpc_ast_traverse_next(&trav);
|
||||
|
||||
while(ast_next != NULL) {
|
||||
printf("Tag: %s; Contents: %s\n",
|
||||
ast_next->tag,
|
||||
ast_next->contents);
|
||||
ast_next = mpc_ast_traverse_next(&trav);
|
||||
}
|
||||
|
||||
mpc_ast_traverse_free(&trav);
|
||||
|
||||
printf("Partial traversal.\n");
|
||||
|
||||
trav = mpc_ast_traverse_start(ast, mpc_ast_trav_order_post);
|
||||
|
||||
ast_next = mpc_ast_traverse_next(&trav);
|
||||
|
||||
for(i=0; i<2 && ast_next != NULL; i++) {
|
||||
printf("Tag: %s; Contents: %s\n",
|
||||
ast_next->tag,
|
||||
ast_next->contents);
|
||||
ast_next = mpc_ast_traverse_next(&trav);
|
||||
}
|
||||
|
||||
mpc_ast_traverse_free(&trav);
|
||||
|
||||
/* Clean up and return */
|
||||
mpc_cleanup(3, Node, Leaf, Input);
|
||||
mpc_ast_delete(ast);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
35
mpc.h
35
mpc.h
@@ -16,6 +16,7 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** State Type
|
||||
@@ -81,6 +82,7 @@ typedef mpc_val_t*(*mpc_fold_t)(int,mpc_val_t**);
|
||||
*/
|
||||
|
||||
mpc_parser_t *mpc_new(const char *name);
|
||||
mpc_parser_t *mpc_copy(mpc_parser_t *a);
|
||||
mpc_parser_t *mpc_define(mpc_parser_t *p, mpc_parser_t *a);
|
||||
mpc_parser_t *mpc_undefine(mpc_parser_t *p);
|
||||
|
||||
@@ -219,6 +221,9 @@ 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_strtriml(mpc_val_t *x);
|
||||
mpc_val_t *mpcf_strtrimr(mpc_val_t *x);
|
||||
mpc_val_t *mpcf_strtrim(mpc_val_t *x);
|
||||
|
||||
mpc_val_t *mpcf_escape(mpc_val_t *x);
|
||||
mpc_val_t *mpcf_escape_regex(mpc_val_t *x);
|
||||
@@ -265,6 +270,7 @@ mpc_ast_t *mpc_ast_build(int n, const char *tag, ...);
|
||||
mpc_ast_t *mpc_ast_add_root(mpc_ast_t *a);
|
||||
mpc_ast_t *mpc_ast_add_child(mpc_ast_t *r, mpc_ast_t *a);
|
||||
mpc_ast_t *mpc_ast_add_tag(mpc_ast_t *a, const char *t);
|
||||
mpc_ast_t *mpc_ast_add_root_tag(mpc_ast_t *a, const char *t);
|
||||
mpc_ast_t *mpc_ast_tag(mpc_ast_t *a, const char *t);
|
||||
mpc_ast_t *mpc_ast_state(mpc_ast_t *a, mpc_state_t s);
|
||||
|
||||
@@ -272,6 +278,30 @@ void mpc_ast_delete(mpc_ast_t *a);
|
||||
void mpc_ast_print(mpc_ast_t *a);
|
||||
void mpc_ast_print_to(mpc_ast_t *a, FILE *fp);
|
||||
|
||||
int mpc_ast_get_index(mpc_ast_t *ast, const char *tag);
|
||||
int mpc_ast_get_index_lb(mpc_ast_t *ast, const char *tag, int lb);
|
||||
mpc_ast_t *mpc_ast_get_child(mpc_ast_t *ast, const char *tag);
|
||||
mpc_ast_t *mpc_ast_get_child_lb(mpc_ast_t *ast, const char *tag, int lb);
|
||||
|
||||
typedef enum {
|
||||
mpc_ast_trav_order_pre,
|
||||
mpc_ast_trav_order_post
|
||||
} mpc_ast_trav_order_t;
|
||||
|
||||
typedef struct mpc_ast_trav_t {
|
||||
mpc_ast_t *curr_node;
|
||||
struct mpc_ast_trav_t *parent;
|
||||
int curr_child;
|
||||
mpc_ast_trav_order_t order;
|
||||
} mpc_ast_trav_t;
|
||||
|
||||
mpc_ast_trav_t *mpc_ast_traverse_start(mpc_ast_t *ast,
|
||||
mpc_ast_trav_order_t order);
|
||||
|
||||
mpc_ast_t *mpc_ast_traverse_next(mpc_ast_trav_t **trav);
|
||||
|
||||
void mpc_ast_traverse_free(mpc_ast_trav_t **trav);
|
||||
|
||||
/*
|
||||
** Warning: This function currently doesn't test for equality of the `state` member!
|
||||
*/
|
||||
@@ -311,10 +341,13 @@ mpc_err_t *mpca_lang_pipe(int flags, FILE *f, ...);
|
||||
mpc_err_t *mpca_lang_contents(int flags, const char *filename, ...);
|
||||
|
||||
/*
|
||||
** Debug & Testing
|
||||
** Misc
|
||||
*/
|
||||
|
||||
|
||||
void mpc_print(mpc_parser_t *p);
|
||||
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*),
|
||||
|
9
package.json
Normal file
9
package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "mpc",
|
||||
"version": "0.8.8",
|
||||
"repo": "orangeduck/mpc",
|
||||
"description": "A Parser Combinator library for C",
|
||||
"keywords": ["parser", "combinator", "library", "c", "mpc"],
|
||||
"license": "BSD",
|
||||
"src": ["mpc.c", "mpc.h"]
|
||||
}
|
110
tests/core.c
110
tests/core.c
@@ -6,8 +6,8 @@
|
||||
|
||||
static int int_eq(const void* x, const void* y) { return (*(int*)x == *(int*)y); }
|
||||
static void int_print(const void* x) { printf("'%i'", *((int*)x)); }
|
||||
static int string_eq(const void* x, const void* y) { return (strcmp(x, y) == 0); }
|
||||
static void string_print(const void* x) { printf("'%s'", (char*)x); }
|
||||
static int streq(const void* x, const void* y) { return (strcmp(x, y) == 0); }
|
||||
static void strprint(const void* x) { printf("'%s'", (char*)x); }
|
||||
|
||||
void test_ident(void) {
|
||||
|
||||
@@ -21,13 +21,13 @@ void test_ident(void) {
|
||||
free
|
||||
);
|
||||
|
||||
PT_ASSERT(mpc_test_pass(Ident, "test", "test", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_test_fail(Ident, " blah", "", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_test_pass(Ident, "anoth21er", "anoth21er", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_test_pass(Ident, "du__de", "du__de", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_test_fail(Ident, "some spaces", "", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_test_fail(Ident, "", "", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_test_fail(Ident, "18nums", "", string_eq, free, string_print));
|
||||
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));
|
||||
PT_ASSERT(mpc_test_pass(Ident, "du__de", "du__de", streq, free, strprint));
|
||||
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);
|
||||
|
||||
@@ -69,7 +69,93 @@ void test_maths(void) {
|
||||
mpc_cleanup(4, Expr, Factor, Term, Maths);
|
||||
}
|
||||
|
||||
void suite_core(void) {
|
||||
pt_add_test(test_ident, "Test Ident", "Suite Core");
|
||||
pt_add_test(test_maths, "Test Maths", "Suite Core");
|
||||
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");
|
||||
free(r.output);
|
||||
|
||||
success = mpc_parse("test", "bb", q, &r);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
void suite_core(void) {
|
||||
pt_add_test(test_ident, "Test Ident", "Suite Core");
|
||||
pt_add_test(test_maths, "Test Maths", "Suite Core");
|
||||
pt_add_test(test_strip, "Test Strip", "Suite Core");
|
||||
pt_add_test(test_repeat, "Test Repeat", "Suite Core");
|
||||
pt_add_test(test_copy, "Test Copy", "Suite Core");
|
||||
}
|
||||
|
168
tests/grammar.c
168
tests/grammar.c
@@ -25,7 +25,7 @@ void test_grammar(void) {
|
||||
|
||||
t2 = mpc_ast_build(3, ">",
|
||||
|
||||
mpc_ast_build(3, "value|>",
|
||||
mpc_ast_build(3, "product|value|>",
|
||||
mpc_ast_new("char", "("),
|
||||
mpc_ast_build(3, "expression|>",
|
||||
|
||||
@@ -91,8 +91,174 @@ void test_language_file(void) {
|
||||
|
||||
}
|
||||
|
||||
void test_doge(void) {
|
||||
|
||||
mpc_ast_t *t0;
|
||||
mpc_parser_t* Adjective = mpc_new("adjective");
|
||||
mpc_parser_t* Noun = mpc_new("noun");
|
||||
mpc_parser_t* Phrase = mpc_new("phrase");
|
||||
mpc_parser_t* Doge = mpc_new("doge");
|
||||
|
||||
mpca_lang(MPCA_LANG_DEFAULT,
|
||||
" adjective : \"wow\" | \"many\" | \"so\" | \"such\"; "
|
||||
" noun : \"lisp\" | \"language\" | \"c\" | \"book\" | \"build\"; "
|
||||
" phrase : <adjective> <noun>; "
|
||||
" doge : /^/ <phrase>* /$/; ",
|
||||
Adjective, Noun, Phrase, Doge, NULL);
|
||||
|
||||
t0 =
|
||||
mpc_ast_build(4, ">",
|
||||
mpc_ast_new("regex", ""),
|
||||
mpc_ast_build(2, "phrase|>",
|
||||
mpc_ast_new("adjective|string", "so"),
|
||||
mpc_ast_new("noun|string", "c")),
|
||||
mpc_ast_build(2, "phrase|>",
|
||||
mpc_ast_new("adjective|string", "so"),
|
||||
mpc_ast_new("noun|string", "c")),
|
||||
mpc_ast_new("regex", "")
|
||||
);
|
||||
|
||||
PT_ASSERT(mpc_test_pass(Doge, "so c so c", t0, (int(*)(const void*,const void*))mpc_ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(const void*))mpc_ast_print));
|
||||
|
||||
PT_ASSERT(mpc_test_fail(Doge, "so a so c", t0, (int(*)(const void*,const void*))mpc_ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(const void*))mpc_ast_print));
|
||||
|
||||
mpc_ast_delete(t0);
|
||||
|
||||
mpc_cleanup(4, Adjective, Noun, Phrase, Doge);
|
||||
|
||||
}
|
||||
|
||||
void test_partial(void) {
|
||||
|
||||
mpc_ast_t *t0;
|
||||
mpc_err_t *err;
|
||||
|
||||
mpc_parser_t *Line = mpc_new("line");
|
||||
mpc_parser_t *Number = mpc_new("number");
|
||||
mpc_parser_t *QuotedString = mpc_new("quoted_string");
|
||||
mpc_parser_t *LinePragma = mpc_new("linepragma");
|
||||
mpc_parser_t *Parser = mpc_new("parser");
|
||||
|
||||
mpc_define(Line, mpca_tag(mpc_apply(mpc_sym("#line"), mpcf_str_ast), "string"));
|
||||
|
||||
err = mpca_lang(MPCA_LANG_PREDICTIVE,
|
||||
"number : /[0-9]+/ ;\n"
|
||||
"quoted_string : /\"(\\.|[^\"])*\"/ ;\n"
|
||||
"linepragma : <line> <number> <quoted_string>;\n"
|
||||
"parser : /^/ (<linepragma>)* /$/ ;\n",
|
||||
Line, Number, QuotedString, LinePragma, Parser, NULL);
|
||||
|
||||
PT_ASSERT(err == NULL);
|
||||
|
||||
t0 = mpc_ast_build(3, ">",
|
||||
mpc_ast_new("regex", ""),
|
||||
mpc_ast_build(3, "linepragma|>",
|
||||
mpc_ast_new("line|string", "#line"),
|
||||
mpc_ast_new("number|regex", "10"),
|
||||
mpc_ast_new("quoted_string|regex", "\"test\"")),
|
||||
mpc_ast_new("regex", ""));
|
||||
|
||||
PT_ASSERT(mpc_test_pass(Parser, "#line 10 \"test\"", t0,
|
||||
(int(*)(const void*,const void*))mpc_ast_eq,
|
||||
(mpc_dtor_t)mpc_ast_delete,
|
||||
(void(*)(const void*))mpc_ast_print));
|
||||
|
||||
mpc_cleanup(5, Line, Number, QuotedString, LinePragma, Parser);
|
||||
|
||||
}
|
||||
|
||||
void test_qscript(void) {
|
||||
|
||||
mpc_ast_t *t0;
|
||||
mpc_parser_t *Qscript = mpc_new("qscript");
|
||||
mpc_parser_t *Comment = mpc_new("comment");
|
||||
mpc_parser_t *Resource = mpc_new("resource");
|
||||
mpc_parser_t *Rtype = mpc_new("rtype");
|
||||
mpc_parser_t *Rname = mpc_new("rname");
|
||||
mpc_parser_t *InnerBlock = mpc_new("inner_block");
|
||||
mpc_parser_t *Statement = mpc_new("statement");
|
||||
mpc_parser_t *Function = mpc_new("function");
|
||||
mpc_parser_t *Parameter = mpc_new("parameter");
|
||||
mpc_parser_t *Literal = mpc_new("literal");
|
||||
mpc_parser_t *Block = mpc_new("block");
|
||||
mpc_parser_t *Seperator = mpc_new("seperator");
|
||||
mpc_parser_t *Qstring = mpc_new("qstring");
|
||||
mpc_parser_t *SimpleStr = mpc_new("simplestr");
|
||||
mpc_parser_t *ComplexStr = mpc_new("complexstr");
|
||||
mpc_parser_t *Number = mpc_new("number");
|
||||
mpc_parser_t *Float = mpc_new("float");
|
||||
mpc_parser_t *Int = mpc_new("int");
|
||||
|
||||
mpc_err_t *err = mpca_lang(0,
|
||||
" qscript : /^/ (<comment> | <resource>)* /$/ ;\n"
|
||||
" comment : '#' /[^\\n]*/ ;\n"
|
||||
"resource : '[' (<rtype> <rname>) ']' <inner_block> ;\n"
|
||||
" rtype : /[*]*/ ;\n"
|
||||
" rname : <qstring> ;\n"
|
||||
"\n"
|
||||
"inner_block : (<comment> | <statement>)* ;\n"
|
||||
" statement : <function> '(' (<comment> | <parameter> | <block>)* ')' <seperator> ;\n"
|
||||
" function : <qstring> ;\n"
|
||||
" parameter : (<statement> | <literal>) ;\n"
|
||||
" literal : (<number> | <qstring>) <seperator> ;\n"
|
||||
" block : '{' <inner_block> '}' ;\n"
|
||||
" seperator : ',' | \"\" ;\n"
|
||||
"\n"
|
||||
"qstring : (<complexstr> | <simplestr>) <qstring>* ;\n"
|
||||
" simplestr : /[a-zA-Z0-9_!@#$%^&\\*_+\\-\\.=\\/<>]+/ ;\n"
|
||||
" complexstr : (/\"[^\"]*\"/ | /'[^']*'/) ;\n"
|
||||
"\n"
|
||||
"number : (<float> | <int>) ;\n"
|
||||
" float : /[-+]?[0-9]+\\.[0-9]+/ ;\n"
|
||||
" int : /[-+]?[0-9]+/ ;\n",
|
||||
Qscript, Comment, Resource, Rtype, Rname, InnerBlock, Statement, Function,
|
||||
Parameter, Literal, Block, Seperator, Qstring, SimpleStr, ComplexStr, Number,
|
||||
Float, Int, NULL);
|
||||
|
||||
PT_ASSERT(err == NULL);
|
||||
|
||||
t0 = mpc_ast_build(3, ">",
|
||||
mpc_ast_new("regex", ""),
|
||||
mpc_ast_build(5, "resource|>",
|
||||
mpc_ast_new("char", "["),
|
||||
mpc_ast_new("rtype|regex", ""),
|
||||
mpc_ast_new("rname|qstring|simplestr|regex", "my_func"),
|
||||
mpc_ast_new("char", "]"),
|
||||
mpc_ast_build(5, "inner_block|statement|>",
|
||||
mpc_ast_new("function|qstring|simplestr|regex", "echo"),
|
||||
mpc_ast_new("char", "("),
|
||||
mpc_ast_build(2, "parameter|literal|>",
|
||||
mpc_ast_build(2, "qstring|>",
|
||||
mpc_ast_new("simplestr|regex", "a"),
|
||||
mpc_ast_build(2, "qstring|>",
|
||||
mpc_ast_new("simplestr|regex", "b"),
|
||||
mpc_ast_new("qstring|simplestr|regex", "c")
|
||||
)
|
||||
),
|
||||
mpc_ast_new("seperator|string", "")
|
||||
),
|
||||
mpc_ast_new("char", ")"),
|
||||
mpc_ast_new("seperator|string", "")
|
||||
)
|
||||
),
|
||||
mpc_ast_new("regex", ""));
|
||||
|
||||
PT_ASSERT(mpc_test_pass(Qscript, "[my_func]\n echo (a b c)\n", t0,
|
||||
(int(*)(const void*,const void*))mpc_ast_eq,
|
||||
(mpc_dtor_t)mpc_ast_delete,
|
||||
(void(*)(const void*))mpc_ast_print));
|
||||
|
||||
mpc_cleanup(18, Qscript, Comment, Resource, Rtype, Rname, InnerBlock,
|
||||
Statement, Function, Parameter, Literal, Block, Seperator, Qstring,
|
||||
SimpleStr, ComplexStr, Number, Float, Int);
|
||||
|
||||
}
|
||||
|
||||
void suite_grammar(void) {
|
||||
pt_add_test(test_grammar, "Test Grammar", "Suite Grammar");
|
||||
pt_add_test(test_language, "Test Language", "Suite Grammar");
|
||||
pt_add_test(test_language_file, "Test Language File", "Suite Grammar");
|
||||
pt_add_test(test_doge, "Test Doge", "Suite Grammar");
|
||||
pt_add_test(test_partial, "Test Partial", "Suite Grammar");
|
||||
pt_add_test(test_qscript, "Test QScript", "Suite Grammar");
|
||||
}
|
||||
|
182
tests/ptest.c
182
tests/ptest.c
@@ -26,32 +26,47 @@ static int suite_passing = 0;
|
||||
/* 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
|
||||
BLACK = 0,
|
||||
BLUE = 1,
|
||||
GREEN = 2,
|
||||
AQUA = 3,
|
||||
RED = 4,
|
||||
PURPLE = 5,
|
||||
YELLOW = 6,
|
||||
WHITE = 7,
|
||||
GRAY = 8,
|
||||
|
||||
LIGHT_BLUE = 9,
|
||||
LIGHT_GREEN = 10,
|
||||
LIGHT_AQUA = 11,
|
||||
LIGHT_RED = 12,
|
||||
LIGHT_PURPLE = 13,
|
||||
LIGHT_YELLOW = 14,
|
||||
LIGHT_WHITE = 15,
|
||||
|
||||
DEFAULT = 16
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <windows.h>
|
||||
|
||||
static void pt_color(int color) {
|
||||
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(hCon, color);
|
||||
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);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -71,14 +86,13 @@ static const char* colors[] = {
|
||||
"\x1B[31m",
|
||||
"\x1B[35m",
|
||||
"\x1B[33m",
|
||||
"\x1B[37m"
|
||||
"\x1B[37m",
|
||||
"\x1B[39m",
|
||||
};
|
||||
|
||||
static void pt_color(int color) {
|
||||
|
||||
printf("%s", colors[color]);
|
||||
|
||||
}
|
||||
static void pt_color(int color) {
|
||||
printf("%s", colors[color]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -92,16 +106,17 @@ static char assert_err[MAX_ERROR];
|
||||
static char assert_err_buff[MAX_ERROR];
|
||||
static int assert_err_num = 0;
|
||||
|
||||
void pt_assert_run(int result, const char* expr, const char* func, const char* file, int line) {
|
||||
void pt_assert_run(int result, const char* expr, const char* file, int line) {
|
||||
|
||||
(void) func;
|
||||
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 );
|
||||
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++;
|
||||
@@ -110,22 +125,30 @@ void pt_assert_run(int result, const char* expr, const char* func, const char* f
|
||||
}
|
||||
|
||||
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); 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;
|
||||
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;
|
||||
default: break;
|
||||
}
|
||||
|
||||
assert_err_num++;
|
||||
strcat(assert_err, assert_err_buff);
|
||||
|
||||
pt_color(WHITE); pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
|
||||
pt_color(RED);
|
||||
printf("Failed! \n\n%s\n", assert_err);
|
||||
pt_color(DEFAULT);
|
||||
|
||||
printf(" | Stopping Execution.\n");
|
||||
puts(" | Stopping Execution.");
|
||||
fflush(stdout);
|
||||
exit(0);
|
||||
|
||||
@@ -134,14 +157,14 @@ static void ptest_signal(int sig) {
|
||||
/* Tests */
|
||||
|
||||
static void pt_title_case(char* output, const char* input) {
|
||||
|
||||
|
||||
int space = 1;
|
||||
size_t i;
|
||||
unsigned int i;
|
||||
|
||||
strcpy(output, input);
|
||||
|
||||
for(i = 0; i < strlen(output); i++) {
|
||||
|
||||
|
||||
if (output[i] == '_' || output[i] == ' ') {
|
||||
space = 1;
|
||||
output[i] = ' ';
|
||||
@@ -173,19 +196,22 @@ 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) {
|
||||
|
||||
|
||||
test_t test;
|
||||
|
||||
if (num_tests == MAX_TESTS) {
|
||||
printf("ERROR: Exceeded maximum test count of %i!\n", MAX_TESTS); abort();
|
||||
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();
|
||||
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();
|
||||
printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n",
|
||||
suite, MAX_NAME); abort();
|
||||
}
|
||||
|
||||
test.func = func;
|
||||
@@ -194,7 +220,6 @@ void pt_add_test(void (*func)(void), const char* name, const char* suite) {
|
||||
|
||||
tests[num_tests] = test;
|
||||
num_tests++;
|
||||
|
||||
}
|
||||
|
||||
/* Suites */
|
||||
@@ -217,15 +242,16 @@ int pt_run(void) {
|
||||
|
||||
int i;
|
||||
double total;
|
||||
test_t test;
|
||||
|
||||
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");
|
||||
puts("");
|
||||
puts(" +-------------------------------------------+");
|
||||
puts(" | ptest MicroTesting Magic for C |");
|
||||
puts(" | |");
|
||||
puts(" | http://github.com/orangeduck/ptest |");
|
||||
puts(" | |");
|
||||
puts(" | Daniel Holden (contact@theorangeduck.com) |");
|
||||
puts(" +-------------------------------------------+");
|
||||
|
||||
signal(SIGFPE, ptest_signal);
|
||||
signal(SIGILL, ptest_signal);
|
||||
@@ -235,12 +261,12 @@ int pt_run(void) {
|
||||
strcpy(current_suite, "");
|
||||
|
||||
for(i = 0; i < num_tests; i++) {
|
||||
|
||||
test_t test = tests[i];
|
||||
|
||||
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) {
|
||||
@@ -262,6 +288,7 @@ int pt_run(void) {
|
||||
strcpy(assert_err_buff, "");
|
||||
assert_err_num = 0;
|
||||
printf(" | %s ... ", test.name);
|
||||
fflush(stdout);
|
||||
|
||||
test.func();
|
||||
|
||||
@@ -269,10 +296,14 @@ int pt_run(void) {
|
||||
|
||||
if (test_passing) {
|
||||
num_tests_passes++;
|
||||
pt_color(GREEN); printf("Passed! \n"); pt_color(WHITE);
|
||||
pt_color(GREEN);
|
||||
puts("Passed!");
|
||||
pt_color(DEFAULT);
|
||||
} else {
|
||||
num_tests_fails++;
|
||||
pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
|
||||
pt_color(RED);
|
||||
printf("Failed! \n\n%s\n", assert_err);
|
||||
pt_color(DEFAULT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -285,28 +316,37 @@ int pt_run(void) {
|
||||
|
||||
end = clock();
|
||||
|
||||
printf(" \n");
|
||||
printf(" +---------------------------------------------------+\n");
|
||||
printf(" | Summary |\n");
|
||||
printf(" +---------++------------+-------------+-------------+\n");
|
||||
puts("");
|
||||
puts(" +---------------------------------------------------+");
|
||||
puts(" | Summary |");
|
||||
puts(" +---------++------------+-------------+-------------+");
|
||||
|
||||
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");
|
||||
pt_color(YELLOW); printf(" Total %4d ", num_suites);
|
||||
pt_color(DEFAULT); putchar('|');
|
||||
pt_color(GREEN); printf(" Passed %4d ", num_suites_passes);
|
||||
pt_color(DEFAULT); putchar('|');
|
||||
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(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");
|
||||
pt_color(YELLOW); printf(" Total %4d ", num_tests);
|
||||
pt_color(DEFAULT); putchar('|');
|
||||
pt_color(GREEN); printf(" Passed %4d ", num_tests_passes);
|
||||
pt_color(DEFAULT); putchar('|');
|
||||
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(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");
|
||||
pt_color(YELLOW); printf(" Total %4d ", num_asserts);
|
||||
pt_color(DEFAULT); putchar('|');
|
||||
pt_color(GREEN); printf(" Passed %4d ", num_assert_passes);
|
||||
pt_color(DEFAULT); putchar('|');
|
||||
pt_color(RED); printf(" Failed %4d ", num_assert_fails);
|
||||
pt_color(DEFAULT); puts("|");
|
||||
|
||||
printf(" +---------++------------+-------------+-------------+\n");
|
||||
printf(" \n");
|
||||
puts(" +---------++------------+-------------+-------------+");
|
||||
puts("");
|
||||
|
||||
total = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
|
||||
|
@@ -6,17 +6,16 @@
|
||||
#define PT_SUITE(name) void name(void)
|
||||
|
||||
#define PT_FUNC(name) static void name(void)
|
||||
#define PT_REG(name) pt_add_test(name, #name, __func__)
|
||||
#define PT_REG(name) pt_add_test(name, #name)
|
||||
#define PT_TEST(name) auto void name(void); PT_REG(name); void name(void)
|
||||
|
||||
#define PT_ASSERT(expr) pt_assert_run((int)(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__)
|
||||
#define PT_ASSERT(expr) pt_assert_run((int)(expr), #expr, __FILE__, __LINE__)
|
||||
#define PT_ASSERT_STR_EQ(fst, snd) pt_assert_run(strcmp(fst, snd) == 0, "strcmp( " #fst ", " #snd " ) == 0", __FILE__, __LINE__)
|
||||
|
||||
void pt_assert_run(int result, const char* expr, const char* func, const char* file, int line);
|
||||
void pt_assert_run(int result, const char* expr, 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
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user