#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(transparent)]
pub struct SymbolId(pub u16);
impl SymbolId {
pub const ANONYMOUS: Self = Self(0xFFFF);
#[inline]
pub const fn new(id: u16) -> Self {
Self(id)
}
#[inline]
pub const fn is_anonymous(self) -> bool {
self.0 == 0xFFFF
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Category {
Omega = 0,
Gamma = 1,
Forall = 2,
Delta = 3,
Domain = 4,
Psi = 5,
Block = 6,
Reserved = 7,
}
impl Category {
pub const fn from_id(id: SymbolId) -> Self {
match id.0 {
0..=63 => Self::Omega,
64..=127 => Self::Gamma,
128..=191 => Self::Forall,
192..=255 => Self::Delta,
256..=319 => Self::Domain,
320..=383 => Self::Psi,
384..=447 => Self::Block,
_ => Self::Reserved,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Symbol {
pub glyph: [u8; 4],
pub len: u8,
pub category: Category,
pub id: u16,
}
pub static AISP_SYMBOLS: &[Symbol] = &[
Symbol { glyph: *b"\xE2\x8A\xA4\x00", len: 3, category: Category::Omega, id: 0 }, Symbol { glyph: *b"\xE2\x8A\xA5\x00", len: 3, category: Category::Omega, id: 1 }, Symbol { glyph: *b"\xE2\x88\xA7\x00", len: 3, category: Category::Omega, id: 2 }, Symbol { glyph: *b"\xE2\x88\xA8\x00", len: 3, category: Category::Omega, id: 3 }, Symbol { glyph: *b"\xC2\xAC\x00\x00", len: 2, category: Category::Omega, id: 4 }, Symbol { glyph: *b"\xE2\x86\x92\x00", len: 3, category: Category::Omega, id: 5 }, Symbol { glyph: *b"\xE2\x86\x94\x00", len: 3, category: Category::Omega, id: 6 }, Symbol { glyph: *b"\xE2\x87\x92\x00", len: 3, category: Category::Omega, id: 7 }, Symbol { glyph: *b"\xE2\x87\x94\x00", len: 3, category: Category::Omega, id: 8 }, Symbol { glyph: *b"\xE2\x8A\xA2\x00", len: 3, category: Category::Omega, id: 9 }, Symbol { glyph: *b"\xE2\x8A\xA8\x00", len: 3, category: Category::Omega, id: 10 }, Symbol { glyph: *b"\xE2\x89\x9C\x00", len: 3, category: Category::Omega, id: 11 }, Symbol { glyph: *b"\xE2\x89\x94\x00", len: 3, category: Category::Omega, id: 12 }, Symbol { glyph: *b"\xE2\x89\xA1\x00", len: 3, category: Category::Omega, id: 13 }, Symbol { glyph: *b"\xE2\x89\xA2\x00", len: 3, category: Category::Omega, id: 14 }, Symbol { glyph: *b"\xCE\xBB\x00\x00", len: 2, category: Category::Omega, id: 15 }, Symbol { glyph: *b"\xE2\x88\x8E\x00", len: 3, category: Category::Omega, id: 16 }, Symbol { glyph: *b"\xE2\x88\x98\x00", len: 3, category: Category::Omega, id: 17 }, Symbol { glyph: *b"\xE2\x86\xA6\x00", len: 3, category: Category::Omega, id: 18 },
Symbol { glyph: *b"\xE2\x88\x88\x00", len: 3, category: Category::Gamma, id: 64 }, Symbol { glyph: *b"\xE2\x88\x89\x00", len: 3, category: Category::Gamma, id: 65 }, Symbol { glyph: *b"\xE2\x8A\x86\x00", len: 3, category: Category::Gamma, id: 66 }, Symbol { glyph: *b"\xE2\x8A\x87\x00", len: 3, category: Category::Gamma, id: 67 }, Symbol { glyph: *b"\xE2\x88\xA9\x00", len: 3, category: Category::Gamma, id: 68 }, Symbol { glyph: *b"\xE2\x88\xAA\x00", len: 3, category: Category::Gamma, id: 69 }, Symbol { glyph: *b"\xE2\x88\x85\x00", len: 3, category: Category::Gamma, id: 70 }, Symbol { glyph: *b"\xF0\x9D\x92\xAB", len: 4, category: Category::Gamma, id: 71 },
Symbol { glyph: *b"\xE2\x88\x80\x00", len: 3, category: Category::Forall, id: 128 }, Symbol { glyph: *b"\xE2\x88\x83\x00", len: 3, category: Category::Forall, id: 129 }, Symbol { glyph: *b"\xCE\xA0\x00\x00", len: 2, category: Category::Forall, id: 130 }, Symbol { glyph: *b"\xCE\xA3\x00\x00", len: 2, category: Category::Forall, id: 131 }, Symbol { glyph: *b"\xE2\x8A\x95\x00", len: 3, category: Category::Forall, id: 132 }, Symbol { glyph: *b"\xE2\x8A\x96\x00", len: 3, category: Category::Forall, id: 133 }, Symbol { glyph: *b"\xE2\x8A\x97\x00", len: 3, category: Category::Forall, id: 134 }, Symbol { glyph: *b"\xE2\x97\x8A\x00", len: 3, category: Category::Forall, id: 135 },
Symbol { glyph: *b"\xE2\x84\x95\x00", len: 3, category: Category::Domain, id: 256 }, Symbol { glyph: *b"\xE2\x84\xA4\x00", len: 3, category: Category::Domain, id: 257 }, Symbol { glyph: *b"\xE2\x84\x9D\x00", len: 3, category: Category::Domain, id: 258 }, Symbol { glyph: *b"\xE2\x84\x9A\x00", len: 3, category: Category::Domain, id: 259 }, Symbol { glyph: *b"\xF0\x9D\x94\xB9", len: 4, category: Category::Domain, id: 260 }, Symbol { glyph: *b"\xF0\x9D\x95\x8A", len: 4, category: Category::Domain, id: 261 },
Symbol { glyph: *b"\xE2\x9F\xA6\x00", len: 3, category: Category::Block, id: 384 }, Symbol { glyph: *b"\xE2\x9F\xA7\x00", len: 3, category: Category::Block, id: 385 }, Symbol { glyph: *b"\xE2\x9F\xA8\x00", len: 3, category: Category::Block, id: 386 }, Symbol { glyph: *b"\xE2\x9F\xA9\x00", len: 3, category: Category::Block, id: 387 }, Symbol { glyph: *b"\xF0\x9D\x94\xB8", len: 4, category: Category::Block, id: 388 },
Symbol { glyph: *b"\xCE\xA9\x00\x00", len: 2, category: Category::Block, id: 389 }, Symbol { glyph: *b"\xCE\x93\x00\x00", len: 2, category: Category::Block, id: 390 }, Symbol { glyph: *b"\xCE\x9B\x00\x00", len: 2, category: Category::Block, id: 391 }, Symbol { glyph: *b"\xCE\xA7\x00\x00", len: 2, category: Category::Block, id: 392 }, Symbol { glyph: *b"\xCE\x95\x00\x00", len: 2, category: Category::Block, id: 393 }, Symbol { glyph: *b"\xCE\x98\x00\x00", len: 2, category: Category::Block, id: 394 }, ];
pub fn is_aisp_char(c: char) -> bool {
matches!(c,
'≜' | '≔' | '≡' | '≢' | '⇒' | '⇔' | '↔' | '⊢' | '⊨' | '∎' |
'∀' | '∃' | 'λ' | 'Π' | 'Σ' |
'∈' | '∉' | '⊆' | '⊇' | '∩' | '∪' | '∅' |
'⊕' | '⊖' | '⊗' | '∘' | '→' | '↦' |
'⟨' | '⟩' | '⟦' | '⟧' | '◊' | '𝔸' |
'⊤' | '⊥' | '¬' | '∧' | '∨' |
'ℕ' | 'ℤ' | 'ℝ' | 'ℚ' | '𝔹' | '𝕊' |
'𝒫' | 'ψ' | 'δ' | 'φ' | 'τ' | 'ε' | 'γ' | 'ρ' |
'Ω' | 'Γ' | 'Λ' | 'Χ' | 'Ε' | 'Θ' | 'ℭ'
)
}
pub fn count_symbols(s: &str) -> usize {
s.chars().filter(|&c| is_aisp_char(c)).count()
}
pub fn count_tokens(s: &str) -> usize {
s.split_whitespace().count()
}
pub fn lookup_symbol(glyph: &str) -> Option<SymbolId> {
let bytes = glyph.as_bytes();
for sym in AISP_SYMBOLS.iter() {
let len = sym.len as usize;
if len == bytes.len() && &sym.glyph[..len] == bytes {
return Some(SymbolId::new(sym.id));
}
}
None
}
pub fn starts_with_symbol(s: &str) -> Option<(SymbolId, usize)> {
for len in (2..=4).rev() {
if s.len() >= len {
if let Some(id) = lookup_symbol(&s[..len]) {
return Some((id, len));
}
}
}
None
}
pub fn get_glyph(id: SymbolId) -> Option<&'static str> {
for sym in AISP_SYMBOLS.iter() {
if sym.id == id.0 {
let len = sym.len as usize;
return core::str::from_utf8(&sym.glyph[..len]).ok();
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_aisp_char() {
assert!(is_aisp_char('∀'));
assert!(is_aisp_char('λ'));
assert!(is_aisp_char('≜'));
assert!(is_aisp_char('⟦'));
assert!(!is_aisp_char('a'));
assert!(!is_aisp_char('1'));
}
#[test]
fn test_count_symbols() {
assert_eq!(count_symbols("∀x:x≥0"), 1); assert_eq!(count_symbols("f≜λx.x"), 2); }
#[test]
fn test_lookup_symbol() {
assert!(lookup_symbol("∀").is_some());
assert!(lookup_symbol("λ").is_some());
assert!(lookup_symbol("𝔸").is_some());
assert!(lookup_symbol("x").is_none());
}
#[test]
fn test_starts_with_symbol() {
let (id, len) = starts_with_symbol("∀x:P(x)").unwrap();
assert_eq!(len, 3); assert_eq!(Category::from_id(id), Category::Forall);
}
#[test]
fn test_get_glyph() {
let id = lookup_symbol("λ").unwrap();
assert_eq!(get_glyph(id), Some("λ"));
}
}