glyph-types 0.0.1

Core type definitions and value system for the Glyph programming language
Documentation
//! Glyph Type System
//!
//! Implements the gradual type system for Glyph with support for
//! type inference and runtime type checking.

use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;

pub mod inference;
pub mod value;

pub use value::Value;

/// Type representation in the Glyph type system
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Type {
    // Primitives
    Int,
    Float,
    Str,
    Bool,
    Bytes,
    Unit,

    // Containers
    List(Box<Type>),
    Dict(Box<Type>, Box<Type>),
    Optional(Box<Type>),

    // Special types
    Promise(Box<Type>),
    Result(Box<Type>, Box<Type>),

    // Functions
    Function {
        params: Vec<(String, Type)>,
        return_type: Box<Type>,
    },

    // Type variables for inference
    TypeVar(u32),

    // Unknown type (for gradual typing)
    Unknown,
}

impl Type {
    /// Check if this type is assignable to another type
    pub fn is_assignable_to(&self, other: &Type) -> bool {
        match (self, other) {
            // Exact match
            (t1, t2) if t1 == t2 => true,

            // Unknown is assignable to/from anything (gradual typing)
            (Type::Unknown, _) | (_, Type::Unknown) => true,

            // Container types
            (Type::List(t1), Type::List(t2)) => t1.is_assignable_to(t2),
            (Type::Optional(t1), Type::Optional(t2)) => t1.is_assignable_to(t2),
            (Type::Promise(t1), Type::Promise(t2)) => t1.is_assignable_to(t2),

            // Result types
            (Type::Result(ok1, err1), Type::Result(ok2, err2)) => {
                ok1.is_assignable_to(ok2) && err1.is_assignable_to(err2)
            }

            // Dict types
            (Type::Dict(k1, v1), Type::Dict(k2, v2)) => {
                k1.is_assignable_to(k2) && v1.is_assignable_to(v2)
            }

            // Function types (contravariant in params, covariant in return)
            (
                Type::Function {
                    params: p1,
                    return_type: r1,
                },
                Type::Function {
                    params: p2,
                    return_type: r2,
                },
            ) => {
                p1.len() == p2.len()
                    && p1
                        .iter()
                        .zip(p2.iter())
                        .all(|((_, t1), (_, t2))| t2.is_assignable_to(t1))
                    && r1.is_assignable_to(r2)
            }

            _ => false,
        }
    }
}

impl fmt::Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Type::Int => write!(f, "int"),
            Type::Float => write!(f, "float"),
            Type::Str => write!(f, "str"),
            Type::Bool => write!(f, "bool"),
            Type::Bytes => write!(f, "bytes"),
            Type::Unit => write!(f, "unit"),
            Type::List(t) => write!(f, "list[{t}]"),
            Type::Dict(k, v) => write!(f, "dict[{k}, {v}]"),
            Type::Optional(t) => write!(f, "optional[{t}]"),
            Type::Promise(t) => write!(f, "promise[{t}]"),
            Type::Result(ok, err) => write!(f, "result[{ok}, {err}]"),
            Type::Function {
                params,
                return_type,
            } => {
                write!(f, "(")?;
                for (i, (name, typ)) in params.iter().enumerate() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    write!(f, "{name}: {typ}")?;
                }
                write!(f, ") -> {return_type}")
            }
            Type::TypeVar(id) => write!(f, "T{id}"),
            Type::Unknown => write!(f, "?"),
        }
    }
}

#[derive(Debug, Error)]
pub enum TypeError {
    #[error("Type mismatch: expected {expected}, found {found}")]
    TypeMismatch { expected: Type, found: Type },

    #[error("Undefined variable: {0}")]
    UndefinedVariable(String),

    #[error("Cannot call non-function type: {0}")]
    NotCallable(Type),

    #[error("Wrong number of arguments: expected {expected}, found {found}")]
    ArgumentCountMismatch { expected: usize, found: usize },

    #[error("Cannot access attribute '{attr}' on type {typ}")]
    AttributeError { attr: String, typ: Type },

    #[error("Type inference failed: {0}")]
    InferenceFailed(String),
}