summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2025-10-27 10:52:01 -0500
committersanine <sanine.not@pm.me>2025-10-27 10:52:01 -0500
commit8f63364c2b1ec54e104c189db43805917800b789 (patch)
tree8329eb5a9955667f1aa8d2e22110062d841379c7
parent11f8fd4418a068c76fc7c810967a6bf285ee74b6 (diff)
implement basic grammatical expansionc-version
-rw-r--r--.gitignore1
-rw-r--r--Makefile3
-rw-r--r--expression.c105
-rw-r--r--expression.h22
-rw-r--r--expression.test.c61
-rw-r--r--lily-test.h741
6 files changed, 926 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore
index 07c0265..34125b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*.swp
*.swo
example
+test
diff --git a/Makefile b/Makefile
index 658bce0..0a02e41 100644
--- a/Makefile
+++ b/Makefile
@@ -1,2 +1,5 @@
example: genalg.h genalg.c genalg.example.c
gcc -g -o example genalg.c genalg.example.c -Wall -Wextra -Wpedantic -Werror -lm
+
+test: expression.h expression.c expression.test.c
+ gcc -g -o test expression.c expression.test.c -Wall -Wextra -Wpedantic -Werror -lm
diff --git a/expression.c b/expression.c
index 11904fc..f4d7b26 100644
--- a/expression.c
+++ b/expression.c
@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include "expression.h"
@@ -65,7 +66,7 @@ int li_prod_rule_add(
}
-li_grammar_t * li_grammar_alloc(size_t num_nonterminal) {
+li_grammar_t * li_grammar_alloc(int num_nonterminal) {
li_grammar_t *grammar = malloc(sizeof(li_grammar_t));
if (grammar == NULL) {
fprintf(stderr, "failed to allocate grammar\n");
@@ -77,18 +78,18 @@ li_grammar_t * li_grammar_alloc(size_t num_nonterminal) {
free(grammar);
return NULL;
}
- for (size_t i=0; i<num_nonterminal; i++) {
+ for (int i=0; i<num_nonterminal; i++) {
grammar->rules[i].num_productions = 0;
grammar->rules[i].head = NULL;
}
- grammar->num_nonterminal = NULL;
+ grammar->num_nonterminal = num_nonterminal;
return grammar;
}
void li_grammar_free(li_grammar_t *grammar) {
if (grammar != NULL) {
- for (size_t i=0; i<grammar->num_nonterminal; i++) {
+ for (int i=0; i<grammar->num_nonterminal; i++) {
li_production_free(grammar->rules[i].head);
}
free(grammar->rules);
@@ -100,7 +101,7 @@ void li_grammar_free(li_grammar_t *grammar) {
int li_grammar_rule_add(
li_grammar_t *grammar, int nonterminal, size_t seq_len, int *seq
) {
- if (nonterminal < 0 || nonterminal >= grammar->num_nonterminal) {
+ if (nonterminal >= grammar->num_nonterminal) {
return 2;
}
return li_prod_rule_add(grammar->rules + nonterminal, seq_len, seq);
@@ -108,3 +109,97 @@ int li_grammar_rule_add(
+int stack_push(int *stack, size_t stack_sz, size_t *top, int value) {
+ *top += 1;
+ if (*top >= stack_sz) {
+ fprintf(stderr, "grammar stack overflow!\n");
+ return 1;
+ }
+ stack[*top] = value;
+ return 0;
+}
+
+
+int stack_pop(int *stack, size_t *top, int *value) {
+ if (*top == ((size_t)-1)) {
+ fprintf(stderr, "grammar stack underflow!\n");
+ return 1;
+ }
+
+ *value = stack[*top];
+ *top -=1;
+ return 0;
+}
+
+
+int expand_production(
+ li_grammar_t *grammar,
+ int *dst, size_t sz, size_t *len,
+ int *stack, size_t stack_sz, size_t *top,
+ int symbol,
+ const uint8_t *randomness, size_t rand_sz
+) {
+ struct li_production_rule_t rule = grammar->rules[symbol];
+ struct li_production_t *prod;
+ if (rule.num_productions == 0) {
+ // empty rule, nothing to do
+ return 0;
+ } else if (rule.num_productions == 1) {
+ // pick only production
+ prod = rule.head;
+ } else {
+ // use randomness to select production
+ int selection = randomness[0] % rule.num_productions;
+ randomness += 1;
+ rand_sz -= 1;
+ prod = rule.head;
+ for (int i=0; i<selection; i++) {
+ prod = prod->next;
+ }
+ }
+
+ // push production to stack
+ for (int i=prod->seq_len-1; i>=0; i--) {
+ if (stack_push(stack, stack_sz, top, prod->seq[i]) != 0) {
+ return 1;
+ }
+ }
+
+ // pop production from stack, recursing on nonterminals
+ while (*top != ((size_t)-1)) {
+ int symbol;
+ if (stack_pop(stack, top, &symbol) != 0) {
+ return 1;
+ }
+ if (symbol < grammar->num_nonterminal) {
+ int err = expand_production(grammar, dst, sz, len, stack, stack_sz, top, symbol, randomness, rand_sz);
+ if (err != 0) {
+ return err;
+ }
+ } else {
+ if (*len > sz) {
+ fprintf(stderr, "exceeded maximum output buffer length\n");
+ return 1;
+ }
+ dst[*len] = symbol;
+ *len += 1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+int li_grammar_rule_expand(
+ li_grammar_t *grammar,
+ int *dst, size_t *len,
+ int *stack, size_t stack_sz,
+ int symbol,
+ const uint8_t *randomness, size_t rand_sz
+) {
+ size_t sz = *len;
+ *len = 0;
+ size_t top = -1;
+ return expand_production(grammar, dst, sz, len, stack, stack_sz, &top, symbol, randomness, rand_sz);
+}
diff --git a/expression.h b/expression.h
index 36a0e02..e288a77 100644
--- a/expression.h
+++ b/expression.h
@@ -1,6 +1,8 @@
#ifndef LICHEN_EXPRESSION_H
#define LICHEN_EXPRESSION_H
+#include <stdint.h>
+
/****************************************************************
*
* a grammar is built on both nonterminals and terminals. both are
@@ -32,11 +34,27 @@ int li_prod_rule_add(struct li_production_rule_t *rule, size_t seq_len, int *seq
typedef struct li_grammar_t {
struct li_production_rule_t *rules;
- size_t num_nonterminal;
+ int num_nonterminal;
} li_grammar_t;
-li_rammar_t * li_grammar_alloc(size_t num_nonterminal);
+li_grammar_t * li_grammar_alloc(int num_nonterminal);
void li_grammar_free(li_grammar_t *grammar);
int li_grammar_rule_add(li_grammar_t *grammar, int nonterminal, size_t seq_len, int *seq);
+// expand a symbol into only nonterminals
+// dst is a buffer to place the expansion into, and len should initially contain the size of the buffer
+// stack is a buffer used internally to expand the rule, and stack_sz is the size of the stack
+// symbol is the symbol to start with
+// randomness is a sequence of random values, from any source, and rand_sz is the length of the sequence
+// if successful, this function will return 0 and dst will contain the expanded sequence, with len containing
+// the length of the actual expansion
+// if unsuccessful, this function will return nonzero. dst, len, and stack may be modified in unpredictable ways.
+int li_grammar_rule_expand(
+ li_grammar_t *grammar,
+ int *dst, size_t *len,
+ int *stack, size_t stack_sz,
+ int symbol,
+ const uint8_t *randomness, size_t rand_sz
+);
+
#endif
diff --git a/expression.test.c b/expression.test.c
new file mode 100644
index 0000000..234347d
--- /dev/null
+++ b/expression.test.c
@@ -0,0 +1,61 @@
+#define LILY_IMPLEMENTATION
+#include "lily-test.h"
+#include "expression.h"
+
+
+LILY_FILE_BEGIN(expression_suite)
+
+
+enum simple_symbol_t {
+ START,
+ SIMPLE_NUM_NONTERMINALS,
+ A, B, C,
+};
+
+LILY_TEST("expand basic expression") {
+ li_grammar_t *grammar = li_grammar_alloc(SIMPLE_NUM_NONTERMINALS);
+ REQUIRE_NEQ(grammar, NULL, "%p");
+
+ int err;
+
+ int rule0[] = { A, START, B };
+ err = li_grammar_rule_add(grammar, START, 3, rule0);
+ CHECK_EQ(err, 0, "%d");
+ int rule1[] = { C };
+ err = li_grammar_rule_add(grammar, START, 1, rule1);
+ CHECK_EQ(err, 0, "%d");
+
+ int buf[32]; size_t bufsz = 32; int stack[32];
+ uint8_t rand[] = { 1, 1, 1, 0 };
+
+ err = li_grammar_rule_expand(grammar, buf, &bufsz, stack, 32, START, rand, 4);
+
+ REQUIRE_EQ(err, 0, "%d");
+ CHECK_EQ(bufsz, 7, "%lu");
+
+ CHECK_EQ(buf[0], A, "%d");
+ CHECK_EQ(buf[1], A, "%d");
+ CHECK_EQ(buf[2], A, "%d");
+
+ CHECK_EQ(buf[3], C, "%d");
+
+ CHECK_EQ(buf[4], B, "%d");
+ CHECK_EQ(buf[5], B, "%d");
+ CHECK_EQ(buf[6], B, "%d");
+
+ li_grammar_free(grammar);
+}
+#include LILY_PUSH_TEST()
+
+
+#define LILY_FILE_END
+#include LILY_REGISTER_TESTS()
+
+
+
+int main() {
+ lily_begin();
+ expression_suite();
+ lily_finish();
+ return 0;
+}
diff --git a/lily-test.h b/lily-test.h
new file mode 100644
index 0000000..8c1ad13
--- /dev/null
+++ b/lily-test.h
@@ -0,0 +1,741 @@
+/****************************************************************
+ *
+ * ======== lily-test ========
+ *
+ * a midsize C unit testing library
+ * copyright (c) 2022 kate swanson (sanine)
+ *
+ * This is anti-capitalist software, released for free use by individuals and
+ * organizations that do not operate by capitalist principles.
+ *
+ * Permission is hereby granted, free of charge, to any person or
+ * organization (the "User") obtaining a copy of this software and associated
+ * documentation files (the "Software"), to use, copy, modify, merge,
+ * distribute, and/or sell copies of the Software, subject to the following
+ * conditions:
+ *
+ * 1. The above copyright notice and this permission notice shall be included
+ * in all copies or modified versions of the Software.
+ *
+ * 2. The User is one of the following:
+ * a. An individual person, laboring for themselves
+ * b. A non-profit organization
+ * c. An educational institution
+ * d. An organization that seeks shared profit for all of its members, and
+ * allows non-members to set the cost of their labor
+ *
+ * 3. If the User is an organization with owners, then all owners are workers
+ * and all workers are owners with equal equity and/or equal vote.
+ *
+ * 4. If the User is an organization, then the User is not law enforcement or
+ * military, or working for or under either.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF
+ * ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * https://anticapitalist.software/
+ * https://sanine.net
+ *
+ ****************************************************************/
+
+
+#ifndef LILY_TEST_H
+#define LILY_TEST_H
+
+#define LILY_VERSION_MAJOR 2
+#define LILY_VERSION_MINOR 0
+#define LILY_VERSION_PATCH 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define STR_IMP(x) #x
+#define STR(x) STR_IMP(x)
+/* define SOURCE_PATH_SIZE to strip away the
+ leading parts of the full compilation path */
+#ifndef SOURCE_PATH_SIZE
+#define LILY_LOCATION (__FILE__ ":" STR(__LINE__))
+#else
+#define LILY_LOCATION ((__FILE__ ":" STR(__LINE__)) + SOURCE_PATH_SIZE)
+#endif
+
+#define LILY_NULLSAFE(x) (x==NULL ? "(nil)" : x)
+int lily_streq(const char *str1, const char *str2);
+
+
+/* self-location macro */
+#ifndef LILY_TEST_H_LOCATION
+#define LILY_TEST_H_LOCATION "lily-test.h"
+#endif
+
+
+/* counter macros */
+#define LILY_HEX_(x) 0x##x
+#define LILY_HEX(x) LILY_HEX_(x)
+#define LILY_COUNTER_POS_0 0
+#define LILY_COUNTER_POS_1 0
+#define LILY_COUNTER_POS_2 0
+#define LILY_COUNTER__(a, b, c) 0x##a##b##c
+#define LILY_COUNTER_(a, b, c) LILY_COUNTER__(a, b, c)
+#define LILY_COUNTER LILY_COUNTER_(LILY_COUNTER_POS_2, LILY_COUNTER_POS_1, LILY_COUNTER_POS_0)
+
+/* automatic registration macros */
+typedef struct lily_ll_node_t {
+ struct lily_ll_node_t *next;
+ void (*f)();
+} lily_ll_node_t;
+#define LILY_FILE_BEGIN(unique_id) \
+static lily_ll_node_t lily_ll_last = { NULL, NULL }; \
+static void file_runner(); \
+void (*unique_id)() = file_runner;
+
+#define LILY_LIST_HEAD lily_ll_last
+#define LILY_LIST_NEXT lily_ll_last
+#define LILY_PUSH_TEST() LILY_TEST_H_LOCATION
+#define LILY_REGISTER_TESTS() LILY_TEST_H_LOCATION
+
+#define LILY_CAT_(x, y) x##y
+#define LILY_CAT(x, y) LILY_CAT_(x, y)
+#define LILY_ANON(x) LILY_CAT(x, LILY_COUNTER)
+#define LILY_ANON_WRAP LILY_ANON(LILY_WRAP_)
+#define LILY_ANON_FUNC LILY_ANON(LILY_FUNCTION_)
+#define LILY_ANON_NODE LILY_ANON(LILY_NODE_)
+#define LILY_ANON_DESC LILY_ANON(LILY_DESCRIPTION_)
+
+#define LILY_TEST_(wrap, f, desc_str, description) \
+static void f(); \
+static void wrap() \
+{ \
+ lily_g.test_name = description; \
+ f(); \
+} \
+static void f()
+
+#define LILY_TEST(description) LILY_TEST_(LILY_ANON_WRAP, LILY_ANON_FUNC, LILY_ANON_DESC, description)
+
+/* assertions */
+typedef struct lily_test_msg_t {
+ struct lily_test_msg_t *next;
+ char *msg;
+ const char *location;
+} lily_test_msg_t;
+void lily_msg_destroy(lily_test_msg_t *m);
+
+void lily_info(const char *location, const char *fmt, ...);
+#define LILY_INFO_(location, ...) \
+ lily_info(location, __VA_ARGS__)
+#define LILY_INFO(...) LILY_INFO_(LILY_LOCATION, __VA_ARGS__)
+#define INFO(...) LILY_INFO(__VA_ARGS__)
+
+void lily_check(int x, const char *location, const char *fmt, ...);
+#define LILY_CHECK_(str, x, location) \
+ lily_check(x, location, "CHECK failed: %s", str)
+#define LILY_CHECK(x) LILY_CHECK_(STR(x), x, LILY_LOCATION)
+#define CHECK(x) LILY_CHECK(x)
+
+#define LILY_CHECK_CMP_(x, y, xstr, ystr, cmp, cmpstr, fmt) \
+ lily_check((x) cmp (y), LILY_LOCATION, \
+ "CHECK failed: %s " cmpstr " %s\n %s = " fmt "\n %s = " fmt,\
+ xstr, ystr, xstr, x, ystr, y)
+#define LILY_CHECK_CMP(x, cmp, y, fmt) LILY_CHECK_CMP_(x, y, #x, #y, cmp, #cmp, fmt)
+
+/* check comparision assertions */
+#define CHECK_EQ(x, y, fmt) LILY_CHECK_CMP(x, ==, y, fmt)
+#define CHECK_NEQ(x, y, fmt) LILY_CHECK_CMP(x, !=, y, fmt)
+#define CHECK_LT(x, y, fmt) LILY_CHECK_CMP(x, <, y, fmt)
+#define CHECK_LE(x, y, fmt) LILY_CHECK_CMP(x, <=, y, fmt)
+#define CHECK_GT(x, y, fmt) LILY_CHECK_CMP(x, >, y, fmt)
+#define CHECK_GE(x, y, fmt) LILY_CHECK_CMP(x, >=, y, fmt)
+
+#define LILY_CHECK_EQF(x, y, xstr, ystr, fmt) \
+ lily_check(fabs(x-y) < lily_g.epsilon, LILY_LOCATION, \
+ "CHECK failed: %s == %s\n %s = " fmt "\n %s = " fmt "\n epsilon: %f", \
+ xstr, ystr, xstr, x, ystr, y, lily_g.epsilon)
+#define CHECK_EQF(x, y, fmt) LILY_CHECK_EQF(x, y, #x, #y, fmt)
+
+#define LILY_CHECK_EQS(x, y, xstr, ystr) \
+ lily_check(lily_streq(x, y), LILY_LOCATION, \
+ "CHECK failed: %s == %s\n %s = \"%s\"\n %s = \"%s\"", \
+ xstr, ystr, xstr, x, ystr, y)
+#define CHECK_EQS(x, y) LILY_CHECK_EQS(x, y, #x, #y)
+
+
+void lily_require(int x, const char *location, const char *fmt, ...);
+#define LILY_REQUIRE_(str, x, location) \
+ lily_require(x, location, "REQUIRE failed: %s", str)
+#define LILY_REQUIRE(x) LILY_REQUIRE_(STR(x), x, LILY_LOCATION)
+#define REQUIRE(x) LILY_REQUIRE(x)
+
+#define LILY_REQUIRE_CMP_(x, y, xstr, ystr, cmp, cmpstr, fmt) \
+ lily_require((x) cmp (y), LILY_LOCATION, \
+ "REQUIRE failed: %s " cmpstr " %s\n %s = " fmt "\n %s = " fmt,\
+ xstr, ystr, xstr, x, ystr, y)
+#define LILY_REQUIRE_CMP(x, cmp, y, fmt) LILY_REQUIRE_CMP_(x, y, #x, #y, cmp, #cmp, fmt)
+
+/* require comparison assertions */
+#define REQUIRE_EQ(x, y, fmt) LILY_REQUIRE_CMP(x, ==, y, fmt)
+#define REQUIRE_NEQ(x, y, fmt) LILY_REQUIRE_CMP(x, !=, y, fmt)
+#define REQUIRE_LT(x, y, fmt) LILY_REQUIRE_CMP(x, <, y, fmt)
+#define REQUIRE_LE(x, y, fmt) LILY_REQUIRE_CMP(x, <=, y, fmt)
+#define REQUIRE_GT(x, y, fmt) LILY_REQUIRE_CMP(x, >, y, fmt)
+#define REQUIRE_GE(x, y, fmt) LILY_REQUIRE_CMP(x, >=, y, fmt)
+
+#define LILY_REQUIRE_EQF(x, y, xstr, ystr, fmt) \
+ lily_require(fabs(x-y) < lily_g.epsilon, LILY_LOCATION, \
+ "REQUIRE failed: %s == %s\n %s = " fmt "\n %s = " fmt "\n epsilon: %f", \
+ xstr, ystr, xstr, x, ystr, y, lily_g.epsilon)
+#define REQUIRE_EQF(x, y, fmt) LILY_REQUIRE_EQF(x, y, #x, #y, fmt)
+
+#define LILY_REQUIRE_EQS(x, y, xstr, ystr) \
+ lily_require(lily_streq(x, y), LILY_LOCATION, \
+ "REQUIRE failed: %s == %s\n %s = \"%s\"\n %s = \"%s\"", \
+ xstr, ystr, xstr, x, ystr, y)
+#define REQUIRE_EQS(x, y) LILY_REQUIRE_EQS(x, y, #x, #y)
+
+
+void lily_begin();
+void lily_finish();
+void lily_run_test(void (*test)());
+void lily_set_epsilon(double epsilon);
+
+
+extern struct lily_global_t {
+ int failed;
+ jmp_buf env;
+ const char *test_name;
+ lily_test_msg_t HEAD;
+ lily_test_msg_t *TAIL;
+ int n_assertions;
+ int n_assertions_failed;
+ int n_tests;
+ int n_tests_failed;
+ double epsilon;
+} lily_g;
+
+#ifdef LILY_IMPLEMENTATION
+struct lily_global_t lily_g;
+
+lily_test_msg_t *lily_msg_create(const char *location, const char *fmt, va_list args)
+{
+ lily_test_msg_t *m = (lily_test_msg_t*) malloc(sizeof(lily_test_msg_t));
+ if (m == NULL) {
+ return NULL;
+ }
+ m->next = NULL;
+ m->location = location;
+
+ va_list args_len;
+ va_copy(args_len, args);
+ size_t len = vsnprintf(NULL, 0, fmt, args_len) + 1;
+ va_end(args_len);
+
+ m->msg = (char*) malloc(len * sizeof(char));
+ if (m->msg == NULL) {
+ free(m);
+ return NULL;
+ }
+ vsnprintf(m->msg, len, fmt, args);
+
+ return m;
+}
+
+void lily_msg_destroy(lily_test_msg_t *m)
+{
+ if (m == NULL) {
+ /* ignore */
+ return;
+ }
+
+ if (m->next != NULL) {
+ // destroy the whole chain
+ lily_msg_destroy(m->next);
+ }
+
+ free(m->msg);
+ free(m);
+}
+
+
+void lily_run_test(void (*test)())
+{
+ lily_g.n_tests += 1;
+ lily_g.HEAD.next = NULL;
+ lily_g.TAIL = &(lily_g.HEAD);
+ lily_g.failed = 0;
+ int jumped = setjmp(lily_g.env);
+
+ if (!jumped) {
+ test();
+ }
+
+ if (lily_g.failed) {
+ lily_g.n_tests_failed += 1;
+ printf("================================================================================\n");
+ printf("test \"%s\" failed!\n\n", lily_g.test_name);
+ lily_test_msg_t *n = lily_g.HEAD.next;
+ while(n != NULL) {
+ printf(" %s\n [%s]\n\n", n->msg, n->location);
+ n = n->next;
+ }
+ }
+ lily_msg_destroy(lily_g.HEAD.next);
+}
+
+
+void lily_info(const char *location, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ lily_test_msg_t *m = lily_msg_create(location, fmt, args);
+ va_end(args);
+
+ lily_g.TAIL->next = m;
+ lily_g.TAIL = m;
+}
+
+void lily_check(int x, const char *location, const char *fmt, ...)
+{
+ lily_g.n_assertions += 1;
+ if (!x) {
+ // assertion failed
+ lily_g.n_assertions_failed += 1;
+
+ va_list args;
+ va_start(args, fmt);
+ lily_test_msg_t *m = lily_msg_create(location, fmt, args);
+ va_end(args);
+
+ lily_g.TAIL->next = m;
+ lily_g.TAIL = m;
+ lily_g.failed = 1;
+ }
+}
+
+
+void lily_require(int x, const char *location, const char *fmt, ...)
+{
+ lily_g.n_assertions += 1;
+ if (!x) {
+ // assertion failed
+ lily_g.n_assertions_failed += 1;
+
+ va_list args;
+ va_start(args, fmt);
+ lily_test_msg_t *m = lily_msg_create(location, fmt, args);
+ va_end(args);
+
+ lily_g.TAIL->next = m;
+ lily_g.TAIL = m;
+ lily_g.failed = 1;
+
+ /* longjump out of test */
+ longjmp(lily_g.env, 1);
+ }
+}
+
+
+void lily_begin()
+{
+ lily_g.n_tests = 0;
+ lily_g.n_tests_failed = 0;
+ lily_g.n_assertions_failed = 0;
+ lily_g.n_assertions_failed = 0;
+ lily_g.epsilon = 1e-3;
+
+ printf("================================================================================\n");
+ printf("lily-test version %d.%d.%d\n", LILY_VERSION_MAJOR, LILY_VERSION_MINOR, LILY_VERSION_PATCH);
+}
+
+
+void lily_finish()
+{
+ printf("================================================================================\n");
+ printf("ran %d tests (%d failed)\n",
+ lily_g.n_tests, lily_g.n_tests_failed);
+ printf("checked %d asserts (%d failed)\n",
+ lily_g.n_assertions, lily_g.n_assertions_failed);
+}
+
+
+void lily_set_epsilon(double epsilon)
+{
+ lily_g.epsilon = epsilon;
+}
+
+
+int lily_streq(const char *str1, const char *str2)
+{
+ if (str1 == NULL && str2 == NULL) {
+ /* both are null (and therefore equal, i guess) */
+ return 1;
+ }
+
+ if (str1 == NULL || str2 == NULL) {
+ /* only one is null */
+ return 0;
+ }
+
+ /* neither are null, check normal string equality */
+ return strcmp(str1, str2) == 0;
+}
+#endif
+
+/* ifdef LILY_TEST_H */
+#else
+
+/* counter incrementing */
+/* this counter can count up to 4096, but it should be pretty trivial to extend it more or less arbitrarily far */
+/* to increment the counter, just #define LILY_COUNTER_INCREMENT and then include this file again */
+#ifdef LILY_COUNTER_INCREMENT
+#undef LILY_COUNTER_INCREMENT
+ /* ones position */
+# if LILY_HEX(LILY_COUNTER_POS_0) == 0
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 1
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 1
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 2
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 2
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 3
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 3
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 4
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 4
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 5
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 5
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 6
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 6
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 7
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 7
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 8
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 8
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 9
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 9
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 a
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 10
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 b
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 11
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 c
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 12
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 d
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 13
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 e
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 14
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 f
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 15
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 0
+ /* sixteens position */
+# if LILY_HEX(LILY_COUNTER_POS_1) == 0
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 1
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 1
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 2
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 2
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 3
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 3
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 4
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 4
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 5
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 5
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 6
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 6
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 7
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 7
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 8
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 8
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 9
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 9
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 a
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 10
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 b
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 11
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 c
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 12
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 d
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 13
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 e
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 14
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 f
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 15
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 0
+ /* 256s position */
+# if LILY_HEX(LILY_COUNTER_POS_2) == 0
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 1
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 1
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 2
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 2
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 3
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 3
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 4
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 4
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 5
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 5
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 6
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 6
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 7
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 7
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 8
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 8
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 9
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 9
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 a
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 10
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 b
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 11
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 c
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 12
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 d
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 13
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 e
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 14
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 f
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 15
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 0
+# endif
+# endif
+# endif
+
+#elif defined(LILY_COUNTER_DECREMENT)
+#undef LILY_COUNTER_DECREMENT
+ /* ones position */
+# if LILY_HEX(LILY_COUNTER_POS_0) == 15
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 e
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 14
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 d
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 13
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 c
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 12
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 b
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 11
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 a
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 10
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 9
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 9
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 8
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 8
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 7
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 7
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 6
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 6
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 5
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 5
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 4
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 4
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 3
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 3
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 2
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 2
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 1
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 1
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 0
+# elif LILY_HEX(LILY_COUNTER_POS_0) == 0
+# undef LILY_COUNTER_POS_0
+# define LILY_COUNTER_POS_0 f
+ /* sixteens position */
+# if LILY_HEX(LILY_COUNTER_POS_1) == 15
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 e
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 14
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 d
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 13
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 c
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 12
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 b
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 11
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 a
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 10
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 9
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 9
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 8
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 8
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 7
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 7
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 6
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 6
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 5
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 5
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 4
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 4
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 3
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 3
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 2
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 2
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 1
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 1
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 0
+# elif LILY_HEX(LILY_COUNTER_POS_1) == 0
+# undef LILY_COUNTER_POS_1
+# define LILY_COUNTER_POS_1 f
+ /* 256s position */
+# if LILY_HEX(LILY_COUNTER_POS_2) == 15
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 e
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 14
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 d
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 13
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 c
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 12
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 b
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 11
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 a
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 10
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 9
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 9
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 8
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 8
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 7
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 7
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 6
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 6
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 5
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 5
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 4
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 4
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 3
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 3
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 2
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 2
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 1
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 1
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 0
+# elif LILY_HEX(LILY_COUNTER_POS_2) == 0
+# undef LILY_COUNTER_POS_2
+# define LILY_COUNTER_POS_2 f
+# endif
+# endif
+# endif
+
+
+#elif defined(LILY_FILE_END)
+/* ending a file */
+
+/* LILY_LIST_HEAD is one too high, so we decrement */
+#define LILY_COUNTER_DECREMENT
+#include LILY_TEST_H_LOCATION
+
+void file_runner()
+{
+ lily_ll_node_t *n = &LILY_LIST_HEAD;
+ while (n->next != NULL) {
+ lily_run_test(n->f);
+ n = n->next;
+ }
+}
+
+#else
+/* register new test */
+
+static lily_ll_node_t LILY_ANON_NODE = {
+#define LILY_COUNTER_DECREMENT
+#include LILY_TEST_H_LOCATION
+ &LILY_LIST_HEAD,
+#define LILY_COUNTER_INCREMENT
+#include LILY_TEST_H_LOCATION
+ LILY_ANON_WRAP,
+};
+#undef LILY_LIST_HEAD
+#define LILY_LIST_HEAD LILY_ANON_NODE
+#define LILY_COUNTER_INCREMENT
+#include LILY_TEST_H_LOCATION
+
+#endif
+
+#endif