Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
symbols.c
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 library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 library 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include "serene/rt/symbols.h"
20
21#include "serene/rt/context.h"
22#include "serene/rt/engine.h"
23#include "serene/rt/errors.h"
27#include "serene/rt/strings.h"
28#include "serene/utils.h"
29
32) {
33 PANIC_IF_NULL(ctx);
34 PANIC_IF_NULL(ns);
35 PANIC_IF_NULL(name);
36
37 srn_symbol_t *sym = ALLOC(ctx, srn_symbol_t);
38 sym->name = srn_string_copy(ctx, name);
39
40 sym->ns = ns;
41
42 return srn_value_make(ctx, VSymbol, metadata, (void *)sym);
43}
44
46 srn_context_t *ctx, srn_metadata_t *metadata, srn_namespace_t *ns, srn_string_t *symbol_name
47) {
48 PANIC_IF_NULL(ctx);
49 PANIC_IF_NULL(ns);
50 PANIC_IF_NULL(symbol_name);
51
52 if (srn_string_is_empty(symbol_name)) {
53 return srn_errors_make_error(ctx, metadata, EMPTY_SYMBOL_NAME, nullptr);
54 };
55
56 hmap_key_t k = {.data = &symbol_name->buffer, .len = symbol_name->size};
57
58 // Every published table head is immutable, so readers only need an acquire
59 // load and never take the lock.
60 hmap_t *table = atomic_load_explicit(&ns->sym_table, memory_order_acquire);
61 void *result = hmap_lookup(table, &k, nullptr);
62 if (result != nullptr) {
63 return (srn_value_t *)result;
64 }
65
66 srn_value_t *sym = nullptr;
67
68 // The lock covers the lookup as well as the insert. Two interners racing
69 // on the same new name would otherwise both miss and insert two distinct
70 // symbols for one name, and symbol equality is pointer identity.
72 // Another interner may have added the name between the unlocked lookup
73 // and taking the lock, so the lookup is repeated here.
74 table = atomic_load_explicit(&ns->sym_table, memory_order_relaxed);
75 sym = hmap_lookup(table, &k, nullptr);
76
77 if (sym == nullptr) {
78 // The table retains the symbol for the namespace's lifetime, so the
79 // value and its name copy are allocated through the namespace's root
80 // context, not the interning caller's transient one.
81 sym = srn_symbol_make(ns->root_context, metadata, ns, symbol_name);
82
83 // The old head stays valid for readers that already loaded it, so the
84 // new version gets its own allocation.
85 hmap_t *next = ALLOC_OR_PANIC(ns->root_context, hmap_t);
86 *next = hmap_insert(table, &k, (void *)sym);
87 atomic_store_explicit(&ns->sym_table, next, memory_order_release);
88 }
89 });
90
91 return sym;
92}
93
95 srn_context_t *ctx, srn_metadata_t *metadata, srn_namespace_t *ns, srn_string_t *optional_name
96) {
97 // TODO(lxsameer): Fold the optional name into the generated spelling.
98 UNUSED(optional_name);
99 PANIC_IF_NULL(ctx);
100 PANIC_IF_NULL(ns);
101
102 // A generated symbol must be fresh, a symbol nothing else already names.
103 // The spelling is readable text, so a user symbol can occupy it. When the
104 // name is taken, a new id gives a new name to try. The lock covers the
105 // lookup and the insert, the pairing srn_symbol_intern relies on too.
106 for (;;) {
108
109 // Calculate the length
110 int len = snprintf(nullptr, 0, "gensym_%" PRIu64, id);
111 PANIC_IF(len < 0, "This should never happen!");
112
113 // Including the null terminator
114 char *name = srn_allocate(ctx, len + 1, alignof(char));
115 (void)snprintf(name, len + 1, "gensym_%" PRIu64, id);
116
117 srn_value_t *sym_name = srn_string_make(ctx, metadata, name);
118 hmap_key_t k = {.data = &AS_STRING(sym_name)->buffer, .len = AS_STRING(sym_name)->size};
119
120 srn_value_t *sym = nullptr;
121
123 hmap_t *table = atomic_load_explicit(&ns->sym_table, memory_order_relaxed);
124
125 if (hmap_lookup(table, &k, nullptr) == nullptr) {
126 // The table retains the symbol for the namespace's lifetime, so the
127 // value and its name copy are allocated through the namespace's root
128 // context, not the generating caller's transient one.
129 sym = srn_symbol_make(ns->root_context, metadata, ns, AS_STRING(sym_name));
130
131 hmap_t *next = ALLOC_OR_PANIC(ns->root_context, hmap_t);
132 *next = hmap_insert(table, &k, (void *)sym);
133 atomic_store_explicit(&ns->sym_table, next, memory_order_release);
134 }
135 });
136
137 // A null `sym` means the spelling was taken, so the loop tries the next id.
138 if (sym != nullptr) {
139 return sym;
140 }
141 }
142}
void * srn_allocate(const srn_context_t *ctx, size_t size, size_t alignment)
Allocate size bytes aligned to alignment from the context's memory block.
Definition context.c:73
#define ALLOC(ctx, T)
Definition context.h:95
srn_value_t * srn_value_make(srn_context_t *ctx, srn_value_tag_t tag, srn_metadata_t *metadata, void *payload)
Creates a new serene value.
Definition core.c:36
@ VSymbol
Definition core.h:102
#define AS_STRING(value_ref)
Definition core.h:154
srn_object_id_t srn_allocate_object_id(srn_engine_t *engine)
Definition engine.c:193
uint64_t srn_object_id_t
Definition engine.h:45
Error handling for the runtime.
@ EMPTY_SYMBOL_NAME
Definition errors.h:109
void * hmap_lookup(const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Lookup the given k in the given hmap and return the value if it's been found.
Definition hashmap.c:665
This is an implementation of Compressed Hash-Array Mapped Prefix-tree, which is a bit-partitioned,...
A fiber blocking mutex.
#define WITH_LOCK(lock, body)
Run body with lock held.
Definition mutex.h:84
A namespace is an isolation for names nad technically a compilation unit.
const size_t len
auto result
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_is_empty(const srn_string_t *s)
Definition strings.c:49
srn_string_t * srn_string_copy(srn_context_t *ctx, const srn_string_t *src)
Allocate a copy of src through ctx and return it.
Definition strings.c:39
Note: For key equality we use the memcpy function.
Definition hashmap.h:66
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
srn_fiber_mutex_t * sym_table_lock
Only when setting the sym_table hmap it self.
Definition namespaces.h:52
size_t size
Size of the buffer.
Definition strings.h:62
uint8_t buffer[]
The buffer that holds the WTF8 sequence.
Definition strings.h:66
srn_namespace_t * ns
Definition symbols.h:32
srn_string_t * name
Definition symbols.h:31
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
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define PANIC_IF(cond, msg)
Definition utils.h:59
#define UNUSED(x)
Definition utils.h:45