Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
protocols.h File Reference
#include "serene/rt/engine.h"
Include dependency graph for protocols.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

srn_value_tsrn_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.
srn_hash_t srn_value_hash (const srn_context_t *ctx, const srn_value_t *v)
 Compute the xxHash32 of a value using the engine seed.
bool srn_value_hashable (const srn_value_t *v)
 Whether v can be a hash key.

Function Documentation

◆ srn_value_eq()

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 at line 38 of file protocols.c.

38 {
39 PANIC_IF_NULL(ctx);
40 if (a == nullptr || b == nullptr || (a->type != b->type)) {
41 return &false_v;
42 }
43
44 switch (a->type) {
45 case VNil:
46 case VTrue:
47 case VFalse:
48 // Tag-only values; matching tags means equal.
49 return &true_v;
50
51 case VI64:
52 return equality_to_value(AS_I64(a) == AS_I64(b));
53
54 case VF64:
55 // IEEE compare: NaN != NaN, +0.0 == -0.0. Hash normalizes -0.0 -> +0.0
56 // so the eq <=> hash invariant holds.
57 return equality_to_value(AS_F64(a) == AS_F64(b));
58
59 case VString:
61
62 case VSymbol:
63 // Symbols are interned per namespace; pointer identity is enough.
64 return equality_to_value(AS_SYMBOL(a) == AS_SYMBOL(b));
65
66 case VKeyword:
67 // Keywords are interned engine-wide; pointer identity is enough.
69
70 case VNamespace:
71 // Namespaces are unique per engine; pointer identity suffices.
72 return equality_to_value(AS_NS(a) == AS_NS(b));
73
74 case VList: {
75 srn_list_t *la = AS_LIST(a);
76 srn_list_t *lb = AS_LIST(b);
77 if (la->len != lb->len) {
78 return &false_v;
79 }
80 srn_list_node_t *na = la->head;
81 srn_list_node_t *nb = lb->head;
82 while (na != nullptr) {
83 if (srn_value_eq(ctx, na->value, nb->value) == &false_v) {
84 return &false_v;
85 }
86 na = na->next;
87 nb = nb->next;
88 }
89 return &true_v;
90 }
91
92 case VSeq: {
93 srn_seq_t *sa = AS_SEQ(a);
94 srn_seq_t *sb = AS_SEQ(b);
95
96 if (sa->inner.len != sb->inner.len) {
97 return &false_v;
98 }
99 for (size_t i = 0; i < sa->inner.len; i++) {
100 seq_lookup_result_t ra = seq_get(&sa->inner, i);
101 seq_lookup_result_t rb = seq_get(&sb->inner, i);
102 // A lookup error inside the length is corruption, the same condition
103 // the hash path panics on. Returning false here instead would make a
104 // corrupted seq compare unequal to itself with no diagnostic.
105 PANIC_IF(ra.maybe_error != nullptr || rb.maybe_error != nullptr, "Corrupted seq during eq");
106 if (srn_value_eq(ctx, (srn_value_t *)ra.data, (srn_value_t *)rb.data) == &false_v) {
107 return &false_v;
108 }
109 }
110 return &true_v;
111 }
112
113 case VMap:
114 // Identity equality for now. Two maps compare equal iff they share the
115 // same underlying CHAMT instance. Structural map equality is parked until
116 // there's a real need (and a corresponding hmap walker).
117 return equality_to_value(AS_MAP(a) == AS_MAP(b));
118
119 case VClosure:
120 return equality_to_value(AS_CLOSURE(a) == AS_CLOSURE(b));
121
122 case VError:
123 return equality_to_value(AS_ERROR(a) == AS_ERROR(b));
124
125 case VQuote:
126 TODO("VQuote payload is not defined yet");
127
128 default:
129 PANIC("Unknown value tag in srn_value_eq");
130 }
131}
srn_value_t false_v
Definition core.c:29
srn_value_t true_v
Definition core.c:28
#define AS_LIST(value_ref)
Definition core.h:179
#define AS_F64(value_ref)
Definition core.h:173
#define AS_SYMBOL(value_ref)
Definition core.h:177
@ VQuote
Definition core.h:120
@ VSeq
Definition core.h:119
@ VNamespace
Definition core.h:122
@ VClosure
Definition core.h:121
@ VNil
Definition core.h:113
@ VList
Definition core.h:118
@ VFalse
Definition core.h:115
@ VF64
Definition core.h:117
@ VSymbol
Definition core.h:124
@ VI64
Definition core.h:116
@ VMap
Definition core.h:126
@ VError
VError should be last.
Definition core.h:128
@ VKeyword
Definition core.h:125
@ VTrue
Definition core.h:114
@ VString
Definition core.h:123
#define AS_STRING(value_ref)
Definition core.h:176
#define AS_NS(value_ref)
Definition core.h:175
#define AS_I64(value_ref)
Definition core.h:172
#define AS_MAP(value_ref)
Definition core.h:182
#define AS_SEQ(value_ref)
Definition core.h:181
#define AS_ERROR(value_ref)
Definition core.h:178
#define AS_CLOSURE(value_ref)
Definition core.h:174
#define AS_KEYWORD(value_ref)
Definition core.h:180
static srn_value_t * equality_to_value(bool cond)
Definition protocols.c:36
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
seq_lookup_result_t seq_get(const seq_t *seq, size_t n)
Negative index is not supported.
Definition seq.c:205
bool srn_string_eq(const srn_string_t *a, const srn_string_t *b)
Definition strings.c:79
seq_elem_t data
Definition seq.h:135
size_t len
logical length.
Definition seq.h:161
A list is a singly linked sequence of values.
Definition lists.h:30
struct srn_list_node_t * next
Definition lists.h:32
srn_value_t * value
Definition lists.h:31
srn_list_node_t * head
Definition lists.h:37
size_t len
Definition lists.h:36
A persistent, immutable, indexed sequence.
Definition seqs.h:27
seq_t inner
Definition seqs.h:28
srn_value_tag_t type
Definition core.h:132
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define PANIC_IF(cond, msg)
Definition utils.h:59
#define TODO(msg)
Definition utils.h:54
#define PANIC(msg)
Definition utils.h:53
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_value_hash()

