mon-core 0.0.3

A robust parser and validator for the Mycel Object Notation (MON) language, designed for fast, efficient, and human-friendly configuration.
Documentation
//! # Abstract Syntax Tree (AST) for MON
//!
//! This module defines the data structures that represent the Abstract Syntax Tree (AST) of a
//! parsed MON document. The AST is a tree-like representation of the source code's structure,
//! and it is the central data structure used throughout the compilation pipeline.
//!
//! ## Architectural Overview
//!
//! The AST is generated by the [`Parser`](crate::parser::Parser) from a stream of tokens.
//! The initial AST is a "raw" representation that has not been semantically analyzed.
//! This raw tree is then passed to the [`Resolver`](crate::resolver::Resolver), which processes it
//! by resolving imports, expanding aliases and spreads, and performing type validation.
//! The final, resolved AST is the main output of the `mon-core` library.
//!
//! The nodes of the tree are designed to be easily traversable and to store all necessary
//! information from the source, including positional data (spans) for accurate error reporting.
//!
//! ## Key Data Structures
//!
//! - [`MonDocument`]: The root of the AST, containing the root value (typically an object) and a list
//!   of all top-level `import` statements.
//!
//! - [`MonValue`]: A wrapper around a [`MonValueKind`] that also includes metadata like a potential
//!   anchor name and its source code position. This is the primary representation of any value in MON.
//!
//! - [`MonValueKind`]: An enum that defines the actual type of a value, such as `Object`, `Array`,
//!   `String`, `Number`, `Alias`, etc.
//!
//! - [`Member`]: Represents the different kinds of entries within an object, including a `Pair` (key-value),
//!   a `Spread` (`...*my_anchor`), or a `TypeDefinition`.
//!
//! - [`TypeDefinition`], [`StructDef`], [`EnumDef`]: These structs represent the schema definition
//!   constructs (`#struct` and `#enum`) in MON.
//!
//! - [`SymbolTable`]: A container generated by the resolver to store all resolved type definitions
//!   for easy lookup during validation.
//!
//! Developers typically do not need to construct these AST nodes manually. Instead, they are
//! generated by the `Parser` and consumed by other parts of the library or by language tools.
use log::error;
use miette::SourceSpan;
use std::fmt::{Debug, Display};
use std::panic::Location;

/// Represents a fully parsed MON document, including the root value and any import statements.
#[derive(Debug, PartialEq, Clone)]
pub struct MonDocument {
    pub root: MonValue,
    pub imports: Vec<ImportStatement>,
}

/// Represents a value in a MON document, such as a `string`, `number`, `object`, or `array`...
/// It also includes metadata like its source position and any associated anchor.
#[derive(Debug, PartialEq, Clone)]
pub struct MonValue {
    pub kind: MonValueKind,
    pub anchor: Option<String>,
    pub pos_start: usize,
    pub pos_end: usize,
}

impl MonValue {
    /// Returns the source span of the value, which can be used for error reporting.
    #[must_use]
    pub fn get_source_span(&self) -> SourceSpan {
        SourceSpan::new(self.pos_start.into(), self.pos_end - self.pos_start)
    }
}

/// An enum representing the different kinds of values that can exist in a MON document.
#[derive(Debug, PartialEq, Clone)]
pub enum MonValueKind {
    /// A string literal.
    String(String),
    /// A floating-point number.
    Number(f64),
    /// A boolean value.
    Boolean(bool),
    /// A null value.
    Null,
    /// An object, containing a vector of `Member`s.
    Object(Vec<Member>),
    /// An array, containing a vector of `MonValue`s.
    Array(Vec<MonValue>),
    /// An alias to an anchored value.
    Alias(String),
    /// An enum variant.
    EnumValue {
        enum_name: String,
        variant_name: String,
    },
    /// A spread of an array.
    ArraySpread(String),
}

/// Represents a member of a MON object.
#[derive(Debug, PartialEq, Clone)]
pub enum Member {
    /// A key-value pair.
    Pair(Pair),
    /// A spread of another object's members.
    Spread(String),
    /// An import statement.
    Import(ImportStatement),
    /// A type definition (`#struct` or `#enum`).
    TypeDefinition(TypeDefinition),
}

/// Represents a key-value pair within a MON object.
#[derive(Debug, PartialEq, Clone)]
pub struct Pair {
    /// The key of the pair.
    pub key: String,
    /// The value of the pair.
    pub value: MonValue,
    /// An optional type specification used for validation, e.g., `key::String`.
    ///
    /// [NOTE] This is lazily generated, check if it exists first
    pub validation: Option<TypeSpec>,
}

/// Represents an `import` statement, e.g., `import "path/to/file.mon" as my_namespace;`
#[derive(Debug, PartialEq, Clone)]
pub struct ImportStatement {
    /// The file path to the imported file.
    pub path: String,
    /// The specification of what to import (either a namespace or a list of named items).
    pub spec: ImportSpec,
    /// The starting character position of this statement in the source text.
    pub pos_start: usize,
    /// The ending character position of this statement in the source text.
    pub pos_end: usize,
}

/// Defines what is being imported from a file.
#[derive(Debug, PartialEq, Clone)]
pub enum ImportSpec {
    /// Imports the entire file into a single namespace, e.g., `as my_namespace`.
    Namespace(String),
    /// Imports specific items (anchors or types) from the file, e.g., `{ MyType, &my_anchor }`.
    Named(Vec<ImportSpecifier>),
}

/// Represents a single item being imported by name.
#[derive(Debug, PartialEq, Clone)]
pub struct ImportSpecifier {
    /// The name of the type or anchor to import.
    pub name: String,
    /// Whether the imported item is an anchor (e.g., `&my_anchor`).
    pub is_anchor: bool,
}

