Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
symbol_tests.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <lxsameer@lxsameer.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "base.h"
22#include "serene/rt/core.h"
24#include "serene/rt/protocols.h"
25#include "serene/rt/strings.h"
26#include "serene/rt/symbols.h"
27
28#define SYMBOL_TESTS(X) \
29 X("symbol::make", test_symbol_make), X("symbol::intern_symbol", test_symbol_intern), \
30 X("symbol::gensym", test_gensym), X("symbol::gensym_freshness", test_gensym_freshness)
31
32// Every test here works against one namespace named `serene.tests`.
34 srn_value_t *name = srn_string_make(ctx, &absurd_metadata, "serene.tests");
35 srn_error_t *err = nullptr;
37 TEST_ASSERT(ns != nullptr);
38 return ns;
39}
40
41static size_t sym_table_len(srn_namespace_t *ns) { return atomic_load(&ns->sym_table)->len; }
42
43static void test_symbol_make() {
44 MAKE_ENGINE(mm, engine);
45 MAKE_CONTEXT(engine, ctx);
46
48 srn_value_t *symbol_name = srn_string_make(ctx, &absurd_metadata, "foo");
49 srn_value_t *sym = srn_symbol_make(ctx, &absurd_metadata, ns, AS_STRING(symbol_name));
50
51 TEST_CHECK(IS_A(sym, VSymbol));
52 TEST_CHECK(AS_SYMBOL(sym)->ns == ns);
53
54 TEST_CHECK(srn_string_eq(AS_SYMBOL(sym)->name, AS_STRING(symbol_name)));
55
56 RELEASE_CONTEXT(ctx);
57 SHUTDOWN_ENGINE(mm, engine);
58}
59
60static void test_symbol_intern() {
61 MAKE_ENGINE(mm, engine);
62 MAKE_CONTEXT(engine, ctx);
63
64 srn_value_t *symname = srn_string_make(ctx, &absurd_metadata, "foo");
66
67 srn_value_t *symbol = srn_symbol_intern(ctx, &absurd_metadata, ns, AS_STRING(symname));
68
69 TEST_CHECK(sym_table_len(ns) == 1);
70 TEST_CHECK(IS_A(symbol, VSymbol));
71 TEST_CHECK(srn_string_eq(AS_SYMBOL(symbol)->name, AS_STRING(symname)));
72
73 srn_value_t *symbol1 = srn_symbol_intern(ctx, &absurd_metadata, ns, AS_STRING(symname));
74 TEST_CHECK(sym_table_len(ns) == 1);
75 TEST_CHECK(IS_A(symbol1, VSymbol));
76 TEST_CHECK(srn_string_eq(AS_SYMBOL(symbol1)->name, AS_STRING(symname)));
77 srn_value_t *check = srn_value_eq(ctx, symbol, symbol1);
78 TEST_CHECK(IS_TRUE(check));
79
80 RELEASE_CONTEXT(ctx);
81 SHUTDOWN_ENGINE(mm, engine);
82}
83
84static void test_gensym() {
85 MAKE_ENGINE(mm, engine);
86 MAKE_CONTEXT(engine, ctx);
87
89
90 srn_value_t *sym1 = srn_symbol_gen(ctx, &absurd_metadata, ns, nullptr);
91 srn_value_t *sym2 = srn_symbol_gen(ctx, &absurd_metadata, ns, nullptr);
92
93 srn_value_t *sym1_name = srn_string_make(ctx, &absurd_metadata, "gensym_101");
94 srn_value_t *sym2_name = srn_string_make(ctx, &absurd_metadata, "gensym_102");
95
96 // gensym should intern the symbol
97 TEST_CHECK(sym_table_len(ns) == 2);
98
99 TEST_CHECK(IS_A(sym1, VSymbol));
100 TEST_CHECK(IS_A(sym2, VSymbol));
101
102 TEST_CHECK(srn_string_eq(AS_SYMBOL(sym1)->name, AS_STRING(sym1_name)));
103 TEST_CHECK(srn_string_eq(AS_SYMBOL(sym2)->name, AS_STRING(sym2_name)));
104
105 RELEASE_CONTEXT(ctx);
106 SHUTDOWN_ENGINE(mm, engine);
107}
108
109// A generated symbol must be fresh. The spelling is readable text, so a user
110// symbol can occupy the name the next id would produce, and the generator
111// has to move to a new id instead of returning the occupant.
113 MAKE_ENGINE(mm, engine);
114 MAKE_CONTEXT(engine, ctx);
115
116 srn_namespace_t *ns = make_test_ns(ctx);
117
118 // Occupy the exact spelling the next generated symbol would take. The
119 // namespace consumed id 100, so the next object id is 101 (the counter
120 // behavior symbol::gensym pins).
121 srn_value_t *taken = srn_string_make(ctx, &absurd_metadata, "gensym_101");
122 srn_value_t *user_sym = srn_symbol_intern(ctx, &absurd_metadata, ns, AS_STRING(taken));
123 TEST_CHECK(IS_A(user_sym, VSymbol));
124
125 srn_value_t *g = srn_symbol_gen(ctx, &absurd_metadata, ns, nullptr);
127
128 // Not the occupant, not its spelling, and interned alongside it.
129 TEST_CHECK(g != user_sym);
130 TEST_CHECK(!srn_string_eq(AS_SYMBOL(g)->name, AS_STRING(taken)));
131 TEST_CHECK(sym_table_len(ns) == 2);
132
133 RELEASE_CONTEXT(ctx);
134 SHUTDOWN_ENGINE(mm, engine);
135}
#define TEST_CHECK(cond)
Definition acutest.h:95
#define TEST_ASSERT(cond)
Definition acutest.h:117
#define RELEASE_CONTEXT(x)
Definition base.h:48
#define SHUTDOWN_ENGINE(mm, engine)
Definition base.h:40
#define MAKE_ENGINE(mm, engine)
Definition base.h:34
#define MAKE_CONTEXT(engine, x)
Definition base.h:44
srn_metadata_t absurd_metadata
We use this for testing. Defined once in core.c.
Definition core.c:26
#define AS_SYMBOL(value_ref)
Definition core.h:155
@ VSymbol
Definition core.h:102
#define AS_STRING(value_ref)
Definition core.h:154
#define IS_A(value_ref, field)
Definition core.h:145
#define IS_TRUE(value_ref)
Definition core.h:146
srn_namespace_t * srn_namespace_get_or_create(srn_engine_t *engine, srn_string_t *name, srn_error_t **err)
Create a new and empty namespace if it is not already created in the engine, if it exists,...
Definition namespaces.c:30
A namespace is an isolation for names nad technically a compilation unit.
srn_value_t * srn_value_eq(const srn_context_t *ctx, const srn_value_t *a, const srn_value_t *b)
Check to values for equality. Return a boolean.
Definition protocols.c:38
srn_value_t * srn_string_make(srn_context_t *ctx, srn_metadata_t *metadata, const char *src)
Create a string from a null terminated C string.
Definition strings.c:51
bool srn_string_eq(const srn_string_t *a, const srn_string_t *b)
Definition strings.c:79
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:173
static void test_gensym_freshness()
static srn_namespace_t * make_test_ns(srn_context_t *ctx)
static void test_gensym()
static void test_symbol_intern()
static size_t sym_table_len(srn_namespace_t *ns)
static void test_symbol_make()
srn_value_t * srn_symbol_make(srn_context_t *ctx, srn_metadata_t *metadata, srn_namespace_t *ns, srn_string_t *name)
Create a new symbol. IT DOES NOT INTERNALIZE THE SYMBOL.
Definition symbols.c:30
srn_value_t * srn_symbol_intern(srn_context_t *ctx, srn_metadata_t *metadata, srn_namespace_t *ns, srn_string_t *symbol_name)
Definition symbols.c:45
srn_value_t * srn_symbol_gen(srn_context_t *ctx, srn_metadata_t *metadata, srn_namespace_t *ns, srn_string_t *optional_name)
Definition symbols.c:94