syn-canon 0.5.2

Canonical form of a syn file, equal across formatting drift and local binder names
Documentation
//! Original-source binding identities, lexical namespaces, and the
//! scheduler reference keys both passes resolve through.

use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use proc_macro2::Ident;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Ns {
    Value,
    Type,
    Lifetime,
    Label,
    Macro,
}

impl Ns {
    pub(crate) fn key(self, name: &str) -> &str {
        match self {
            Self::Value | Self::Type | Self::Macro => unraw(name),
            Self::Lifetime | Self::Label => name,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct BindingId(pub(crate) usize);

/// The identity of a value or type reference: the canonical identity of
/// the binding that answers, or the name's own spelling when nothing
/// resolves it.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) enum FreeKey {
    /// The canonical identity the shared resolver names the binding.
    Resolved(String),
    /// The name's spelling, no binding resolved it.
    Unresolved(String),
}

impl FreeKey {
    /// The stable text the structural labels compare, the prefixes kept
    /// apart so no spelling can spell a resolved identity.
    pub(crate) fn text(&self) -> String {
        match self {
            Self::Resolved(name) => format!("f{name}"),
            Self::Unresolved(name) => format!("g{name}"),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Origin {
    Parameter,
    Let,
    Closure,
    Generic,
    Lifetime,
    Label,
    Item,
    Use,
    TypeAlias,
    MacroRule,
    SelfTy,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum IntWidth {
    W8,
    W16,
    W32,
    W64,
    W128,
}

impl IntWidth {
    pub(crate) fn bits(self) -> u32 {
        match self {
            Self::W8 => 8,
            Self::W16 => 16,
            Self::W32 => 32,
            Self::W64 => 64,
            Self::W128 => 128,
        }
    }
}

/// A fixed-width primitive scalar rustc evaluates independently of the target.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum PrimTy {
    /// A two's-complement integer of `width` bits.
    Int { width: IntWidth, signed: bool },
    /// The `bool` scalar.
    Bool,
}

impl PrimTy {
    /// The canonical spelling of the suffix.
    pub(crate) fn suffix(self) -> &'static str {
        match self {
            Self::Int {
                width,
                signed: true,
            } => match width {
                IntWidth::W8 => "i8",
                IntWidth::W16 => "i16",
                IntWidth::W32 => "i32",
                IntWidth::W64 => "i64",
                IntWidth::W128 => "i128",
            },
            Self::Int {
                width,
                signed: false,
            } => match width {
                IntWidth::W8 => "u8",
                IntWidth::W16 => "u16",
                IntWidth::W32 => "u32",
                IntWidth::W64 => "u64",
                IntWidth::W128 => "u128",
            },
            Self::Bool => "bool",
        }
    }
}

/// The primitive a primitive name spells, raw or plain.
pub(crate) fn primitive_suffix(name: &str) -> Option<PrimTy> {
    match unraw(name) {
        "i8" => Some(PrimTy::Int {
            width: IntWidth::W8,
            signed: true,
        }),
        "i16" => Some(PrimTy::Int {
            width: IntWidth::W16,
            signed: true,
        }),
        "i32" => Some(PrimTy::Int {
            width: IntWidth::W32,
            signed: true,
        }),
        "i64" => Some(PrimTy::Int {
            width: IntWidth::W64,
            signed: true,
        }),
        "i128" => Some(PrimTy::Int {
            width: IntWidth::W128,
            signed: true,
        }),
        "u8" => Some(PrimTy::Int {
            width: IntWidth::W8,
            signed: false,
        }),
        "u16" => Some(PrimTy::Int {
            width: IntWidth::W16,
            signed: false,
        }),
        "u32" => Some(PrimTy::Int {
            width: IntWidth::W32,
            signed: false,
        }),
        "u64" => Some(PrimTy::Int {
            width: IntWidth::W64,
            signed: false,
        }),
        "u128" => Some(PrimTy::Int {
            width: IntWidth::W128,
            signed: false,
        }),
        "bool" => Some(PrimTy::Bool),
        _ => None,
    }
}

/// A declaration's index in the immutable helper summary table.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct HelperId(pub(crate) usize);

#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Binding {
    pub(crate) id: BindingId,
    pub(crate) canon: String,
    pub(crate) origin: Origin,
    /// A proven fixed-width primitive scalar type.
    pub(crate) prim: Option<PrimTy>,
    /// A function parameter's declared position, zero for other origins.
    pub(crate) port: usize,
    /// The scheduler's value port of a closure input or planned let.
    pub(crate) value_port: Option<crate::schedule::Port>,
    pub(crate) target: Option<usize>,
    pub(crate) raw: bool,
    /// The identity handle of an accepted total helper declaration.
    pub(crate) helper: Option<HelperId>,
    pub(crate) macro_resolution: Option<crate::macros::MacroResolution>,
}

#[derive(Clone, Default)]
pub(crate) struct Frame {
    names: [BTreeMap<String, Binding>; 5],
    /// A glob import marked this scope uncertain.
    pub(crate) glob: bool,
    pub(crate) observed: bool,
    /// Standard names an import may rebind, all set under an unknown glob.
    pub(crate) std_rebound: u64,
    /// The variant names, in declaration order, of each enum binding of the type namespace.
    enums: BTreeMap<BindingId, Vec<String>>,
}

impl Frame {
    pub(crate) fn bind(&mut self, ns: Ns, name: &str, mut binding: Binding) {
        binding.raw = name.starts_with("r#");
        self.names[ns as usize].insert(ns.key(name).to_string(), binding);
    }

    pub(crate) fn lookup(&self, ns: Ns, name: &str) -> Option<&Binding> {
        self.names[ns as usize].get(ns.key(name))
    }

    pub(crate) fn bindings(&self, ns: Ns) -> &BTreeMap<String, Binding> {
        &self.names[ns as usize]
    }

    pub(crate) fn bindings_mut(&mut self, ns: Ns) -> &mut BTreeMap<String, Binding> {
        &mut self.names[ns as usize]
    }

    pub(crate) fn next_id(&self) -> usize {
        self.names
            .iter()
            .flat_map(BTreeMap::values)
            .map(|binding| binding.id.0)
            .max()
            .map_or(0, |id| id + 1)
    }

    /// Attach a declaration's cached helper handle.
    pub(crate) fn set_helper(&mut self, ns: Ns, name: &str, helper: HelperId) {
        self.names[ns as usize]
            .get_mut(ns.key(name))
            .expect("indexed helper value binding")
            .helper = Some(helper);
    }

    pub(crate) fn set_macro_resolution(
        &mut self,
        ns: Ns,
        name: &str,
        standard: Option<crate::macros::MacroResolution>,
    ) {
        self.bindings_mut(ns)
            .get_mut(ns.key(name))
            .expect("a prebound name")
            .macro_resolution = standard;
    }

    /// Record the variants of the enum `name` binds in the type namespace.
    pub(crate) fn set_variants(&mut self, name: &str, item: &syn::ItemEnum) {
        let id = self.lookup(Ns::Type, name).expect("a prebound enum").id;
        self.enums.insert(
            id,
            item.variants
                .iter()
                .map(|variant| ident_name(&variant.ident))
                .collect(),
        );
    }

    /// The variants of the enum `binding` names, when this frame declared one.
    pub(crate) fn variants(&self, binding: &Binding) -> Option<&[String]> {
        self.enums.get(&binding.id).map(Vec::as_slice)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Domain {
    Local,
    Inherited,
}

#[derive(Clone, Debug)]
pub(crate) struct TargetSeg {
    pub(crate) name: String,
    pub(crate) origin: Option<(Domain, BindingId, Ns)>,
}

#[derive(Clone, Debug)]
pub(crate) struct Target {
    pub(crate) rooted: bool,
    pub(crate) segments: Vec<TargetSeg>,
}

pub(crate) enum FirstSeg<'a> {
    Alias(&'a Target),
    Module(Domain, BindingId),
    Free,
}

pub(crate) struct Inherited<'env> {
    pub(crate) frames: &'env [Frame],
    pub(crate) mod_frames: &'env BTreeMap<BindingId, Frame>,
    pub(crate) targets: &'env [Target],
}

pub(crate) fn unraw(name: &str) -> &str {
    name.strip_prefix("r#").unwrap_or(name)
}

pub(crate) fn ident_name(ident: &Ident) -> String {
    unraw(&ident.to_string()).to_string()
}