steel_decimal/
lib.rs

1// src/lib.rs
2//! # Steel Decimal
3//! 
4//! A Rust library that provides decimal arithmetic support for the Steel programming language.
5//! This crate transforms Steel scripts to use high-precision decimal operations and provides
6//! function registration utilities for Steel VMs.
7//!
8//! ## Quick Start
9//!
10//! ### Basic Usage
11//! ```rust
12//! use steel_decimal::SteelDecimal;
13//! use steel::steel_vm::engine::Engine;
14//!
15//! // Create a new Steel Decimal engine
16//! let steel_decimal = SteelDecimal::new();
17//!
18//! // Transform a script
19//! let script = "(+ 1.5 2.3)";
20//! let transformed = steel_decimal.transform(script);
21//! // Result: "(decimal-add \"1.5\" \"2.3\")"
22//!
23//! // Register functions with Steel VM
24//! let mut vm = Engine::new();
25//! steel_decimal.register_functions(&mut vm);
26//!
27//! // Now you can execute the transformed script
28//! let result = vm.compile_and_run_raw_program(transformed);
29//! ```
30//!
31//! ### With Variables
32//! ```rust
33//! use steel_decimal::SteelDecimal;
34//! use steel::steel_vm::engine::Engine;
35//! use std::collections::HashMap;
36//!
37//! let mut variables = HashMap::new();
38//! variables.insert("x".to_string(), "10.5".to_string());
39//! variables.insert("y".to_string(), "20.3".to_string());
40//!
41//! let steel_decimal = SteelDecimal::with_variables(variables);
42//!
43//! let script = "(+ $x $y)";
44//! let transformed = steel_decimal.transform(script);
45//!
46//! let mut vm = Engine::new();
47//! steel_decimal.register_functions(&mut vm);
48//! ```
49//!
50//! ### Selective Function Registration
51//! ```rust
52//! use steel_decimal::FunctionRegistryBuilder;
53//! use steel::steel_vm::engine::Engine;
54//!
55//! let mut vm = Engine::new();
56//! FunctionRegistryBuilder::new()
57//!     .basic_arithmetic(true)
58//!     .advanced_math(false)
59//!     .trigonometric(false)
60//!     .register(&mut vm);
61//! ```
62
63pub mod functions;
64pub mod parser;
65pub mod registry;
66pub mod utils;
67
68pub use functions::*;
69pub use parser::ScriptParser;
70pub use registry::{FunctionRegistry, FunctionRegistryBuilder};
71pub use utils::{TypeConverter, ScriptAnalyzer, DecimalPrecision, ConversionError};
72
73use std::collections::HashMap;
74use steel::steel_vm::engine::Engine;
75
76/// Main entry point for the Steel Decimal library
77pub struct SteelDecimal {
78    parser: ScriptParser,
79    variables: HashMap<String, String>,
80}
81
82impl SteelDecimal {
83    /// Create a new SteelDecimal instance
84    pub fn new() -> Self {
85        Self {
86            parser: ScriptParser::new(),
87            variables: HashMap::new(),
88        }
89    }
90
91    /// Create a new SteelDecimal instance with variables
92    pub fn with_variables(variables: HashMap<String, String>) -> Self {
93        Self {
94            parser: ScriptParser::new(),
95            variables,
96        }
97    }
98
99    /// Transform a script by converting math operations to decimal functions
100    pub fn transform(&self, script: &str) -> String {
101        self.parser.transform(script)
102    }
103
104    /// Register all decimal functions with a Steel VM
105    pub fn register_functions(&self, vm: &mut Engine) {
106        FunctionRegistry::register_all(vm);
107        
108        if !self.variables.is_empty() {
109            FunctionRegistry::register_variables(vm, self.variables.clone());
110        }
111    }
112
113    /// Register functions selectively using a builder
114    pub fn register_functions_with_builder(&self, vm: &mut Engine, builder: FunctionRegistryBuilder) {
115        let builder = if !self.variables.is_empty() {
116            builder.with_variables(self.variables.clone())
117        } else {
118            builder
119        };
120        
121        builder.register(vm);
122    }
123
124    /// Add a variable to the context
125    pub fn add_variable(&mut self, name: String, value: String) {
126        self.variables.insert(name, value);
127    }
128
129    /// Get all variables
130    pub fn get_variables(&self) -> &HashMap<String, String> {
131        &self.variables
132    }
133
134    /// Extract dependencies from a script
135    pub fn extract_dependencies(&self, script: &str) -> std::collections::HashSet<String> {
136        self.parser.extract_dependencies(script)
137    }
138
139    /// Parse and execute a script in one step (convenience method)
140    pub fn parse_and_execute(&self, script: &str) -> Result<Vec<steel::rvals::SteelVal>, String> {
141        let mut vm = Engine::new();
142        self.register_functions(&mut vm);
143        
144        let transformed = self.transform(script);
145        
146        vm.compile_and_run_raw_program(transformed)
147            .map_err(|e| e.to_string())
148    }
149
150    /// Validate that a script can be transformed without errors
151    pub fn validate_script(&self, script: &str) -> Result<(), String> {
152        // Basic validation - check if transformation succeeds
153        let transformed = self.transform(script);
154        
155        // Check if the script contains balanced parentheses
156        let open_count = transformed.chars().filter(|c| *c == '(').count();
157        let close_count = transformed.chars().filter(|c| *c == ')').count();
158        
159        if open_count != close_count {
160            return Err("Unbalanced parentheses in script".to_string());
161        }
162        
163        // Check if all variables are defined
164        let dependencies = self.extract_dependencies(script);
165        for dep in dependencies {
166            if !self.variables.contains_key(&dep) {
167                return Err(format!("Undefined variable: {}", dep));
168            }
169        }
170        
171        Ok(())
172    }
173}
174
175impl Default for SteelDecimal {
176    fn default() -> Self {
177        Self::new()
178    }
179}
180
181/// Convenience functions for quick operations
182pub mod prelude {
183    pub use crate::{SteelDecimal, FunctionRegistry, FunctionRegistryBuilder};
184    pub use crate::functions::*;
185    pub use crate::utils::{TypeConverter, ScriptAnalyzer, DecimalPrecision};
186}