// Symbol Encoding Layout (32-bit UInt)
// [31..28] Tag [27..0] Payload
//
// 1. Short String (Tag is 0~4, and every char is fit in 7bit)
// length = Tag
// Payload = [c0, c1, c2, c3]
//
// 3. Generated Symbol (Tag is 5)
// Payload = global counter
//
// 2. Long String (Tag is 8~15)
// index = self & 0x7fff_ffff
// name = id_to_name[index]
//
// 4. Unuse (Tag = 6, 7)
//
// Decode: value < 0 ? Table : (tag < 5 ? Unpack : Gensym)
///|
/// A unique symbolic identifier providing O(1) equality, comparison, and hashing.
struct Symbol(UInt) derive(Eq, Hash, Compare, Default)
///|
pub impl Debug for Symbol with fn to_repr(self) {
Repr::opaque_("Symbol", Repr::string(self.to_string()))
}
///|
fn Symbol::tag(self : Symbol) -> UInt {
self.0 >> 28
}
///|
/// Creates a symbol from a string.
pub fn Symbol::of(name : String) -> Symbol {
let len = name.length()
if len > 4 {
put_in_table(name)
} else {
for i in 0.. Symbol {
name_to_sym.get_or_init(name, () => {
let id = id_to_name.length()
id_to_name.push(name)
Symbol(0x8000_0000 | id.reinterpret_as_uint())
})
}
///|
/// Global counter incremented to produce unique IDs for generated symbols.
let gen_counter : Ref[Int] = { val: -1 }
///|
/// Generates a unique symbolic identifier using a global counter.
pub fn Symbol::generate() -> Symbol {
gen_counter.val += 1
let id = gen_counter.val
guard! id < 0x0fff_ffff
Symbol(0x5000_0000 | gen_counter.val.reinterpret_as_uint())
}
///|
/// if this symbol was created via `generate()` rather than from a string.
pub fn Symbol::is_generated(self : Symbol) -> Bool {
self.tag() == 5
}
///|
pub impl Show for Symbol with fn output(self, logger) {
match self.tag() {
0..<5 as len =>
for i in len.reinterpret_as_int()>..0 {
let c = (self.0 >> (7 * i)) & 0x7f
logger.write_char(c.reinterpret_as_int().unsafe_to_char())
}
5 => logger.write_string("#{gensym \{self.0 & 0x0fff_ffff}}")
6 | 7 => panic()
8..<_ => {
let i = (self.0 & 0x7fff_ffff).reinterpret_as_int()
logger.write_string(id_to_name[i])
}
}
}
///|
test "inner" {
let names : Array[String] = [
"", "a", "ab", "abc", "abcd", "abcde", "abcdef", "δΈ",
]
let ss = names.map(Symbol::of) + [Symbol::generate(), Symbol::generate()]
inspect(
ss.map(x => x.0.to_string(radix=16)).join("\n"),
content=(
#|0
#|10000061
#|200030e2
#|30187163
#|4c38b1e4
#|80000000
#|80000001
#|80000002
#|50000000
#|50000001
),
)
}