Files
coolguy b0cc737c6b GOAL P3-5: StrId 를 키로 -- 별도 IntMap 은 필요 없다
std.map 은 바이트 열을 키로 받는다. 이름의 번호를 네 바이트로 써 내려놓으면
그대로 심볼 표가 된다. 감사가 권한 IntMap(V) 를 따로 만들 이유가 없고, 그
쪽이 trait 없는 v0.1 과도 덜 싸운다.

intern.key_of 가 그 네 바이트를 써준다. 리졸버가 스코프마다 할 일이라
손으로 풀게 두지 않았다.

  scope x 10 y 20 of 2

245/245, 38/38.
2026-08-17 16:28:47 +09:00

143 lines
4.9 KiB
Plaintext

unit std.intern;
import std.map;
import std.mem;
// One copy of every distinct name, and a number that stands for it.
//
// A compiler compares names constantly and stores them everywhere. Comparing
// two `StrId` is comparing two integers; storing one costs four bytes and no
// ownership. That is the whole point.
//
// There is deliberately no way to get a `str` back out. A borrow of the text
// would be a borrow of the interner, and the interner is exactly the thing you
// want to keep adding to while holding names -- the parser reads an identifier
// and registers the next one in the same breath. Everything you would open the
// text for is here instead: compare it, measure it, hash it, write it.
/// A name, as a number. Copy, four bytes, and meaningless to any other
/// interner -- which is fine, because a program has one.
pub struct StrId {
pub raw: u32,
pub fn same(self: &Self, other: StrId) -> bool {
return self.raw == other.raw;
}
}
/// No name.
pub const NONE: u32 = 4294967295;
/// A name written out as map key bytes. `std.map` keys on bytes, so a name
/// used as a key is just its number -- four of them, and nothing allocated.
/// A symbol table is `Map(V)` keyed on this.
pub fn key_of(id: StrId, out: []mut u8) -> []u8 {
out[0] = (id.raw % 256) as u8;
out[1] = ((id.raw / 256) % 256) as u8;
out[2] = ((id.raw / 65536) % 256) as u8;
out[3] = ((id.raw / 16777216) % 256) as u8;
return out[0..4];
}
struct Entry {
at: usize,
len: usize,
}
pub struct Interner {
/// Every name end to end. Nothing is ever removed, so an offset stays
/// good for as long as the interner does.
bytes: ^[]mut u8,
used: usize,
names: ^[]mut Entry,
count: usize,
/// Text to id, so interning the same name twice gives the same number.
seen: map.Map(u32),
pub fn with_capacity(n: usize) -> !Self {
let text: ^[]mut u8 = try mem.alloc_slice(u8, n * 8);
let table: ^[]mut Entry = try mem.alloc_slice(Entry, n);
let index: map.Map(u32) = try map.Map(u32).with_capacity(n);
return Self{ bytes: text, used: 0, names: table, count: 0,
seen: index };
}
pub fn count_of(self: &Self) -> usize { return self.count; }
/// The number for this name, making one if it is new.
pub fn intern(self: &mut Self, text: []u8) -> !StrId {
let found: u32 = self.seen.get(text, NONE);
if found != NONE { return StrId{ raw: found }; }
if self.count == self.names.^.n { return error.OutOfMemory; }
let at: usize = self.used;
if at + text.n > self.bytes.^.n { return error.OutOfMemory; }
var i: usize = 0;
while i < text.n {
self.bytes.^[at + i] = text[i];
i = i + 1;
}
let id: usize = self.count;
self.names.^[id].at = at;
self.names.^[id].len = text.n;
self.used = at + text.n;
self.count = id + 1;
try self.seen.put(text, id as u32);
return StrId{ raw: id as u32 };
}
/// Is this a name it has seen? `NONE` when not.
pub fn find(self: &Self, text: []u8) -> u32 {
return self.seen.get(text, NONE);
}
pub fn len_of(self: &Self, id: StrId) -> usize {
if (id.raw as usize) >= self.count { return 0; }
return self.names.^[id.raw as usize].len;
}
/// Does this id spell this text? The comparison every `if name == "fn"`
/// in a parser turns into.
pub fn eq(self: &Self, id: StrId, text: []u8) -> bool {
if (id.raw as usize) >= self.count { return false; }
let e: usize = id.raw as usize;
if self.names.^[e].len != text.n { return false; }
var i: usize = 0;
while i < text.n {
if self.bytes.^[self.names.^[e].at + i] != text[i] { return false; }
i = i + 1;
}
return true;
}
/// FNV-1a over the stored bytes, for anything that wants to bucket names
/// without opening them.
pub fn hash_of(self: &Self, id: StrId) -> u32 {
if (id.raw as usize) >= self.count { return 0; }
let e: usize = id.raw as usize;
var h: u32 = 2166136261;
var i: usize = 0;
while i < self.names.^[e].len {
h = h ^ (self.bytes.^[self.names.^[e].at + i] as u32);
h = h * 16777619;
i = i + 1;
}
return h;
}
/// Copy the name into `out` and say how many bytes it took. This is how a
/// name reaches a diagnostic without the interner being borrowed past the
/// statement.
pub fn copy_into(self: &Self, id: StrId, out: []mut u8) -> usize {
if (id.raw as usize) >= self.count { return 0; }
let e: usize = id.raw as usize;
var n: usize = self.names.^[e].len;
if n > out.n { n = out.n; }
var i: usize = 0;
while i < n {
out[i] = self.bytes.^[self.names.^[e].at + i];
i = i + 1;
}
return n;
}
}