srn_hash_t srn_value_hash ( const srn_context_t * ctx,
const srn_value_t * v )

Compute the xxHash32 of a value using the engine seed.

The invariant srn_value_eq(a, b) == true_v => srn_value_hash(a) == srn_value_hash(b) must hold for every tag.

Definition at line 152 of file protocols.c.

152 {
153 PANIC_IF_NULL(ctx);
154 PANIC_IF_NULL(v);
155
156 srn_engine_t *engine = ctx->engine;
157
158 switch (v->type) {
159 case VNil: {
160 return 0;
161 }
162 case VTrue: {
163 return 1;
164 }
165 case VFalse: {
166 return 2;
167 }
168
169 case VI64: {
170 int64_t n = AS_I64(v);
171 return srn_hash(engine, &n, sizeof(n));
172 }
173
174 case VF64: {
175 double d = AS_F64(v);
176 // If you find this weird is hell, you're not alone but apparently +0.0 and -0.0 compare equal
177 // under IEEE rules but differ in the sign bit. So we have to normalize so the eq -> hash
178 // invariant holds.
179 // https://en.wikipedia.org/wiki/Signed_zero
180 // https://docs.rs/fix_float
181 if (d == 0.0) {
182 d = 0.0;
183 }
184 return srn_hash(engine, &d, sizeof(d));
185 }
186
187 case VString: {
188 srn_string_t *s = AS_STRING(v);
189 return srn_hash(engine, s->buffer, s->len);
190 }
191
192 case VSymbol: {
193 // Interned identity, hash the pointer bits.
194 srn_symbol_t *sym = AS_SYMBOL(v);
195 return srn_hash(engine, sym, sizeof(*sym));
196 }
197
198 case VKeyword: {
199 // Interned; hash the pointer-sized payload (the name pointer).
201 return srn_hash(engine, k, sizeof(*k));
202 }
203
204 case VNamespace: {
205 srn_namespace_t *ns = AS_NS(v);
206 return srn_hash(engine, ns->name->buffer, ns->name->len);
207 }
208
209 case VList:
210 // Lists are intentionally not hashable; they can't be used as map keys.
211 // Use a seq or other container if you need a hashable sequence.
212 PANIC("Lists are not hashable");
213
214 case VSeq: {
215 srn_seq_t *s = AS_SEQ(v);
216 srn_hash_t h = srn_hash(engine, &s->inner.len, sizeof(s->inner.len));
217
218 for (size_t i = 0; i < s->inner.len; i++) {
220 if (r.maybe_error != nullptr) {
221 PANIC("Corrupted seq during hash");
222 }
224 h = combine2(engine, h, hi);
225 }
226 return h;
227 }
228
229 case VMap: {
230 PANIC("Hashmaps are not hashable");
231 }
232
233 case VClosure: {
234 PANIC("Closures are not hashable");
235 }
236
237 case VError: {
238 srn_error_t *e = AS_ERROR(v);
239 return srn_hash(engine, e, sizeof(*e));
240 }
241
242 case VQuote:
243 TODO("VQuote payload is not defined yet");
244
245 default:
246 PANIC("Unknown value tag in srn_value_hash");
247 }
248}
int n
Definition acutest.h:525
SRN_HASH_TYPE srn_hash_t
Definition context.h:44
srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len)
Definition engine.c:166
srn_hash_t srn_value_hash(const srn_context_t *ctx, const srn_value_t *v)
Compute the xxHash32 of a value using the engine seed.
Definition protocols.c:152
static srn_hash_t combine2(srn_engine_t *engine, srn_hash_t a, srn_hash_t b)
Definition protocols.c:134
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
Engine is a structure to own the long living and main pieces of the compiler.
Definition engine.h:51
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:125
A keyword, just a name.
Definition keywords.h:29
srn_string_t * name
Definition namespaces.h:45
uint8_t buffer[]
The buffer that holds the WTF8 sequence.
Definition strings.h:66
size_t len
length of the WTF-8 sequence in bytes
Definition strings.h:64
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_value_hashable()

bool srn_value_hashable ( const srn_value_t * v)
nodiscard

Whether v can be a hash key.

srn_value_hash panics on the container and closure tags, so anything keying a map must pass this first and fail as an error value instead.

Definition at line 139 of file protocols.c.

139 {
140 PANIC_IF_NULL(v);
141 switch (v->type) {
142 case VList:
143 case VMap:
144 case VClosure:
145 case VQuote:
146 return false;
147 default:
148 return true;
149 }
150}
Here is the caller graph for this function: