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}