Adding tests, fixing small things

parent b2d4c20d
......@@ -281,6 +281,9 @@ Cleanup a parse result.
Can be used to reset the expect list to start. Usually you are not required to
use this function since the expect list is already at the start position.
#### `void cleri_parse_strn(char * s, size_t n, cleri_parse_t * pr, cleri_translate_t * translate)`
TODO: Explain with small example
### `cleri_node_t`
Node object. A parse result has a parse tree which consists of nodes. Each node
may have children.
......
......@@ -79,7 +79,7 @@ C_DEPS += \
src/%.o: ../src/%.c
@echo 'Building file: $<'
@echo 'Invoking: Cross GCC Compiler'
gcc -I../inc $(CPPFLAGS) -O3 -Wall $(CFLAGS) -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
gcc -DNDEBUG -I../inc $(CPPFLAGS) -O3 -Wall $(CFLAGS) -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
@echo 'Finished building: $<'
@echo ' '
......
echo -n "json: " && cd json && gcc main.c json.c -lcleri && ./a.out
echo -n "choice: " && cd ../choice && gcc main.c -lcleri && ./a.out
echo -n "keyword: " && cd ../keyword && gcc main.c -lcleri && ./a.out
echo -n "list: " && cd ../list && gcc main.c -lcleri && ./a.out
echo -n "optional: " && cd ../optional && gcc main.c -lcleri && ./a.out
echo -n "prio: " && cd ../prio && gcc main.c -lcleri && ./a.out
echo -n "ref: " && cd ../ref && gcc main.c -lcleri && ./a.out
echo -n "repeat: " && cd ../repeat && gcc main.c -lcleri && ./a.out
echo -n "sequence: " && cd ../sequence && gcc main.c -lcleri && ./a.out
echo -n "token: " && cd ../token && gcc main.c -lcleri && ./a.out
echo -n "tokens: " && cd ../tokens && gcc main.c -lcleri && ./a.out
......@@ -12,6 +12,8 @@
#ifndef CLERI_GRAMMAR_H_
#define CLERI_GRAMMAR_H_
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include <cleri/cleri.h>
#include <cleri/olist.h>
......
......@@ -29,6 +29,7 @@ typedef struct cleri_expecting_s cleri_expecting_t;
typedef struct cleri_kwcache_s cleri_kwcache_t;
typedef struct cleri_rule_store_s cleri_rule_store_t;
typedef struct cleri_parse_s cleri_parse_t;
typedef const char * (*cleri_translate_t)(cleri_t *);
/* public functions */
#ifdef __cplusplus
......@@ -38,6 +39,11 @@ extern "C" {
cleri_parse_t * cleri_parse(cleri_grammar_t * grammar, const char * str);
void cleri_parse_free(cleri_parse_t * pr);
void cleri_parse_expect_start(cleri_parse_t * pr);
int cleri_parse_strn(
char * s,
size_t n,
cleri_parse_t * pr,
cleri_translate_t * translate);
#ifdef __cplusplus
}
......
......@@ -11,7 +11,6 @@
*/
#include <cleri/cleri.h>
#include <stdlib.h>
#include <assert.h>
static cleri_t end_of_statement = {
.gid=0,
......
......@@ -12,7 +12,6 @@
*/
#include <cleri/expecting.h>
#include <stdlib.h>
#include <assert.h>
static cleri_exp_modes_t * EXPECTING_modes_new(const char * str);
static void EXPECTING_empty(cleri_expecting_t * expecting);
......
......@@ -28,6 +28,7 @@ cleri_grammar_t * cleri_grammar(cleri_t * start, const char * re_keywords)
const char * re_kw = (re_keywords == NULL) ?
CLERI_DEFAULT_RE_KEYWORDS : re_keywords;
/* re_keywords should start with a ^ */
assert (re_kw[0] == '^');
if (start == NULL)
......
......@@ -13,9 +13,9 @@
#include <cleri/expecting.h>
#include <stdlib.h>
static void OPTIONAL_free(cleri_t * cl_object);
static void optional__free(cleri_t * cl_object);
static cleri_node_t * OPTIONAL_parse(
static cleri_node_t * optional__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......@@ -34,8 +34,8 @@ cleri_t * cleri_optional(uint32_t gid, cleri_t * cl_obj)
cleri_t * cl_object = cleri_new(
gid,
CLERI_TP_OPTIONAL,
&OPTIONAL_free,
&OPTIONAL_parse);
&optional__free,
&optional__parse);
if (cl_object == NULL)
{
......@@ -61,7 +61,7 @@ cleri_t * cleri_optional(uint32_t gid, cleri_t * cl_obj)
/*
* Destroy optional object.
*/
static void OPTIONAL_free(cleri_t * cl_object)
static void optional__free(cleri_t * cl_object)
{
cleri_free(cl_object->via.optional->cl_obj);
free(cl_object->via.optional);
......@@ -70,7 +70,7 @@ static void OPTIONAL_free(cleri_t * cl_object)
/*
* Returns a node or NULL. In case of an error pr->is_valid is set to -1.
*/
static cleri_node_t * OPTIONAL_parse(
static cleri_node_t * optional__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......
......@@ -22,6 +22,7 @@
*/
cleri_parse_t * cleri_parse(cleri_grammar_t * grammar, const char * str)
{
cleri_node_t * nd;
cleri_parse_t * pr;
const char * end;
const char * test;
......@@ -52,7 +53,7 @@ cleri_parse_t * cleri_parse(cleri_grammar_t * grammar, const char * str)
pr->match_data = grammar->match_data;
/* do the actual parsing */
cleri__parse_walk(
nd = cleri__parse_walk(
pr,
pr->tree,
grammar->start,
......@@ -66,6 +67,8 @@ cleri_parse_t * cleri_parse(cleri_grammar_t * grammar, const char * str)
return NULL;
}
pr->is_valid = nd != NULL;
/* process the parse result */
end = pr->tree->str + pr->tree->len;
......@@ -75,11 +78,11 @@ cleri_parse_t * cleri_parse(cleri_grammar_t * grammar, const char * str)
if (!isspace(*test))
{
at_end = false;
pr->is_valid = false;
break;
}
}
pr->is_valid = at_end;
pr->pos = (pr->is_valid) ?
pr->tree->len : (size_t) (pr->expecting->str - pr->str);
......@@ -128,6 +131,79 @@ void cleri_parse_expect_start(cleri_parse_t * pr)
pr->expect = pr->expecting->required;
}
/*
* Print parse result to a string. The return value is equal to the snprintf
* function. Argument `translate_cb` maybe NULL or a function which may return
* a string based on the `cleri_t`. This allows for returning nice strings for
* regular expressions. The function may return NULL if it has no translation
* for the given regular expression.
*/
int cleri_parse_strn(
char * s,
size_t n,
cleri_parse_t * pr,
cleri_translate_t * translate)
{
int rc, count = 0;
size_t i, m;
cleri_t * o;
const char * expect;
const char * template;
if (pr->is_valid)
{
return snprintf(s, n, "successfully parsed");
}
/* make sure expecting is at start */
cleri_parse_expect_start(pr);
rc = snprintf(s, n, "error at position %zd, expecting ", pr->pos);
if (rc < 0)
{
return rc;
}
i = rc;
while (pr->expect)
{
o = pr->expect->cl_obj;
if (!translate || !(expect = (*translate)(o))) switch(o->tp)
{
case CLERI_TP_END_OF_STATEMENT:
expect = "end_of_statement";
break;
case CLERI_TP_KEYWORD:
expect = o->via.keyword->keyword;
break;
case CLERI_TP_TOKENS:
expect = o->via.tokens->spaced;
break;
case CLERI_TP_TOKEN:
expect = o->via.token->token;
break;
default:
pr->expect = pr->expect->next;
continue;
}
/* make sure len is not greater than the maximum size */
m = (i < n) ? n - i : 0;
/* we use count = 0 to print the first one, then for the others
* a comma prefix and the last with -or-
*/
template = !count++ ? "%s" : pr->expect->next ? ", %s" : " or %s";
rc = snprintf(s + i, m, template, expect);
if (rc < 0)
{
return rc;
}
i += rc;
pr->expect = pr->expect->next;
}
return (int) i;
}
/*
* Walk a parser object.
* (recursive function, called from each parse_object function)
......
......@@ -13,9 +13,9 @@
#include <stdlib.h>
#include <assert.h>
static void REPEAT_free(cleri_t * cl_object);
static void repeat__free(cleri_t * cl_object);
static cleri_node_t * REPEAT_parse(
static cleri_node_t * repeat__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......@@ -26,7 +26,7 @@ static cleri_node_t * REPEAT_parse(
*
* cl_ob : object to repeat
* min : should be equal to or higher then 0.
* max : should be equal to or higher then 0 but when 0 it means
* max : should be equal to or higher then min, or 0 which means
* unlimited.
*/
cleri_t * cleri_repeat(uint32_t gid, cleri_t * cl_obj, size_t min, size_t max)
......@@ -41,8 +41,8 @@ cleri_t * cleri_repeat(uint32_t gid, cleri_t * cl_obj, size_t min, size_t max)
cleri_t * cl_object = cleri_new(
gid,
CLERI_TP_REPEAT,
&REPEAT_free,
&REPEAT_parse);
&repeat__free,
&repeat__parse);
if (cl_object == NULL)
{
......@@ -70,7 +70,7 @@ cleri_t * cleri_repeat(uint32_t gid, cleri_t * cl_obj, size_t min, size_t max)
/*
* Destroy repeat object.
*/
static void REPEAT_free(cleri_t * cl_object)
static void repeat__free(cleri_t * cl_object)
{
cleri_free(cl_object->via.repeat->cl_obj);
free(cl_object->via.repeat);
......@@ -79,7 +79,7 @@ static void REPEAT_free(cleri_t * cl_object)
/*
* Returns a node or NULL. In case of an error pr->is_valid is set to -1.
*/
static cleri_node_t * REPEAT_parse(
static cleri_node_t * repeat__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......
......@@ -13,13 +13,13 @@
#include <cleri/rule.h>
#include <stdlib.h>
static void RULE_free(cleri_t * cl_object);
static cleri_node_t * RULE_parse(
static void rule__free(cleri_t * cl_object);
static cleri_node_t * rule__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
cleri_rule_store_t * rule);
static void RULE_tested_free(cleri_rule_tested_t * tested);
static void rule__tested_free(cleri_rule_tested_t * tested);
/*
* Returns NULL in case an error has occurred.
......@@ -34,8 +34,8 @@ cleri_t * cleri__rule(uint32_t gid, cleri_t * cl_obj)
cleri_t * cl_object = cleri_new(
gid,
CLERI_TP_RULE,
&RULE_free,
&RULE_parse);
&rule__free,
&rule__parse);
if (cl_object != NULL)
{
......@@ -106,7 +106,7 @@ cleri_rule_test_t cleri__rule_init(
return CLERI_RULE_TRUE;
}
static void RULE_free(cleri_t * cl_object)
static void rule__free(cleri_t * cl_object)
{
cleri_free(cl_object->via.rule->cl_obj);
free(cl_object->via.rule);
......@@ -115,7 +115,7 @@ static void RULE_free(cleri_t * cl_object)
/*
* Returns a node or NULL. In case of an error pr->is_valid is set to -1.
*/
static cleri_node_t * RULE_parse(
static cleri_node_t * rule__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......@@ -173,7 +173,7 @@ static cleri_node_t * RULE_parse(
}
/* cleanup rule */
RULE_tested_free(nrule.tested);
rule__tested_free(nrule.tested);
return node;
}
......@@ -181,7 +181,7 @@ static cleri_node_t * RULE_parse(
/*
* Cleanup rule tested
*/
static void RULE_tested_free(cleri_rule_tested_t * tested)
static void rule__tested_free(cleri_rule_tested_t * tested)
{
cleri_rule_tested_t * next;
while (tested != NULL)
......
......@@ -14,8 +14,8 @@
#include <stdlib.h>
#include <stdio.h>
static void SEQUENCE_free(cleri_t * cl_object);
static cleri_node_t * SEQUENCE_parse(
static void sequence__free(cleri_t * cl_object);
static cleri_node_t * sequence__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......@@ -30,8 +30,8 @@ cleri_t * cleri_sequence(uint32_t gid, size_t len, ...)
cleri_t * cl_object = cleri_new(
gid,
CLERI_TP_SEQUENCE,
&SEQUENCE_free,
&SEQUENCE_parse);
&sequence__free,
&sequence__parse);
if (cl_object == NULL)
{
......@@ -76,7 +76,7 @@ cleri_t * cleri_sequence(uint32_t gid, size_t len, ...)
/*
* Destroy sequence object.
*/
static void SEQUENCE_free(cleri_t * cl_object)
static void sequence__free(cleri_t * cl_object)
{
cleri__olist_free(cl_object->via.sequence->olist);
free(cl_object->via.sequence);
......@@ -85,7 +85,7 @@ static void SEQUENCE_free(cleri_t * cl_object)
/*
* Returns a node or NULL. In case of an error pr->is_valid is set to -1.
*/
static cleri_node_t * SEQUENCE_parse(
static cleri_node_t * sequence__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......
......@@ -16,8 +16,8 @@
#include <stdlib.h>
#include <string.h>
static void TOKEN_free(cleri_t * cl_object);
static cleri_node_t * TOKEN_parse(
static void token__free(cleri_t * cl_object);
static cleri_node_t * token__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......@@ -31,8 +31,8 @@ cleri_t * cleri_token(uint32_t gid, const char * token)
cleri_t * cl_object = cleri_new(
gid,
CLERI_TP_TOKEN,
&TOKEN_free,
&TOKEN_parse);
&token__free,
&token__parse);
if (cl_object == NULL)
......@@ -58,7 +58,7 @@ cleri_t * cleri_token(uint32_t gid, const char * token)
/*
* Destroy token object.
*/
static void TOKEN_free(cleri_t * cl_object)
static void token__free(cleri_t * cl_object)
{
free(cl_object->via.token);
}
......@@ -66,7 +66,7 @@ static void TOKEN_free(cleri_t * cl_object)
/*
* Returns a node or NULL. In case of an error pr->is_valid is set to -1.
*/
static cleri_node_t * TOKEN_parse(
static cleri_node_t * token__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......
......@@ -17,17 +17,18 @@
#include <ctype.h>
#include <assert.h>
static void TOKENS_free(cleri_t * cl_object);
static cleri_node_t * TOKENS_parse(
static void tokens__free(cleri_t * cl_object);
static cleri_node_t * tokens__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
cleri_rule_store_t * rule);
static int TOKENS_list_add(
static int tokens__list_add(
cleri_tlist_t ** tlist,
const char * token,
size_t len);
static void TOKENS_list_free(cleri_tlist_t * tlist);
static void tokens__list_str(cleri_tlist_t * tlist, char * s);
static void tokens__list_free(cleri_tlist_t * tlist);
/*
* Returns NULL in case an error has occurred.
......@@ -41,16 +42,15 @@ cleri_t * cleri_tokens(uint32_t gid, const char * tokens)
cl_object = cleri_new(
gid,
CLERI_TP_TOKENS,
&TOKENS_free,
&TOKENS_parse);
&tokens__free,
&tokens__parse);
if (cl_object == NULL)
{
return NULL;
}
cl_object->via.tokens =
(cleri_tokens_t *) malloc(sizeof(cleri_tokens_t));
cl_object->via.tokens = malloc(sizeof(cleri_tokens_t));
if (cl_object->via.tokens == NULL)
{
......@@ -61,11 +61,11 @@ cleri_t * cleri_tokens(uint32_t gid, const char * tokens)
/* copy the sting twice, first one we set spaces to 0...*/
cl_object->via.tokens->tokens = strdup(tokens);
/* ...and this one we keep for showing the original */
cl_object->via.tokens->spaced = strdup(tokens);
/* ...and this one we keep for showing a spaced version */
cl_object->via.tokens->spaced = malloc(strlen(tokens) + 1);
cl_object->via.tokens->tlist = malloc(sizeof(cleri_tlist_t));
cl_object->via.tokens->tlist =
(cleri_tlist_t *) malloc(sizeof(cleri_tlist_t));
if ( cl_object->via.tokens->tokens == NULL ||
cl_object->via.tokens->spaced == NULL ||
......@@ -87,7 +87,7 @@ cleri_t * cleri_tokens(uint32_t gid, const char * tokens)
{
if (len)
{
if (TOKENS_list_add(
if (tokens__list_add(
&cl_object->via.tokens->tlist,
pt - len,
len))
......@@ -110,10 +110,12 @@ cleri_t * cleri_tokens(uint32_t gid, const char * tokens)
}
}
#ifdef DEBUG
/* check for empty token list */
/* tlist->token is never empty */
assert (cl_object->via.tokens->tlist->token != NULL);
#endif
tokens__list_str(
cl_object->via.tokens->tlist,
cl_object->via.tokens->spaced);
return cl_object;
}
......@@ -121,9 +123,9 @@ cleri_t * cleri_tokens(uint32_t gid, const char * tokens)
/*
* Destroy token object.
*/
static void TOKENS_free(cleri_t * cl_object)
static void tokens__free(cleri_t * cl_object)
{
TOKENS_list_free(cl_object->via.tokens->tlist);
tokens__list_free(cl_object->via.tokens->tlist);
free(cl_object->via.tokens->tokens);
free(cl_object->via.tokens->spaced);
free(cl_object->via.tokens);
......@@ -132,7 +134,7 @@ static void TOKENS_free(cleri_t * cl_object)
/*
* Returns a node or NULL. In case of an error pr->is_valid is set to -1.
*/
static cleri_node_t * TOKENS_parse(
static cleri_node_t * tokens__parse(
cleri_parse_t * pr,
cleri_node_t * parent,
cleri_t * cl_obj,
......@@ -180,7 +182,7 @@ static cleri_node_t * TOKENS_parse(
* The token will be placed in the list based on the length. Thus the token
* list remains ordered on length. (largest first)
*/
static int TOKENS_list_add(
static int tokens__list_add(
cleri_tlist_t ** tlist,
const char * token,
size_t len)
......@@ -202,7 +204,6 @@ static int TOKENS_list_add(
tmp->len = len;
tmp->token = token;
while (current != NULL && len <= current->len)
{
prev = current;
......@@ -223,10 +224,31 @@ static int TOKENS_list_add(
return 0;
}
/*
* Returns 0 if successful and -1 in case an error occurred.
* (the token list remains unchanged in case of an error)
*
* The token will be placed in the list based on the length. Thus the token
* list remains ordered on length. (largest first)
*/
static int tokens__list_str(cleri_tlist_t * tlist, char * s)
{
assert (tlist->token != NULL);
cleri_tlist_t * next;
sprintf(s, "%s", tlist->token);
while (tlist != NULL)
{
next = tlist->next;
tlist->token
tlist = next;
}
}
/*
* Destroy token list. (NULL can be parsed tlist argument)
*/
static void TOKENS_list_free(cleri_tlist_t * tlist)
static void tokens__list_free(cleri_tlist_t * tlist)
{
cleri_tlist_t * next;
while (tlist != NULL)
......
#include <cleri/parse.h>
#include <stdlib.h>
static char * parse_str(
cleri_parse_t * pr,
cleri_translate_t * translate) __attribute__((unused));
static char * parse_str(cleri_parse_t * pr, cleri_translate_t * translate)
{
size_t sz;
int i = cleri_parse_strn(NULL, 0, pr, translate);
if (i < 0)
{
return NULL;
}
sz = i + 1;
char * s = malloc(sz);
if (s)
{
i = cleri_parse_strn(s, sz, pr, translate);
if (i < 0 || i >= (int) sz)
{
free(s);
return NULL;
}
}
return s;
}
#define _assert_is_valid(__grammar, __str) \
{ \
cleri_parse_t * __pr = cleri_parse(__grammar, __str); \
_assert (__pr); \
_assert (__pr->is_valid); \
cleri_parse_free(__pr); \
}
#define _assert_is_not_valid(__grammar, __str) \
{ \
cleri_parse_t * __pr = cleri_parse(__grammar, __str); \
_assert (__pr); \
_assert (!__pr->is_valid); \
cleri_parse_free(__pr); \
}
#define _assert_parse_str(__grammar, __str, __expect, __translate) \
{ \
cleri_parse_t * __pr = cleri_parse(__grammar, __str); \
_assert (__pr); \
char * __s = parse_str(__pr, __translate); \
_assert (__s); \
if (strcmp(__s, __expect) != 0) printf("\n\ngot: `%s`\n", __s); \
_assert (strcmp(__s, __expect) == 0); \
free(__s); \
cleri_parse_free(__pr); \
}
#ifndef CLERI_TEST_H_
#define CLERI_TEST_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#define TEST_OK 0
#define TEST_FAILED 1
#define TEST_MSG_OK "....\x1B[32mOK\x1B[0m"
#define TEST_MSG_FAILED "\x1B[31mFAILED\x1B[0m"
static struct timeval start;
static struct timeval end;
static int status = TEST_OK;
static int count = 0;
const char * padding =
".............................."
"..............................";
static void test_start(char * test_name)
{
count = 0;
int padlen = 60 - strlen(test_name);
printf("Testing %s%*.*s", test_name, padlen, padlen, padding);
gettimeofday(&start, 0);
}
static int test_end(void)
{
gettimeofday(&end, 0);
float t = (end.tv_sec - start.tv_sec) * 1000.0f +
(end.tv_usec - start.tv_usec) / 1000.0f;
printf("%s (%.3f ms)\n",
(status == TEST_OK) ? TEST_MSG_OK : TEST_MSG_FAILED,
t);
return status;
}
#define _assert(e) (void)((e)?count++:(status = TEST_FAILED) && printf("\n\x1B[33mAssertion failed (%s:%d):\x1B[0m %s\n\n", __FILE__, __LINE__, #e))
#endif /* CLERI_TEST_H_ */
\ No newline at end of file
#!/bin/bash
RET=0
echo -n "Test using valgrind for memory errors and leaks: "
if [[ "$NOMEMTEST" -ne "1" ]] && hash valgrind 2>/dev/null; then
NOMEMTEST=0;
echo -e "\x1B[32menabled\x1B[0m";
else
NOMEMTEST=1;
echo -e "\x1B[33mdisabled\x1B[0m";
fi
run () {
if [ ! -f $1/sources ]; then
return;
fi
C_SRC=$(cat $1/sources)
SOURCE=$1/$1.c
OUT=$1.out
rm "$OUT" 2> /dev/null
gcc -I"../inc" -O0 -g3 -Wall -Wextra -Winline -std=gnu89 $SOURCE $C_SRC -lm -lpcre2-8 -o "$OUT"
if [[ "$NOMEMTEST" -ne "1" ]]; then
valgrind --tool=memcheck --error-exitcode=1 --leak-check=full -q ./$OUT
else
./$OUT
fi
rc=$?; if [[ $rc != 0 ]]; then RET=$((RET+1)); fi
rm "$OUT" 2> /dev/null
rm -r "$OUT.dSYM" 2> /dev/null
}
if [ $# -eq 0 ]; then
for d in test_*/ ; do
run "${d%?}"
done
else
name=`echo $1 | sed 's/\(test_\)\?\(.*\?\)$/\2/g' | sed 's/\(.*\)\/$/\1/g'`
run "test_$name"
fi
exit $RET
\ No newline at end of file
../src/children.c
../src/cleri.c
../src/expecting.c
../src/grammar.c
../src/kwcache.c
../src/node.c
../src/olist.c
../src/parse.c
../src/keyword.c
../src/sequence.c
../src/choice.c
#include "../test.h"
#include "../helpers.h"
static int test_choice_most_greedy(void)
{
test_start("choice (most_greedy)");
cleri_grammar_t * grammar;
cleri_t * k_hi, * k_iris, * seq, * choice;