diff options
| author | 2026-02-23 20:36:05 +0100 | |
|---|---|---|
| committer | 2026-02-23 20:36:05 +0100 | |
| commit | 052144cabb126efe925a96f8a0597a0f2005d206 (patch) | |
| tree | 4fd87244b9eef018b30e90fdff24c5b1a145a85e /test/external/metalang99/tests/lang.c | |
| parent | 4e9020dfb847d80475415f9f5914efaa50238767 (diff) | |
add metalang99 testsuite (preprocessor stress testing)
Diffstat (limited to 'test/external/metalang99/tests/lang.c')
| -rw-r--r-- | test/external/metalang99/tests/lang.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/test/external/metalang99/tests/lang.c b/test/external/metalang99/tests/lang.c new file mode 100644 index 0000000..df76c3d --- /dev/null +++ b/test/external/metalang99/tests/lang.c @@ -0,0 +1,165 @@ +// `...` is sometimes used to workaround a TCC bug, see +// <https://github.com/hirrolot/datatype99/issues/10#issuecomment-830813172>. + +#include <metalang99/assert.h> +#include <metalang99/util.h> + +int main(void) { + + ML99_ASSERT_EMPTY_UNEVAL(ML99_EVAL(v())); + +#ifndef __TINYC__ +#define F_IMPL() v(123) +#else +#define F_IMPL(...) v(123) // `...` due to the TCC's bug. +#endif + + // A function with zero arguments + { + ML99_ASSERT_EQ(ML99_call(F, v()), v(123)); + ML99_ASSERT_EQ(ML99_call(v(F), v()), v(123)); + ML99_ASSERT_EQ(ML99_callUneval(F, ), v(123)); + } + +#undef F_IMPL + +#define F_IMPL(x, y, z) v(x##y##z) +#define BAR_IMPL(x, y) v(x + y) + + // Regular usage of the metalanguage + { + ML99_ASSERT_EQ(ML99_call(BAR, v(5), v(7)), v(5 + 7)); + ML99_ASSERT_EQ(ML99_call(ML99_call(F, v(B), v(A), v(R)), v(6), v(11)), v(6 + 11)); + + ML99_ASSERT_EQ(ML99_call(BAR, v(5, 7)), v(5 + 7)); + ML99_ASSERT_EQ(ML99_call(ML99_call(F, v(B, A, R)), v(6, 11)), v(6 + 11)); + + ML99_ASSERT_EQ(ML99_callUneval(BAR, 5, 7), v(5 + 7)); + } + +#undef F_IMPL +#undef BAR_IMPL + +// Even if a term in the argument position evaluates to more than one terms, they should be appended +// to each other but not interspersed with a comma. +#define F_IMPL(...) ML99_TERMS(v(1), v(2), v(3)) // `...` due to the TCC's bug. +#define BAR_IMPL(x) v() + + ML99_EVAL(ML99_call(BAR, ML99_call(F, v()))) + +#undef F_IMPL +#undef BAR_IMPL + +// Recursion might arise from a higher-order macro, if `op` invokes `F`, but nonetheless the +// second call to `F` must be performed as expected. +#define F_IMPL(op) ML99_call(op, v(123)) +#define OP_IMPL(x) ML99_call(F, v(ID)) +#define ID_IMPL(x) v(x) + + ML99_ASSERT_EQ(ML99_call(F, v(OP)), v(123)); + +#undef F_IMPL +#undef OP_IMPL +#undef ID_IMPL + + // ML99_abort + { + ML99_ASSERT_EMPTY(ML99_abort(v())); + ML99_ASSERT_EQ(ML99_abort(v(815057)), v(815057)); + ML99_ASSERT_UNEVAL(ML99_EVAL(v(~), ML99_abort(v(123)), v(~)) == 123); + +// Ensure that `ML99_abort` also works correctly after some evaluations. +#define F_IMPL(...) ML99_call(G, v(1, 2), ML99_call(H, v(123))) // `...` due to the TCC's bug. +#define G_IMPL(_1, _2, _123_plus_1) \ + ML99_abort(v(ML99_ASSERT_UNEVAL(_1 == 1 && _2 == 2 && _123_plus_1 == 123 + 1))) +#define H_IMPL(a) v(a + 1) + + ML99_EVAL(ML99_call(F, v())); + +#undef F_IMPL +#undef G_IMPL +#undef H_IMPL + + // Ensure that `ML99_abort` immediately aborts interpretation even in an argument position. + ML99_ASSERT_EQ(ML99_call(NonExistingF, ML99_abort(v(123))), v(123)); + } + + // Partial application + { + +#ifndef __TINYC__ +#define F_IMPL() v(123) +#else +#define F_IMPL(...) v(123) // // `...` due to the TCC's bug. +#endif + +#define F_ARITY 1 + + // The arity of a function with zero arguments must be 1 + { ML99_ASSERT_EQ(ML99_appl(v(F), v()), v(123)); } + +#undef F_IMPL +#undef F_ARITY + +#define F_IMPL(a, b, c, d) v(a##b##c##d) +#define F_ARITY 4 + + // Regular usage of partial application + { + ML99_ASSERT_EQ( + ML99_appl(ML99_appl(ML99_appl(ML99_appl(v(F), v(10)), v(5)), v(7)), v(8)), + v(10578)); + ML99_ASSERT_EQ( + ML99_appl(ML99_appl(ML99_appl2(v(F), v(10), v(5)), v(7)), v(8)), + v(10578)); + ML99_ASSERT_EQ(ML99_appl(ML99_appl3(v(F), v(10), v(5), v(7)), v(8)), v(10578)); + ML99_ASSERT_EQ(ML99_appl4(v(F), v(10), v(5), v(7), v(8)), v(10578)); + } + +#undef F_IMPL +#undef F_ARITY + +#define F_ARITY 255 + + // The maximum arity + { + ML99_EVAL(ML99_empty(ML99_appl(v(F), v(~)))) + ML99_EVAL(ML99_empty(ML99_appl2(v(F), v(~), v(~)))) + ML99_EVAL(ML99_empty(ML99_appl3(v(F), v(~), v(~), v(~)))) + } + +#undef F_ARITY + } + +#define F_IMPL(x) v((x + 1)) +#define G_IMPL(x) v((x * 8)) + +#define F_ARITY 1 +#define G_ARITY 1 + + // ML99_compose + { ML99_ASSERT_EQ(ML99_appl(ML99_compose(v(F), v(G)), v(3)), v((3 * 8) + 1)); } + +#undef F_IMPL +#undef G_IMPL + +#undef F_ARITY +#undef G_ARITY + +#define F_IMPL(x) v(x) + +#define PROG ML99_TERMS(v(1), v(, ), v(2), v(, ), ML99_call(F, v(7))) + +#define CHECK(...) CHECK_AUX(__VA_ARGS__) +#define CHECK_AUX(a, b, c) ML99_ASSERT_UNEVAL(a == 1 && b == 2 && c == 7) + + // ML99_QUOTE + { CHECK(ML99_EVAL(ML99_EVAL(ML99_QUOTE(PROG)))); } + +#undef F_IMPL + +#undef PROG + +#undef CHECK +#undef CHECK_AUX +} |