aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/external/metalang99/examples
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-02-23 20:36:05 +0100
committerlemon <lsof@mailbox.org>2026-02-23 20:36:05 +0100
commit052144cabb126efe925a96f8a0597a0f2005d206 (patch)
tree4fd87244b9eef018b30e90fdff24c5b1a145a85e /test/external/metalang99/examples
parent4e9020dfb847d80475415f9f5914efaa50238767 (diff)
add metalang99 testsuite (preprocessor stress testing)
Diffstat (limited to 'test/external/metalang99/examples')
-rw-r--r--test/external/metalang99/examples/.gitignore1
-rw-r--r--test/external/metalang99/examples/CMakeLists.txt30
-rw-r--r--test/external/metalang99/examples/ackermann.c22
-rw-r--r--test/external/metalang99/examples/assert_for_each.c31
-rw-r--r--test/external/metalang99/examples/binary_tree.c25
-rw-r--r--test/external/metalang99/examples/demo.c48
-rw-r--r--test/external/metalang99/examples/duffs_device.c76
-rw-r--r--test/external/metalang99/examples/factorial.c16
-rw-r--r--test/external/metalang99/examples/lambda_calculus.c209
-rw-r--r--test/external/metalang99/examples/overload.c15
-rw-r--r--test/external/metalang99/examples/rectangle.c25
11 files changed, 498 insertions, 0 deletions
diff --git a/test/external/metalang99/examples/.gitignore b/test/external/metalang99/examples/.gitignore
new file mode 100644
index 0000000..567609b
--- /dev/null
+++ b/test/external/metalang99/examples/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/test/external/metalang99/examples/CMakeLists.txt b/test/external/metalang99/examples/CMakeLists.txt
new file mode 100644
index 0000000..ed8e963
--- /dev/null
+++ b/test/external/metalang99/examples/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.16)
+project(examples LANGUAGES C)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ add_compile_options(-Wall -Wextra -pedantic -std=c99
+ -ftrack-macro-expansion=0)
+elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ add_compile_options("-fmacro-backtrace-limit=1")
+elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ # Enable a standard-conforming C99/C11 preprocessor.
+ add_compile_options("/std:c11")
+elseif(CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
+ add_compile_definitions(ML99_ALLOW_POOR_DIAGNOSTICS)
+endif()
+
+include_directories(../include)
+
+add_executable(ackermann ackermann.c)
+add_executable(assert_for_each assert_for_each.c)
+add_executable(binary_tree binary_tree.c)
+add_executable(demo demo.c)
+add_executable(duffs_device duffs_device.c)
+add_executable(factorial factorial.c)
+add_executable(overload overload.c)
+add_executable(rectangle rectangle.c)
+add_executable(lambda_calculus lambda_calculus.c)
+
+foreach(TARGET ${BUILDSYSTEM_TARGETS})
+ set_target_properties(TARGET PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED ON)
+endforeach()
diff --git a/test/external/metalang99/examples/ackermann.c b/test/external/metalang99/examples/ackermann.c
new file mode 100644
index 0000000..a3ea623
--- /dev/null
+++ b/test/external/metalang99/examples/ackermann.c
@@ -0,0 +1,22 @@
+#include <metalang99.h>
+
+#define ack(m, n) ML99_natMatchWithArgs(m, v(ack_), n)
+
+#define ack_Z_IMPL(n) ML99_inc(v(n))
+#define ack_S_IMPL(m, n) ML99_natMatchWithArgs(v(n), v(ack_S_), v(m))
+#define ack_S_Z_IMPL(m) ack(v(m), v(1))
+#define ack_S_S_IMPL(n, m) ack(v(m), ack(ML99_inc(v(m)), v(n)))
+
+ML99_ASSERT_EQ(ack(v(0), v(0)), v(1));
+ML99_ASSERT_EQ(ack(v(0), v(1)), v(2));
+ML99_ASSERT_EQ(ack(v(0), v(2)), v(3));
+
+ML99_ASSERT_EQ(ack(v(1), v(0)), v(2));
+ML99_ASSERT_EQ(ack(v(1), v(1)), v(3));
+ML99_ASSERT_EQ(ack(v(1), v(2)), v(4));
+
+ML99_ASSERT_EQ(ack(v(2), v(0)), v(3));
+ML99_ASSERT_EQ(ack(v(2), v(1)), v(5));
+ML99_ASSERT_EQ(ack(v(2), v(2)), v(7));
+
+int main(void) {}
diff --git a/test/external/metalang99/examples/assert_for_each.c b/test/external/metalang99/examples/assert_for_each.c
new file mode 100644
index 0000000..3ba9127
--- /dev/null
+++ b/test/external/metalang99/examples/assert_for_each.c
@@ -0,0 +1,31 @@
+// Asserts multiple expressions at once.
+
+#include <metalang99.h>
+
+#include <assert.h>
+
+#define ASSERT_FOR_EACH(...) \
+ do { \
+ ML99_EVAL(ML99_variadicsForEach( \
+ ML99_compose(v(ML99_semicoloned), ML99_reify(v(assert))), \
+ v(__VA_ARGS__))) \
+ } while (0)
+
+int main(void) {
+ ASSERT_FOR_EACH(123 == 123, 2 + 2 == 4, "foo"[1] == 'o');
+
+ /*
+ * If we combine multiple assertions with the && operator, we will not be able to distinguish
+ * them if one of them fails apparently:
+ *
+ * main: Assertion `123 == 321 && 2 + 2 == 4 && "foo"[1] == 'o' failed.
+ * assert(123 == 321 && 2 + 2 == 4 && "foo"[1] == 'o');
+ */
+
+ /*
+ * ... unlike `ASSERT_FOR_EACH` telling us which one has failed:
+ *
+ * main: Assertion `123 == 321' failed.
+ * ASSERT_FOR_EACH(123 == 321, 2 + 2 == 4, "foo"[1] == 'o');
+ */
+}
diff --git a/test/external/metalang99/examples/binary_tree.c b/test/external/metalang99/examples/binary_tree.c
new file mode 100644
index 0000000..8cd9177
--- /dev/null
+++ b/test/external/metalang99/examples/binary_tree.c
@@ -0,0 +1,25 @@
+// Sums all nodes of a binary tree, recursively.
+
+#include <metalang99.h>
+
+#define leaf(x) ML99_choice(v(leaf), x)
+#define node(lhs, data, rhs) ML99_choice(v(node), lhs, data, rhs)
+
+#define sumTree(tree) ML99_match(tree, v(sumTree_))
+#define sumTree_leaf_IMPL(x) v(x)
+#define sumTree_node_IMPL(lhs, data, rhs) ML99_add3(sumTree(v(lhs)), v(data), sumTree(v(rhs)))
+
+/*
+ * 4
+ * / \
+ * / \
+ * / \
+ * 2 6
+ * / \ / \
+ * 1 3 5 7
+ */
+#define TREE node(node(leaf(v(1)), v(2), leaf(v(3))), v(4), node(leaf(v(5)), v(6), leaf(v(7))))
+
+ML99_ASSERT_EQ(sumTree(TREE), v(28));
+
+int main(void) {}
diff --git a/test/external/metalang99/examples/demo.c b/test/external/metalang99/examples/demo.c
new file mode 100644
index 0000000..4a209d9
--- /dev/null
+++ b/test/external/metalang99/examples/demo.c
@@ -0,0 +1,48 @@
+// `...` is sometimes used to workaround a TCC bug, see
+// <https://github.com/hirrolot/datatype99/issues/10#issuecomment-830813172>.
+
+#include <metalang99.h>
+
+// Compile-time list manipulation:
+
+// 3, 3, 3, 3, 3
+static int five_threes[] = {
+ ML99_LIST_EVAL_COMMA_SEP(ML99_listReplicate(v(5), v(3))),
+};
+
+// 5, 4, 3, 2, 1
+static int from_5_to_1[] = {
+ ML99_LIST_EVAL_COMMA_SEP(ML99_listReverse(ML99_list(v(1, 2, 3, 4, 5)))),
+};
+
+// 9, 2, 5
+static int lesser_than_10[] = {
+ ML99_LIST_EVAL_COMMA_SEP(
+ ML99_listFilter(ML99_appl(v(ML99_greater), v(10)), ML99_list(v(9, 2, 11, 13, 5)))),
+};
+
+// Macro recursion:
+#define factorial(n) ML99_natMatch(n, v(factorial_))
+#define factorial_Z_IMPL(...) v(1) // `...` due to the TCC's bug.
+#define factorial_S_IMPL(n) ML99_mul(ML99_inc(v(n)), factorial(v(n)))
+
+ML99_ASSERT_EQ(factorial(v(4)), v(24));
+
+// Overloading on a number of arguments:
+typedef struct {
+ double width, height;
+} Rect;
+
+#define Rect_new(...) ML99_OVERLOAD(Rect_new_, __VA_ARGS__)
+#define Rect_new_1(x) \
+ { x, x }
+#define Rect_new_2(x, y) \
+ { x, y }
+
+static Rect _7x8 = Rect_new(7, 8), _10x10 = Rect_new(10);
+
+// ... and more!
+
+int main(void) {
+ // Yeah. All is done at compile time.
+}
diff --git a/test/external/metalang99/examples/duffs_device.c b/test/external/metalang99/examples/duffs_device.c
new file mode 100644
index 0000000..cc5ade0
--- /dev/null
+++ b/test/external/metalang99/examples/duffs_device.c
@@ -0,0 +1,76 @@
+/**
+ * Duff's device [1] is a technique to implement loop unrolling through amalgamation of a switch
+ * statement with a do-while loop.
+ *
+ * In this example, we are going to implement automatic generation of Duff's device.
+ *
+ * [1]: https://en.wikipedia.org/wiki/Duff's_device
+ */
+
+#include <metalang99.h>
+
+#include <assert.h>
+
+#define DUFFS_DEVICE(unrolling_factor, counter_ty, count, ...) \
+ do { \
+ if ((count) > 0) { \
+ counter_ty DUFFS_DEVICE_n = ((count) + ML99_DEC(unrolling_factor)) / unrolling_factor; \
+ switch ((count) % unrolling_factor) { \
+ case 0: \
+ do { \
+ __VA_ARGS__ \
+ ML99_EVAL(ML99_callUneval(genCases, ML99_DEC(unrolling_factor), __VA_ARGS__)) \
+ } while (--DUFFS_DEVICE_n > 0); \
+ } \
+ } \
+ } while (0)
+
+#define genCases_IMPL(i, ...) \
+ ML99_IF( \
+ ML99_NAT_EQ(i, 0), \
+ ML99_empty(), \
+ ML99_TERMS( \
+ v(/* FALLTHROUGH */ case i \
+ : __VA_ARGS__), \
+ ML99_callUneval(genCases, ML99_DEC(i), __VA_ARGS__)))
+
+int main(void) {
+#define ARRAY_LEN 50
+#define UNROLLING_FACTOR 3
+
+ int array[ARRAY_LEN] = { ML99_EVAL(ML99_times(v(ARRAY_LEN), v(5, ))) };
+ int *n_ptr = array;
+
+ // Square all the elements in the array.
+ DUFFS_DEVICE(UNROLLING_FACTOR, int, ARRAY_LEN, {
+ *n_ptr *= *n_ptr;
+ n_ptr++;
+ });
+
+ for (int i = 0; i < ARRAY_LEN; i++) {
+ assert(25 == array[i]);
+ }
+}
+
+/*
+The generated Duff's device:
+
+int DUFFS_DEVICE_n = ((50) + 2) / 3;
+switch ((50) % 3) {
+case 0:
+ do {
+ {
+ *n_ptr *= *n_ptr;
+ n_ptr++;
+ }
+ case 2: {
+ *n_ptr *= *n_ptr;
+ n_ptr++;
+ }
+ case 1: {
+ *n_ptr *= *n_ptr;
+ n_ptr++;
+ }
+ } while (--DUFFS_DEVICE_n > 0);
+}
+*/
diff --git a/test/external/metalang99/examples/factorial.c b/test/external/metalang99/examples/factorial.c
new file mode 100644
index 0000000..ed381fa
--- /dev/null
+++ b/test/external/metalang99/examples/factorial.c
@@ -0,0 +1,16 @@
+// `...` is sometimes used to workaround a TCC bug, see
+// <https://github.com/hirrolot/datatype99/issues/10#issuecomment-830813172>.
+
+#include <metalang99.h>
+
+#define factorial(n) ML99_natMatch(n, v(factorial_))
+#define factorial_Z_IMPL(...) v(1) // `...` due to the TCC's bug.
+#define factorial_S_IMPL(n) ML99_mul(ML99_inc(v(n)), factorial(v(n)))
+
+ML99_ASSERT_EQ(factorial(v(0)), v(1));
+ML99_ASSERT_EQ(factorial(v(1)), v(1));
+ML99_ASSERT_EQ(factorial(v(2)), v(2));
+ML99_ASSERT_EQ(factorial(v(3)), v(6));
+ML99_ASSERT_EQ(factorial(v(4)), v(24));
+
+int main(void) {}
diff --git a/test/external/metalang99/examples/lambda_calculus.c b/test/external/metalang99/examples/lambda_calculus.c
new file mode 100644
index 0000000..e300304
--- /dev/null
+++ b/test/external/metalang99/examples/lambda_calculus.c
@@ -0,0 +1,209 @@
+/*
+ * An untyped lambda calculus [1] interpreter using De Bruijn indices [2] and normal order
+ * evaluation strategy [3].
+ *
+ * [1] https://en.wikipedia.org/wiki/Lambda_calculus
+ * [2] https://en.wikipedia.org/wiki/De_Bruijn_index
+ * [3] https://en.wikipedia.org/wiki/Evaluation_strategy#Normal_order
+ */
+
+#include <metalang99.h>
+
+// Syntactic terms {
+
+#define var(i) ML99_call(var, i)
+#define appl(M, N) ML99_call(appl, M, N)
+#define lam(M) ML99_call(lam, M)
+
+#define var_IMPL(i) v(VAR(i))
+#define appl_IMPL(M, N) v(APPL(M, N))
+#define lam_IMPL(M) v(LAM(M))
+
+#define VAR(i) ML99_CHOICE(var, i)
+#define APPL(M, N) ML99_CHOICE(appl, M, N)
+#define LAM(M) ML99_CHOICE(lam, M)
+// } (Syntactic terms)
+
+// Variable substitution: `M[1=x]` {
+
+#define subst(M, x) ML99_call(subst, M, x)
+
+#define subst_IMPL(M, x) substAux_IMPL(M, x, 1)
+#define substAux_IMPL(M, x, depth) ML99_callUneval(ML99_matchWithArgs, M, substAux_, x, depth)
+
+#define substAux_var_IMPL(i, x, depth) \
+ ML99_IF( \
+ ML99_NAT_EQ(i, depth), \
+ v(x), \
+ ML99_call(ML99_if, ML99_callUneval(ML99_greater, i, depth), v(VAR(ML99_DEC(i)), VAR(i))))
+#define substAux_appl_IMPL(M, N, x, depth) \
+ appl(substAux_IMPL(M, x, depth), substAux_IMPL(N, x, depth))
+#define substAux_lam_IMPL(M, x, depth) \
+ lam(ML99_call(substAux, v(M), incFreeVars_IMPL(x), v(ML99_INC(depth))))
+// } (Variable substitution)
+
+// Increment free variables in `M` {
+
+#define incFreeVars(M) ML99_call(incFreeVars, M)
+
+#define incFreeVars_IMPL(M) incFreeVarsAux_IMPL(M, 1)
+#define incFreeVarsAux_IMPL(M, depth) ML99_callUneval(ML99_matchWithArgs, M, incFreeVarsAux_, depth)
+
+#define incFreeVarsAux_var_IMPL(i, depth) \
+ ML99_call(ML99_if, ML99_callUneval(ML99_greaterEq, i, depth), v(VAR(ML99_INC(i)), VAR(i)))
+#define incFreeVarsAux_appl_IMPL(M, N, depth) \
+ appl(incFreeVarsAux_IMPL(M, depth), incFreeVarsAux_IMPL(N, depth))
+#define incFreeVarsAux_lam_IMPL(M, depth) lam(incFreeVarsAux_IMPL(M, ML99_INC(depth)))
+// } (Increment free variables)
+
+// Evaluation {
+
+#define eval(M) ML99_call(eval, M)
+
+#define eval_IMPL(M) ML99_callUneval(ML99_match, M, eval_)
+#define eval_var_IMPL(i) v(VAR(i))
+#define eval_appl_IMPL(M, N) ML99_callUneval(ML99_matchWithArgs, M, eval_appl_, N)
+#define eval_lam_IMPL(M) lam(eval_IMPL(M))
+
+#define eval_appl_var_IMPL(i, N) appl(v(VAR(i)), eval_IMPL(N))
+#define eval_appl_appl_IMPL(M, N, N1) \
+ ML99_call(ML99_matchWithArgs, eval(appl_IMPL(M, N)), v(eval_appl_appl_, N1))
+#define eval_appl_lam_IMPL(M, N) eval(subst_IMPL(M, N))
+
+#define eval_appl_appl_var_IMPL eval_appl_var_IMPL
+#define eval_appl_appl_appl_IMPL(M, N, N1) appl(appl_IMPL(M, N), eval_IMPL(N1))
+#define eval_appl_appl_lam_IMPL eval_appl_lam_IMPL
+// } (Evaluation)
+
+// Syntactical equality {
+
+#define termEq(lhs, rhs) ML99_matchWithArgs(lhs, v(termEq_), rhs)
+#define termEq_var_IMPL(i, rhs) termEqPropagate(var, rhs, i)
+#define termEq_appl_IMPL(M, N, rhs) termEqPropagate(appl, rhs, M, N)
+#define termEq_lam_IMPL(M, rhs) termEqPropagate(lam, rhs, M)
+
+#define termEqPropagate(term_kind, rhs, ...) \
+ ML99_IF( \
+ ML99_IDENT_EQ(TERM_, ML99_CHOICE_TAG(rhs), term_kind), \
+ ML99_matchWithArgs(v(rhs), v(termEq_##term_kind##_), v(__VA_ARGS__)), \
+ ML99_false())
+
+#define termEq_var_var_IMPL(j, i) v(ML99_NAT_EQ(i, j))
+#define termEq_appl_appl_IMPL(M, N, M1, N1) ML99_and(termEq(v(M), v(M1)), termEq(v(N), v(N1)))
+#define termEq_lam_lam_IMPL(M, M1) termEq(v(M), v(M1))
+
+#define TERM_var_var ()
+#define TERM_appl_appl ()
+#define TERM_lam_lam ()
+// } (Syntactical equality)
+
+#define ASSERT_REDUCES_TO(lhs, rhs) \
+ /* Use two interpreter passes: one for `eval(lhs)`, one for `termEq`. Thereby we achieve more \
+ * Metalang99 reduction steps available. */ \
+ ML99_ASSERT_UNEVAL(ML99_EVAL(termEq(v(ML99_EVAL(eval(v(lhs)))), v(ML99_EVAL(eval(v(rhs)))))))
+
+// The identity combinator {
+
+#define I LAM(VAR(1))
+
+ASSERT_REDUCES_TO(APPL(I, VAR(5)), VAR(5));
+// } (The identity combinator)
+
+// The K, S combinators {
+
+#define K LAM(LAM(VAR(2)))
+#define S LAM(LAM(LAM(APPL(APPL(VAR(3), VAR(1)), APPL(VAR(2), VAR(1))))))
+
+ASSERT_REDUCES_TO(APPL(APPL(S, K), K), I);
+ASSERT_REDUCES_TO(APPL(APPL(APPL(S, K), S), K), K);
+
+ASSERT_REDUCES_TO(APPL(APPL(APPL(S, K), VAR(5)), VAR(6)), VAR(6));
+ASSERT_REDUCES_TO(APPL(APPL(K, VAR(5)), VAR(6)), VAR(5));
+// } (The K, S combinators)
+
+// Church booleans {
+
+#define T LAM(LAM(VAR(2)))
+#define F LAM(LAM(VAR(1)))
+
+#define NOT LAM(APPL(APPL(VAR(1), F), T))
+#define AND LAM(LAM(APPL(APPL(VAR(2), VAR(1)), VAR(2))))
+#define OR LAM(LAM(APPL(APPL(VAR(2), VAR(2)), VAR(1))))
+#define XOR LAM(LAM(APPL(APPL(VAR(2), APPL(NOT, VAR(1))), VAR(1))))
+
+#define IF LAM(LAM(LAM(APPL(APPL(VAR(3), VAR(2)), VAR(1)))))
+
+ASSERT_REDUCES_TO(APPL(NOT, T), F);
+ASSERT_REDUCES_TO(APPL(NOT, F), T);
+ASSERT_REDUCES_TO(APPL(NOT, APPL(NOT, T)), T);
+ASSERT_REDUCES_TO(APPL(NOT, APPL(NOT, F)), F);
+
+ASSERT_REDUCES_TO(APPL(APPL(AND, T), T), T);
+ASSERT_REDUCES_TO(APPL(APPL(AND, T), F), F);
+ASSERT_REDUCES_TO(APPL(APPL(AND, F), T), F);
+ASSERT_REDUCES_TO(APPL(APPL(AND, F), F), F);
+
+ASSERT_REDUCES_TO(APPL(APPL(OR, T), T), T);
+ASSERT_REDUCES_TO(APPL(APPL(OR, T), F), T);
+ASSERT_REDUCES_TO(APPL(APPL(OR, F), T), T);
+ASSERT_REDUCES_TO(APPL(APPL(OR, F), F), F);
+
+ASSERT_REDUCES_TO(APPL(APPL(XOR, T), T), F);
+ASSERT_REDUCES_TO(APPL(APPL(XOR, T), F), T);
+ASSERT_REDUCES_TO(APPL(APPL(XOR, F), T), T);
+ASSERT_REDUCES_TO(APPL(APPL(XOR, F), F), F);
+
+ASSERT_REDUCES_TO(APPL(APPL(APPL(IF, T), VAR(5)), VAR(6)), VAR(5));
+ASSERT_REDUCES_TO(APPL(APPL(APPL(IF, F), VAR(5)), VAR(6)), VAR(6));
+// } (Church booleans)
+
+// Church numerals {
+
+#define ZERO LAM(LAM(VAR(1)))
+#define SUCC LAM(LAM(LAM(APPL(VAR(2), APPL(APPL(VAR(3), VAR(2)), VAR(1))))))
+
+#define ONE APPL(SUCC, ZERO)
+#define TWO APPL(SUCC, ONE)
+#define THREE APPL(SUCC, TWO)
+#define FOUR APPL(SUCC, THREE)
+
+#define ADD LAM(LAM(LAM(LAM(APPL(APPL(VAR(4), VAR(2)), APPL(APPL(VAR(3), VAR(2)), VAR(1)))))))
+#define MUL LAM(LAM(LAM(LAM(APPL(APPL(VAR(4), APPL(VAR(3), VAR(2))), VAR(1))))))
+
+ASSERT_REDUCES_TO(APPL(APPL(ADD, ZERO), ZERO), ZERO);
+ASSERT_REDUCES_TO(APPL(APPL(ADD, ZERO), ONE), ONE);
+ASSERT_REDUCES_TO(APPL(APPL(ADD, ONE), ZERO), ONE);
+ASSERT_REDUCES_TO(APPL(APPL(ADD, ONE), TWO), THREE);
+
+ASSERT_REDUCES_TO(APPL(APPL(MUL, ZERO), ZERO), ZERO);
+ASSERT_REDUCES_TO(APPL(APPL(MUL, ZERO), ONE), ZERO);
+ASSERT_REDUCES_TO(APPL(APPL(MUL, ONE), ZERO), ZERO);
+ASSERT_REDUCES_TO(APPL(APPL(MUL, TWO), TWO), FOUR);
+// } (Church numerals)
+
+// Church pairs {
+
+#define PAIR LAM(LAM(LAM(APPL(APPL(VAR(1), VAR(3)), VAR(2)))))
+#define FST LAM(APPL(VAR(1), T))
+#define SND LAM(APPL(VAR(1), F))
+
+ASSERT_REDUCES_TO(APPL(FST, APPL(APPL(PAIR, VAR(5)), VAR(6))), VAR(5));
+ASSERT_REDUCES_TO(APPL(SND, APPL(APPL(PAIR, VAR(5)), VAR(6))), VAR(6));
+// } (Church pairs)
+
+// Church lists {
+
+#define NIL F
+#define CONS PAIR
+#define IS_NIL LAM(APPL(APPL(VAR(1), LAM(LAM(LAM(F)))), T))
+
+#define LIST_1_2_3 APPL(APPL(CONS, VAR(1)), APPL(APPL(CONS, VAR(2)), APPL(APPL(CONS, VAR(3)), NIL)))
+
+ASSERT_REDUCES_TO(APPL(IS_NIL, NIL), T);
+ASSERT_REDUCES_TO(APPL(IS_NIL, LIST_1_2_3), F);
+// } (Church lists)
+
+// Recursion via self-application (or the Y combinator) is perfectly expressible, though when
+// executed, it exhausts the Metalang99 recursion engine limit.
+
+int main(void) {}
diff --git a/test/external/metalang99/examples/overload.c b/test/external/metalang99/examples/overload.c
new file mode 100644
index 0000000..8b5d6fd
--- /dev/null
+++ b/test/external/metalang99/examples/overload.c
@@ -0,0 +1,15 @@
+#include <metalang99.h>
+
+typedef struct {
+ double width, height;
+} Rect;
+
+#define Rect_new(...) ML99_OVERLOAD(Rect_new_, __VA_ARGS__)
+#define Rect_new_1(x) \
+ { x, x }
+#define Rect_new_2(x, y) \
+ { x, y }
+
+static Rect _7x8 = Rect_new(7, 8), _10x10 = Rect_new(10);
+
+int main(void) {}
diff --git a/test/external/metalang99/examples/rectangle.c b/test/external/metalang99/examples/rectangle.c
new file mode 100644
index 0000000..1ab2bbf
--- /dev/null
+++ b/test/external/metalang99/examples/rectangle.c
@@ -0,0 +1,25 @@
+// Computes the area of a rectangle.
+
+#include <metalang99.h>
+
+#define rect(width, height) ML99_tuple(width, height)
+#define rectWidth ML99_tupleGet(0)
+#define rectHeight ML99_tupleGet(1)
+
+#define rectArea(rect) ML99_mul(rectWidth(rect), rectHeight(rect))
+
+/*
+ * 15
+ * +------------------------------+
+ * | |
+ * | |
+ * | | 7
+ * | |
+ * | |
+ * +------------------------------+
+ */
+#define RECTANGLE rect(v(15), v(7))
+
+ML99_ASSERT_EQ(rectArea(RECTANGLE), v(15 * 7));
+
+int main(void) {}