use crate::exp::Exp;
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
sync::{LazyLock, RwLock, atomic::AtomicU64},
};
use string_interner::{DefaultStringInterner, DefaultSymbol};
static FRESH_COUNTER: AtomicU64 = AtomicU64::new(1);
fn fresh_u64() -> u64 {
FRESH_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
static INTERNER: LazyLock<RwLock<DefaultStringInterner>> =
LazyLock::new(|| RwLock::new(DefaultStringInterner::new()));
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(pub(crate) DefaultSymbol);
fn symbol_to_string(s: DefaultSymbol) -> String {
INTERNER.read().unwrap().resolve(s).unwrap().to_string()
}
fn intern(s: &str) -> DefaultSymbol {
INTERNER.write().unwrap().get_or_intern(s)
}
static EMPTY: LazyLock<Symbol> = LazyLock::new(|| Symbol(intern("")));
impl Symbol {
pub fn to_string(self) -> String {
symbol_to_string(self.0)
}
pub fn to_identifier(self) -> Symbol {
*RESERVED.get(&self).unwrap_or(&self)
}
pub fn intern(s: &str) -> Self {
Symbol(intern(s))
}
pub fn empty() -> Self {
*EMPTY
}
}
impl std::fmt::Debug for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", INTERNER.read().unwrap().resolve(self.0).unwrap())
}
}
#[cfg(feature = "serialize")]
impl Serialize for Symbol {
fn serialize<S: serde::Serializer>(&self, _serializer: S) -> Result<S::Ok, S::Error> {
todo! {}
}
}
#[cfg(feature = "serialize")]
impl<'d> Deserialize<'d> for Symbol {
fn deserialize<D: serde::Deserializer<'d>>(_deserializer: D) -> Result<Self, D::Error> {
todo! {}
}
}
impl From<Symbol> for String {
fn from(id: Symbol) -> Self {
symbol_to_string(id.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Ident {
pub(crate) name: Symbol,
src: Symbol,
id: u64,
}
impl Ident {
pub fn fresh(path: Symbol, name: impl AsRef<str>) -> Self {
Ident { name: name.as_ref().into(), src: path, id: fresh_u64() }
}
pub fn fresh_local(name: impl AsRef<str>) -> Self {
Self::fresh(Symbol::empty(), name)
}
pub fn refresh(&self) -> Self {
Ident { name: self.name, src: self.src, id: fresh_u64() }
}
pub fn refresh_with(&self, f: impl FnOnce(String) -> String) -> Self {
Ident { name: f(self.name.to_string()).as_str().into(), src: self.src, id: fresh_u64() }
}
pub fn stale(name: impl AsRef<str>) -> Self {
Ident { name: name.as_ref().into(), src: *EMPTY, id: 0 }
}
pub fn name(&self) -> Symbol {
self.name
}
pub fn src(&self) -> Symbol {
self.src
}
pub fn id(&self) -> u64 {
self.id
}
pub fn unsafe_build(name: &str, path: Symbol, id: u64) -> Self {
Ident { name: Symbol::intern(name), src: path, id }
}
}
impl From<&str> for Symbol {
fn from(s: &str) -> Self {
Symbol::intern(s)
}
}
impl From<Ident> for Symbol {
fn from(id: Ident) -> Self {
id.name
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct QName {
pub module: Box<[Symbol]>,
pub name: Symbol,
}
impl QName {
pub fn is_ident(&self, id: &Symbol) -> bool {
self.module.is_empty() && &self.name == id
}
pub fn as_ident(&self) -> Symbol {
assert!(self.module.is_empty());
self.name.clone()
}
pub fn without_search_path(mut self) -> QName {
self.module = self
.module
.into_iter()
.skip_while(|s| s.to_string().starts_with(char::is_lowercase))
.collect();
self
}
pub fn to_string(&self) -> String {
let mut s = String::new();
for i in self.module.iter() {
s.push_str(&i.to_string());
s.push('.');
}
s.push_str(&self.name.to_string());
s
}
pub fn parse(s: &str) -> Self {
let mut in_paren = false;
for (i, c) in s.char_indices().rev() {
match c {
')' => in_paren = true,
'(' => in_paren = false,
'.' => {
if !in_paren {
let name = Symbol::intern(&s[i + 1..]);
let module = s[..i].split('.').map(|s| Symbol::intern(s)).collect();
return QName { module, name };
}
}
_ => (),
}
}
QName { module: Box::new([]), name: Symbol::intern(s) }
}
pub fn unqual(s: &str) -> Self {
QName { module: Box::new([]), name: Symbol::intern(s) }
}
}
impl From<QName> for Exp {
fn from(q: QName) -> Self {
Exp::qvar(q)
}
}
impl<T: IntoIterator> From<T> for QName
where
T::Item: Into<Symbol>,
{
fn from(qname: T) -> Self {
let qname = qname.into_iter().map(|s| s.into()).collect::<Box<[_]>>();
let Some((&name, module)) = qname.split_last() else { panic!("Bad QName") };
QName { module: module.into(), name }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum Name {
Local(Ident, Option<Symbol>),
Global(QName),
}
impl Name {
pub fn local(ident: Ident) -> Self {
Name::Local(ident, None)
}
pub fn to_qname(self) -> QName {
match self {
Name::Local(_, _) => panic!("Cannot convert local name to QName"),
Name::Global(q) => q,
}
}
pub fn to_ident(self) -> Ident {
match self {
Name::Local(i, None) => i,
Name::Local(_, Some(_)) => panic!("Cannot convert local name with suffix to Ident"),
Name::Global(_) => panic!("Cannot convert global name to Ident"),
}
}
}
pub(crate) static TRUE: LazyLock<Symbol> = LazyLock::new(|| Symbol::intern("True"));
pub(crate) static FALSE: LazyLock<Symbol> = LazyLock::new(|| Symbol::intern("False"));
static RESERVED: LazyLock<HashMap<Symbol, Symbol>> = LazyLock::new(|| {
let mut interner = INTERNER.write().unwrap();
RESERVED_STR
.into_iter()
.map(|s| {
let s1 = Symbol(interner.get_or_intern(s));
let s2 = Symbol(interner.get_or_intern(format!("{}'", s)));
(s1, s2)
})
.collect()
});
const RESERVED_STR: &[&str] = &[
"bool", "int", "string", "real", "unit", "True", "False", "current", "final", "absurd",
"alias",
"any",
"assert",
"assume",
"at",
"axiom",
"break",
"by",
"check",
"clone",
"coinductive",
"constant",
"continue",
"diverges",
"do",
"done",
"else",
"end",
"ensures",
"epsilon",
"exception",
"exists",
"export",
"false",
"for",
"forall",
"fun",
"function",
"ghost",
"goal",
"if",
"import",
"inductive",
"invariant",
"label",
"lemma",
"let",
"match",
"meta",
"module",
"mutable",
"not",
"old",
"predicate",
"private",
"raise",
"reads",
"rec",
"requires",
"return",
"returns",
"scope",
"to",
"true",
"try",
"type",
"use",
"val",
"var",
"variant",
"while",
"with",
"writes",
];