/// Represents a type definition, either a `#struct` or an `#enum`.
#[derive(Debug, PartialEq, Clone)]
pub struct TypeDefinition {
    /// The name of the type being defined.
    pub name: String,
    /// The source span of the type's name.
    pub name_span: SourceSpan,
    /// The actual definition of the type.
    pub def_type: TypeDef,
    /// The starting character position of this definition in the source text.
    pub pos_start: usize,
    /// The ending character position of this definition in the source text.
    pub pos_end: usize,
}

/// An enum that holds either a struct or an enum definition.
#[derive(Debug, PartialEq, Clone)]
pub enum TypeDef {
    /// A struct definition.
    Struct(StructDef),
    /// An enum definition.
    Enum(EnumDef),
}

impl TypeDef {
    /// Returns the source span of the entire type definition.
    #[must_use]
    pub fn get_span(&self) -> SourceSpan {
        match self {
            TypeDef::Struct(s) => (s.pos_start, s.pos_end - s.pos_start).into(),
            TypeDef::Enum(e) => (e.pos_start, e.pos_end - e.pos_start).into(),
        }
    }
}

/// Represents a `#struct` definition.
#[derive(Debug, PartialEq, Clone)]
pub struct StructDef {
    /// The fields that make up the struct.
    pub fields: Vec<FieldDef>,
    /// The starting character position of this struct definition in the source text.
    pub pos_start: usize,
    /// The ending character position of this struct definition in the source text.
    pub pos_end: usize,
}

/// Represents a single field within a `#struct` definition.
#[derive(Debug, PartialEq, Clone)]
pub struct FieldDef {
    /// The name of the field.
    pub name: String,
    /// The type specification for this field.
    pub type_spec: TypeSpec,
    /// An optional default value for this field.
    pub default_value: Option<MonValue>,
}

/// Represents an `#enum` definition.
#[derive(Debug, PartialEq, Clone)]
pub struct EnumDef {
    /// The variants of the enum.
    pub variants: Vec<String>,
    /// The starting character position of this enum definition in the source text.
    pub pos_start: usize,
    /// The ending character position of this enum definition in the source text.
    pub pos_end: usize,
}

/// Represents a type specification used for validation, e.g., `:: String` or `:: [Number, String]`.
#[derive(Debug, PartialEq, Clone)]
pub enum TypeSpec {
    /// A simple type, e.g., `String`, `MyStruct`.
    Simple(String, SourceSpan),
    /// A collection type, e.g., `[Number, String]`.
    Collection(Vec<TypeSpec>, SourceSpan),
    /// A spread type within a collection, e.g., `[Number...]`.
    Spread(Box<TypeSpec>, SourceSpan),
}

impl TypeSpec {
    #[must_use]
    pub fn get_span(&self) -> SourceSpan {
        match self {
            TypeSpec::Simple(_, span)
            | TypeSpec::Collection(_, span)
            | TypeSpec::Spread(_, span) => *span,
        }
    }
}

impl Display for Member {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Member::Pair(p) => write!(f, "Pair({}: {})", p.key, p.value),
            Member::Spread(s) => write!(f, "Spread(...*{s})"),
            Member::Import(i) => write!(f, "Import({i:?})"),
            Member::TypeDefinition(t) => write!(f, "TypeDef({t:?})"),
        }
    }
}

/// A table to store resolved symbols, such as type definitions, from a MON document and its imports.
#[derive(Debug, Default)]
pub struct SymbolTable {
    /// A map of type names to their definitions.
    pub types: std::collections::HashMap<String, TypeDefinition>,
}

impl SymbolTable {
    /// Creates a new, empty symbol table.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }
}

impl Display for MonDocument {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.root)
    }
}

impl Display for MonValueKind {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MonValueKind::String(s) => write!(f, "\"{s}\""),
            MonValueKind::Number(n) => write!(f, "{n}"),
            MonValueKind::Boolean(b) => write!(f, "{b}"),
            MonValueKind::Null => write!(f, "null"),
            MonValueKind::Object(o) => {
                write!(f, "{{ ")?;
                for (i, member) in o.iter().enumerate() {
                    write!(f, "{member}")?;
                    if i < o.len() - 1 {
                        write!(f, ", ")?;
                    }
                }
                write!(f, " }}")
            }
            MonValueKind::Array(a) => {
                write!(f, "[ ")?;
                for (i, value) in a.iter().enumerate() {
                    write!(f, "{value}")?;
                    if i < a.len() - 1 {
                        write!(f, ", ")?;
                    }
                }
                write!(f, "]")
            }
            MonValueKind::Alias(a) => write!(f, "*{a}"),
            MonValueKind::EnumValue {
                enum_name,
                variant_name,
            } => {
                write!(f, "${enum_name}.{variant_name}")
            }
            MonValueKind::ArraySpread(s) => write!(f, "...*{s}"),
        }
    }
}

impl Display for MonValue {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        if let Some(anchor) = &self.anchor {
            write!(f, "&{anchor} ")?;
        }
        write!(f, "{}", self.kind)
    }
}

impl Pair {
    #[track_caller]
    #[must_use]
    pub fn get_span(&self) -> SourceSpan {
        match &self.validation {
            None => {
                error!(
                    "No validation for `Pair`found for source span called by {}",
                    Location::caller()
                );
                SourceSpan::new(0.into(), 0)
            }
            Some(valid) => match valid {
                TypeSpec::Simple(_, source_span)
                | TypeSpec::Collection(_, source_span)
                | TypeSpec::Spread(_, source_span) => *source_span,
            },
        }
    }
}