// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! Policy Expression Language (PEL)
//!
//! The Policy Expression Language (PEL) is an internal abstraction layer to evaluate DataWeave
//! expressions.
//!
//! ## Overview
//!
//! PEL provides:
//! - Expression parsing and evaluation
//! - Runtime environment for expression execution
//! - Context management for variable references
//! - Type handling and coercion
//!
//! ## Features
//!
//! - `experimental_coerced_type`: USE AT OWN RISK: Enables experimental type coercion features. This feature enables reading objects as their original representation as strings.
//!

pub mod expression;

pub mod parser;

/// Runtime environment for executing PEL expressions.
pub mod runtime;

/// Represents a location in the source Dataweave expression, typically used for error reporting.
///
/// The `start` and `end` fields represent the character offsets in the source string.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Location {
    /// The starting character offset of the location (inclusive).
    pub start: usize,
    /// The ending character offset of the location (exclusive).
    pub end: usize,
}

impl Location {
    /// Creates a new `Location` spanning from `start` to `end`.
    pub fn new(start: usize, end: usize) -> Self {
        Self { start, end }
    }
}

/// A unique identifier for a context in the PEL runtime.
///
/// Contexts are used to manage scopes and variable lookups during expression evaluation.
/// Each context has a unique ID that can be used to reference it.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ContextId(&'static str);

impl ContextId {
    /// Creates a new `ContextId` with the given identifier.
    ///
    /// The identifier should be unique within the scope of the application.
    pub const fn new(raw: &'static str) -> Self {
        Self(raw)
    }

    /// Creates a new `Reference` pointing to the first slot in this context.
    pub const fn first_reference(&self) -> Reference {
        Reference {
            context_id: *self,
            offset: 0,
        }
    }
}

/// A reference to a specific slot within a context.
///
/// References are used to track variable lookups and other context-dependent operations.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Reference {
    /// The context this reference belongs to.
    context_id: ContextId,
    /// The offset within the context.
    offset: usize,
}

impl Reference {
    /// Creates a new reference to the next slot in the same context.
    pub const fn next(&self) -> Self {
        Self {
            context_id: self.context_id,
            offset: self.offset + 1,
        }
    }

    /// Returns the offset of this reference within its context.
    pub const fn offset(&self) -> usize {
        self.offset
    }
}
