steel decimal crate with a proper setup, needs so much work to be done to be finished
This commit is contained in:
176
steel_decimal/src/parser.rs
Normal file
176
steel_decimal/src/parser.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
// src/parser.rs
|
||||
use regex::Regex;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct ScriptParser {
|
||||
math_operators: Vec<(Regex, &'static str)>,
|
||||
number_literal_re: Regex,
|
||||
variable_re: Regex,
|
||||
}
|
||||
|
||||
impl ScriptParser {
|
||||
pub fn new() -> Self {
|
||||
let math_operators = vec![
|
||||
// Basic arithmetic
|
||||
(Regex::new(r"\(\s*\+\s+").unwrap(), "(decimal-add "),
|
||||
(Regex::new(r"\(\s*-\s+").unwrap(), "(decimal-sub "),
|
||||
(Regex::new(r"\(\s*\*\s+").unwrap(), "(decimal-mul "),
|
||||
(Regex::new(r"\(\s*/\s+").unwrap(), "(decimal-div "),
|
||||
|
||||
// Power and advanced operations
|
||||
(Regex::new(r"\(\s*\^\s+").unwrap(), "(decimal-pow "),
|
||||
(Regex::new(r"\(\s*\*\*\s+").unwrap(), "(decimal-pow "),
|
||||
(Regex::new(r"\(\s*pow\s+").unwrap(), "(decimal-pow "),
|
||||
(Regex::new(r"\(\s*sqrt\s+").unwrap(), "(decimal-sqrt "),
|
||||
|
||||
// Logarithmic functions
|
||||
(Regex::new(r"\(\s*ln\s+").unwrap(), "(decimal-ln "),
|
||||
(Regex::new(r"\(\s*log\s+").unwrap(), "(decimal-ln "),
|
||||
(Regex::new(r"\(\s*log10\s+").unwrap(), "(decimal-log10 "),
|
||||
(Regex::new(r"\(\s*exp\s+").unwrap(), "(decimal-exp "),
|
||||
|
||||
// Trigonometric functions
|
||||
(Regex::new(r"\(\s*sin\s+").unwrap(), "(decimal-sin "),
|
||||
(Regex::new(r"\(\s*cos\s+").unwrap(), "(decimal-cos "),
|
||||
(Regex::new(r"\(\s*tan\s+").unwrap(), "(decimal-tan "),
|
||||
|
||||
// Comparison operators
|
||||
(Regex::new(r"\(\s*>\s+").unwrap(), "(decimal-gt "),
|
||||
(Regex::new(r"\(\s*<\s+").unwrap(), "(decimal-lt "),
|
||||
(Regex::new(r"\(\s*=\s+").unwrap(), "(decimal-eq "),
|
||||
(Regex::new(r"\(\s*>=\s+").unwrap(), "(decimal-gte "),
|
||||
(Regex::new(r"\(\s*<=\s+").unwrap(), "(decimal-lte "),
|
||||
|
||||
// Utility functions
|
||||
(Regex::new(r"\(\s*abs\s+").unwrap(), "(decimal-abs "),
|
||||
(Regex::new(r"\(\s*min\s+").unwrap(), "(decimal-min "),
|
||||
(Regex::new(r"\(\s*max\s+").unwrap(), "(decimal-max "),
|
||||
(Regex::new(r"\(\s*round\s+").unwrap(), "(decimal-round "),
|
||||
];
|
||||
|
||||
ScriptParser {
|
||||
math_operators,
|
||||
number_literal_re: Regex::new(r#"(?<!")(-?\d+\.?\d*(?:[eE][+-]?\d+)?)(?!")"#).unwrap(),
|
||||
variable_re: Regex::new(r"\$(\w+)").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform a script by converting math operations and numbers to decimal functions
|
||||
pub fn transform(&self, script: &str) -> String {
|
||||
let mut transformed = script.to_string();
|
||||
|
||||
// Step 1: Convert numeric literals to strings
|
||||
transformed = self.convert_numbers_to_strings(&transformed);
|
||||
|
||||
// Step 2: Replace math function calls with decimal equivalents
|
||||
transformed = self.replace_math_functions(&transformed);
|
||||
|
||||
// Step 3: Replace variable references
|
||||
transformed = self.replace_variable_references(&transformed);
|
||||
|
||||
transformed
|
||||
}
|
||||
|
||||
/// Convert all unquoted numeric literals to quoted strings
|
||||
fn convert_numbers_to_strings(&self, script: &str) -> String {
|
||||
self.number_literal_re.replace_all(script, |caps: ®ex::Captures| {
|
||||
format!("\"{}\"", &caps[1])
|
||||
}).to_string()
|
||||
}
|
||||
|
||||
/// Replace math function calls with decimal equivalents
|
||||
fn replace_math_functions(&self, script: &str) -> String {
|
||||
let mut result = script.to_string();
|
||||
|
||||
for (pattern, replacement) in &self.math_operators {
|
||||
result = pattern.replace_all(&result, *replacement).to_string();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Replace variable references ($var) with function calls
|
||||
fn replace_variable_references(&self, script: &str) -> String {
|
||||
self.variable_re.replace_all(script, |caps: ®ex::Captures| {
|
||||
format!("(get-var \"{}\")", &caps[1])
|
||||
}).to_string()
|
||||
}
|
||||
|
||||
/// Extract dependencies from script (useful for analysis)
|
||||
pub fn extract_dependencies(&self, script: &str) -> HashSet<String> {
|
||||
let mut dependencies = HashSet::new();
|
||||
|
||||
// Extract variable dependencies
|
||||
for cap in self.variable_re.captures_iter(script) {
|
||||
dependencies.insert(cap[1].to_string());
|
||||
}
|
||||
|
||||
dependencies
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScriptParser {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_basic_math_transformation() {
|
||||
let parser = ScriptParser::new();
|
||||
|
||||
let input = "(+ 1.5 2.3)";
|
||||
let expected = "(decimal-add \"1.5\" \"2.3\")";
|
||||
let result = parser.transform(input);
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex_expression() {
|
||||
let parser = ScriptParser::new();
|
||||
|
||||
let input = "(+ (* 2 3) (/ 10 2))";
|
||||
let expected = "(decimal-add (decimal-mul \"2\" \"3\") (decimal-div \"10\" \"2\"))";
|
||||
let result = parser.transform(input);
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_replacement() {
|
||||
let parser = ScriptParser::new();
|
||||
|
||||
let input = "(+ $x $y)";
|
||||
let expected = "(decimal-add (get-var \"x\") (get-var \"y\"))";
|
||||
let result = parser.transform(input);
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative_numbers() {
|
||||
let parser = ScriptParser::new();
|
||||
|
||||
let input = "(+ -1.5 2.3)";
|
||||
let expected = "(decimal-add \"-1.5\" \"2.3\")";
|
||||
let result = parser.transform(input);
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scientific_notation() {
|
||||
let parser = ScriptParser::new();
|
||||
|
||||
let input = "(+ 1.5e2 2.3E-1)";
|
||||
let expected = "(decimal-add \"1.5e2\" \"2.3E-1\")";
|
||||
let result = parser.transform(input);
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user