lsh-framework
Version:
A powerful, extensible shell with advanced job management, database persistence, and modern CLI features
257 lines (256 loc) • 9.57 kB
JavaScript
/**
* Floating Point Arithmetic Implementation
* Provides ZSH-compatible floating point math support
*/
export class FloatingPointArithmetic {
mathFunctions = new Map();
precision = 6;
constructor() {
this.setupMathFunctions();
}
/**
* Evaluate arithmetic expression with floating point support
*/
evaluate(expression) {
try {
// Clean and validate expression
const cleaned = this.cleanExpression(expression);
// Replace variables with their values
const withVariables = this.replaceVariables(cleaned);
// Replace function calls
const withFunctions = this.replaceFunctions(withVariables);
// Evaluate the expression
const result = this.evaluateExpression(withFunctions);
// Round to specified precision
return this.roundToPrecision(result);
}
catch (error) {
throw new Error(`Arithmetic error: ${error.message}`);
}
}
/**
* Set precision for floating point results
*/
setPrecision(precision) {
this.precision = Math.max(0, Math.min(15, precision));
}
/**
* Get current precision
*/
getPrecision() {
return this.precision;
}
/**
* Add a custom math function
*/
addMathFunction(func) {
this.mathFunctions.set(func.name, func);
}
/**
* Get available math functions
*/
getAvailableFunctions() {
return Array.from(this.mathFunctions.keys());
}
/**
* Setup built-in math functions
*/
setupMathFunctions() {
// Basic trigonometric functions
this.addMathFunction({ name: 'sin', func: Math.sin, arity: 1 });
this.addMathFunction({ name: 'cos', func: Math.cos, arity: 1 });
this.addMathFunction({ name: 'tan', func: Math.tan, arity: 1 });
this.addMathFunction({ name: 'asin', func: Math.asin, arity: 1 });
this.addMathFunction({ name: 'acos', func: Math.acos, arity: 1 });
this.addMathFunction({ name: 'atan', func: Math.atan, arity: 1 });
this.addMathFunction({ name: 'atan2', func: (x, y) => Math.atan2(y ?? 0, x), arity: 2 });
// Hyperbolic functions
this.addMathFunction({ name: 'sinh', func: Math.sinh, arity: 1 });
this.addMathFunction({ name: 'cosh', func: Math.cosh, arity: 1 });
this.addMathFunction({ name: 'tanh', func: Math.tanh, arity: 1 });
this.addMathFunction({ name: 'asinh', func: Math.asinh, arity: 1 });
this.addMathFunction({ name: 'acosh', func: Math.acosh, arity: 1 });
this.addMathFunction({ name: 'atanh', func: Math.atanh, arity: 1 });
// Exponential and logarithmic functions
this.addMathFunction({ name: 'exp', func: Math.exp, arity: 1 });
this.addMathFunction({ name: 'log', func: Math.log, arity: 1 });
this.addMathFunction({ name: 'log10', func: Math.log10, arity: 1 });
this.addMathFunction({ name: 'log2', func: Math.log2, arity: 1 });
this.addMathFunction({ name: 'pow', func: (x, y) => Math.pow(x, y ?? 0), arity: 2 });
// Power and root functions
this.addMathFunction({ name: 'sqrt', func: Math.sqrt, arity: 1 });
this.addMathFunction({ name: 'cbrt', func: Math.cbrt, arity: 1 });
// Rounding functions
this.addMathFunction({ name: 'ceil', func: Math.ceil, arity: 1 });
this.addMathFunction({ name: 'floor', func: Math.floor, arity: 1 });
this.addMathFunction({ name: 'round', func: Math.round, arity: 1 });
this.addMathFunction({ name: 'trunc', func: Math.trunc, arity: 1 });
// Absolute value and sign
this.addMathFunction({ name: 'abs', func: Math.abs, arity: 1 });
this.addMathFunction({ name: 'sign', func: Math.sign, arity: 1 });
// Random functions
this.addMathFunction({ name: 'random', func: () => Math.random(), arity: 1 });
this.addMathFunction({ name: 'rand', func: () => Math.random(), arity: 1 });
// Constants
this.addMathFunction({ name: 'pi', func: () => Math.PI, arity: 1 });
this.addMathFunction({ name: 'e', func: () => Math.E, arity: 1 });
// Additional utility functions
this.addMathFunction({ name: 'min', func: (x, y) => Math.min(x, y ?? x), arity: 2 });
this.addMathFunction({ name: 'max', func: (x, y) => Math.max(x, y ?? x), arity: 2 });
this.addMathFunction({ name: 'clz32', func: Math.clz32, arity: 1 });
this.addMathFunction({ name: 'fround', func: Math.fround, arity: 1 });
this.addMathFunction({ name: 'imul', func: (x, y) => Math.imul(x, y ?? 0), arity: 2 });
}
/**
* Clean expression by removing whitespace and validating characters
*/
cleanExpression(expression) {
// Remove whitespace
const cleaned = expression.replace(/\s/g, '');
// Validate characters (allow numbers, operators, parentheses, function names, and dots)
if (!/^[0-9+\-*/().,a-zA-Z_]+$/.test(cleaned)) {
throw new Error('Invalid characters in expression');
}
return cleaned;
}
/**
* Replace variables in expression (placeholder for future variable support)
*/
replaceVariables(expression) {
// For now, just return the expression as-is
// In a full implementation, this would replace variables like $x with their values
return expression;
}
/**
* Replace function calls with their results
*/
replaceFunctions(expression) {
let result = expression;
// Find function calls like sin(1.5) or pow(2,3)
const functionRegex = /([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)/g;
let match;
while ((match = functionRegex.exec(expression)) !== null) {
const [fullMatch, funcName, argsStr] = match;
const func = this.mathFunctions.get(funcName);
if (!func) {
throw new Error(`Unknown function: ${funcName}`);
}
const args = this.parseFunctionArguments(argsStr);
if (args.length !== func.arity) {
throw new Error(`Function ${funcName} expects ${func.arity} arguments, got ${args.length}`);
}
const funcResult = func.func(args[0], args[1]);
result = result.replace(fullMatch, funcResult.toString());
}
return result;
}
/**
* Parse function arguments
*/
parseFunctionArguments(argsStr) {
if (!argsStr.trim())
return [];
const args = [];
let current = '';
let parenCount = 0;
for (let i = 0; i < argsStr.length; i++) {
const char = argsStr[i];
if (char === '(') {
parenCount++;
current += char;
}
else if (char === ')') {
parenCount--;
current += char;
}
else if (char === ',' && parenCount === 0) {
args.push(parseFloat(current.trim()));
current = '';
}
else {
current += char;
}
}
if (current.trim()) {
args.push(parseFloat(current.trim()));
}
return args;
}
/**
* Evaluate the mathematical expression
*/
evaluateExpression(expression) {
try {
// Use Function constructor for safe evaluation
// This is a simplified approach - a full implementation would use a proper parser
const func = new Function(`return ${expression}`);
const result = func();
if (typeof result !== 'number' || isNaN(result)) {
throw new Error('Invalid expression result');
}
return result;
}
catch (error) {
throw new Error(`Expression evaluation failed: ${error.message}`);
}
}
/**
* Round number to specified precision
*/
roundToPrecision(num) {
const factor = Math.pow(10, this.precision);
return Math.round(num * factor) / factor;
}
/**
* Format number with specified precision
*/
formatNumber(num, precision) {
const prec = precision !== undefined ? precision : this.precision;
return num.toFixed(prec);
}
/**
* Check if a string represents a valid floating point number
*/
isValidFloat(str) {
const num = parseFloat(str);
return !isNaN(num) && isFinite(num);
}
/**
* Convert string to floating point number
*/
parseFloat(str) {
const num = parseFloat(str);
if (isNaN(num)) {
throw new Error(`Invalid floating point number: ${str}`);
}
return num;
}
/**
* Get mathematical constants
*/
getConstants() {
return {
pi: Math.PI,
e: Math.E,
ln2: Math.LN2,
ln10: Math.LN10,
log2e: Math.LOG2E,
log10e: Math.LOG10E,
sqrt1_2: Math.SQRT1_2,
sqrt2: Math.SQRT2,
};
}
/**
* Evaluate expression with error handling
*/
safeEvaluate(expression) {
try {
const result = this.evaluate(expression);
return { success: true, result };
}
catch (error) {
return { success: false, error: error.message };
}
}
}
export default FloatingPointArithmetic;