use luau_common::{DenseHashHasher, DenseHashSet};
use std::hash::{Hash, Hasher};
use crate::allocator::AstArena;
#[derive(Debug, Clone, Copy)]
pub struct AstName<'ast> {
bytes: &'ast [u8],
}
impl<'ast> AstName<'ast> {
pub const fn empty_key() -> Self {
Self { bytes: b"" }
}
pub fn from_static(value: &'static str) -> Self {
Self {
bytes: value.as_bytes(),
}
}
pub fn bytes(self) -> &'ast [u8] {
self.bytes
}
pub(crate) fn from_bytes(bytes: &'ast [u8]) -> Self {
Self { bytes }
}
pub(crate) fn narrow<'short>(self) -> AstName<'short>
where
'ast: 'short,
{
AstName { bytes: self.bytes }
}
}
impl PartialEq for AstName<'_> {
fn eq(&self, other: &Self) -> bool {
self.bytes.len() == other.bytes.len()
&& std::ptr::eq(self.bytes.as_ptr(), other.bytes.as_ptr())
}
}
impl Eq for AstName<'_> {}
impl Hash for AstName<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
let key = self.bytes.as_ptr() as usize;
((key >> 4) ^ (key >> 9)).hash(state);
}
}
impl PartialEq<&str> for AstName<'_> {
fn eq(&self, other: &&str) -> bool {
self.bytes == other.as_bytes()
}
}
impl PartialEq<str> for AstName<'_> {
fn eq(&self, other: &str) -> bool {
self.bytes == other.as_bytes()
}
}
impl PartialOrd for AstName<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AstName<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bytes.cmp(other.bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LexemeType {
Eof,
Name,
Attribute,
ReservedAnd,
ReservedBreak,
ReservedDo,
ReservedElse,
ReservedElseif,
ReservedEnd,
ReservedFalse,
ReservedFor,
ReservedFunction,
ReservedIf,
ReservedIn,
ReservedLocal,
ReservedNil,
ReservedNot,
ReservedOr,
ReservedRepeat,
ReservedReturn,
ReservedThen,
ReservedTrue,
ReservedUntil,
ReservedWhile,
}
#[derive(Debug)]
pub struct AstNameTable<'ast> {
entries: DenseHashSet<NameEntry<'ast>, NameEntryHasher>,
arena: &'ast AstArena,
}
impl<'ast> AstNameTable<'ast> {
pub fn new(arena: &'ast AstArena) -> Self {
let mut table = Self {
entries: DenseHashSet::with_buckets(NameEntry::empty(), 128),
arena,
};
for &(name, kind) in RESERVED {
table.add_static(name, kind);
}
table
}
pub fn add_static(&mut self, name: &'static str, kind: LexemeType) -> AstName<'ast> {
let bytes = name.as_bytes();
let entry = self.entries.insert_mut(NameEntry::interned(bytes, kind));
debug_assert!(entry.kind == kind);
entry.name
}
pub fn get_or_add_with_type(&mut self, name: &str) -> (AstName<'ast>, LexemeType) {
self.get_or_add_bytes_with_type(name.as_bytes())
}
pub fn get_or_add_bytes_with_type(&mut self, name: &[u8]) -> (AstName<'ast>, LexemeType) {
let entry = self.entries.insert_mut(NameEntry::lookup(name));
if entry.kind == LexemeType::Eof {
let interned = self.arena.alloc_bytes(name);
*entry = NameEntry::owned(interned);
}
(entry.name, entry.kind)
}
pub fn get_with_type(&self, name: &str) -> Option<(AstName<'ast>, LexemeType)> {
self.get_bytes_with_type(name.as_bytes())
}
pub fn get_bytes_with_type(&self, name: &[u8]) -> Option<(AstName<'ast>, LexemeType)> {
self.entries
.get(&NameEntry::lookup(name))
.map(|entry| (entry.name, entry.kind))
}
pub fn get_or_add(&mut self, name: &str) -> AstName<'ast> {
self.get_or_add_with_type(name).0
}
pub fn get_or_add_bytes(&mut self, name: &[u8]) -> AstName<'ast> {
self.get_or_add_bytes_with_type(name).0
}
pub fn get(&self, name: &str) -> Option<AstName<'ast>> {
self.get_with_type(name).map(|(name, _)| name)
}
pub fn get_bytes(&self, name: &[u8]) -> Option<AstName<'ast>> {
self.get_bytes_with_type(name).map(|(name, _)| name)
}
pub fn arena(&self) -> &'ast AstArena {
self.arena
}
}
#[derive(Debug, Clone, Copy)]
struct NameEntry<'ast> {
key: NameKey,
name: AstName<'ast>,
kind: LexemeType,
}
#[derive(Debug, Clone, Copy, Default)]
struct NameKey {
bytes: *const u8,
len: u32,
}
impl NameKey {
fn empty() -> Self {
Self {
bytes: std::ptr::null(),
len: 0,
}
}
fn new(bytes: &[u8]) -> Self {
Self {
bytes: bytes.as_ptr(),
len: u32::try_from(bytes.len()).expect("name length must fit u32"),
}
}
fn equals(self, other: Self) -> bool {
if self.len != other.len {
return false;
}
if self.bytes == other.bytes || self.len == 0 {
return true;
}
let len = self.len as usize;
for i in 0..len {
let left = unsafe { *self.bytes.add(i) };
let right = unsafe { *other.bytes.add(i) };
if left != right {
return false;
}
}
true
}
}
#[derive(Debug, Clone, Copy)]
struct NameEntryHasher;
impl<'ast> NameEntry<'ast> {
fn empty() -> Self {
Self {
key: NameKey::empty(),
name: AstName::from_static(""),
kind: LexemeType::Eof,
}
}
fn lookup(bytes: &[u8]) -> Self {
Self {
key: NameKey::new(bytes),
name: AstName::empty_key(),
kind: LexemeType::Eof,
}
}
fn interned(bytes: &'ast [u8], kind: LexemeType) -> Self {
Self {
key: NameKey::new(bytes),
name: AstName::from_bytes(bytes),
kind,
}
}
fn owned(bytes: &'ast [u8]) -> Self {
Self::interned(
bytes,
if bytes.first() == Some(&b'@') {
LexemeType::Attribute
} else {
LexemeType::Name
},
)
}
}
impl PartialEq for NameKey {
fn eq(&self, other: &Self) -> bool {
self.equals(*other)
}
}
impl Eq for NameKey {}
impl PartialEq for NameEntry<'_> {
fn eq(&self, other: &Self) -> bool {
self.key == other.key
}
}
impl Eq for NameEntry<'_> {}
#[derive(Debug, Clone, Copy)]
pub struct AstNameDenseHasher;
impl DenseHashHasher<AstName<'_>> for AstNameDenseHasher {
fn hash(key: &AstName<'_>) -> u64 {
let key = key.bytes.as_ptr() as usize;
((key >> 4) ^ (key >> 9)) as u64
}
}
impl DenseHashHasher<NameEntry<'_>> for NameEntryHasher {
fn hash(entry: &NameEntry<'_>) -> u64 {
name_hash(entry.key.bytes, entry.key.len as usize)
}
}
fn name_hash(bytes: *const u8, len: usize) -> u64 {
const FNV_OFFSET_BASIS: u32 = 2_166_136_261;
const FNV_PRIME: u32 = 16_777_619;
let mut hash = FNV_OFFSET_BASIS;
for i in 0..len {
let byte = unsafe { *bytes.add(i) };
hash ^= u32::from(byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
u64::from(hash)
}
const RESERVED: &[(&str, LexemeType)] = &[
("and", LexemeType::ReservedAnd),
("break", LexemeType::ReservedBreak),
("do", LexemeType::ReservedDo),
("else", LexemeType::ReservedElse),
("elseif", LexemeType::ReservedElseif),
("end", LexemeType::ReservedEnd),
("false", LexemeType::ReservedFalse),
("for", LexemeType::ReservedFor),
("function", LexemeType::ReservedFunction),
("if", LexemeType::ReservedIf),
("in", LexemeType::ReservedIn),
("local", LexemeType::ReservedLocal),
("nil", LexemeType::ReservedNil),
("not", LexemeType::ReservedNot),
("or", LexemeType::ReservedOr),
("repeat", LexemeType::ReservedRepeat),
("return", LexemeType::ReservedReturn),
("then", LexemeType::ReservedThen),
("true", LexemeType::ReservedTrue),
("until", LexemeType::ReservedUntil),
("while", LexemeType::ReservedWhile),
];