using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Text.RegularExpressions; using System.IO; using System.Threading; using csmic.CodedFunctions; using csmic.Interpreter; using csmic.ComputableEngine; using System.Runtime.Remoting.Messaging; // namespace: csmic // // summary: . namespace csmic { /// /// An interpreter object that reads user input and evaluates the code. /// /// The interpreter does not support exceptions by design. Instead, invalid /// calculations, parameters, etc. will result in a result of zero. /// /// InputInterpreter interpreter = new InputInterpreter(); /// interpreter.Interpret("1/0"); // The result will be 0, not an exception. /// /// public class InputInterpreter { #region Members /// /// The output generated. /// private string output; /// /// The variables assigned. /// internal Dictionary variables; /// /// The time for execution. /// private TimeSpan executionTime; /// /// The verbose message of the calculation. /// private string message; /// /// The list of coded functions that can be executed. /// private List codedFunctions; /// /// The list of user defined functions that can be executed. /// private List interpretedFunctions; /// /// The private calculated value. /// private decimal calculatedValue; #endregion #region Constructor /// /// Creates a new InputInterpreter. /// public InputInterpreter() { this.output = string.Empty; this.variables = new Dictionary(); this.executionTime = new TimeSpan(); this.codedFunctions = new List(); this.interpretedFunctions = new List(); LoadDefaultCodedFunctions(); } /// /// Creates a new InputInterpreter from an original. /// /// The orginal input interpreter to copy. public InputInterpreter(InputInterpreter original) { this.output = original.output; this.variables = original.variables; this.executionTime = original.executionTime; this.codedFunctions = original.codedFunctions; this.interpretedFunctions = original.interpretedFunctions; } #endregion #region Properties /// /// Gets the message that represents the InputInterpreters output. /// public string Output { get { return this.output; } } /// /// Gets the verbose message that is generated with a calculation. /// public string Message { get { return this.message; } } /// /// Gets the value of the output as a decimal. /// public decimal Decimal { get { return this.calculatedValue; } } /// /// Gets the value of the output cast as an int. /// public int Int { get { return (int)decimal.Round(this.calculatedValue); } } /// /// Gets the value of the output cast as a float. /// public float Float { get { return (float)this.calculatedValue; } } /// /// Gets the value of the output cast as a double. /// public double Double { get { return (double)this.calculatedValue; } } /// /// Gets the value (cast as a long) converted to its binary equivalent. /// public string Binary { get { return Convert.ToString((long)this.calculatedValue, 2).PadLeft(64, '0'); } } /// /// Gets the execution time of the last calculation. /// public TimeSpan LastExecutionTime { get { return this.executionTime; } } /// /// Gets or sets a list of coded functions that the interpreter supports. /// public List CodedFunctions { get { return this.codedFunctions; } set { this.codedFunctions = value; } } /// /// Gets or sets a list of user generated interpreted functions that the interpreter supports. /// /// The interpreted functions. public List InterpretedFunctions { get { return this.interpretedFunctions; } set { this.interpretedFunctions = value; } } /// Gets the variables. /// The variables. public Dictionary Variables { get { return this.variables; } set { this.variables = value; } } #endregion #region Public Methods /// /// Interprets and executes given input. /// /// The input to interpret and execute. public void Interpret(string input) { if (string.IsNullOrEmpty(input)) { return; } DateTime timeStart = DateTime.Now; this.message = string.Empty; UTF8Encoding encoder = new UTF8Encoding(); Parser p = new Parser(new Scanner(new MemoryStream(encoder.GetBytes(input)))); p.Interpreter = this; try { p.Parse(); this.calculatedValue = p.CalculatedValue; if (p.errors.count > 0) { ProduceOutput(this.calculatedValue, p.errors.builder.ToString()); } } catch (Exception e) { this.calculatedValue = 0; ProduceOutput(this.calculatedValue, e.Message); } DateTime timeEnd = DateTime.Now; this.executionTime = timeEnd - timeStart; } /// /// Computes an expression and returns the result as a decimal. /// /// The expression to be calculated. /// The value that was computed. public decimal ComputeExpression(string expression) { this.Interpret(expression); return this.calculatedValue; } /// /// Assigns a decimal value to a variable. /// /// The name of the variable. /// The value of the variable. /// True if the variable was set, false otherwise. internal bool Assign(string name, decimal value) { Variable v = new Variable(); v.Type = VariableType.Decimal; v.Value = value; if (!this.variables.ContainsKey(name)) { this.variables.Add(name, v); } else { this.variables[name] = v; } return true; } /// /// Assigns a decimal value to a variable. /// /// The name of the variable. /// The expression of the variable. /// True if the variable was set, false otherwise. internal bool Assign(string name, string expression) { Variable v = new Variable(); v.Type = VariableType.Equation; v.Value = expression; if (!this.variables.ContainsKey(name)) { this.variables.Add(name, v); } else { this.variables[name] = v; } return true; } /// /// Assigns a decimal value to a variable. /// /// The name of the variable. /// The values of the variable. /// True if the variable was set, false otherwise. internal bool Assign(string name, decimal[] values) { Variable v = new Variable(); v.Type = VariableType.Array; v.Value = values; if (!this.variables.ContainsKey(name)) { this.variables.Add(name, v); } else { this.variables[name] = v; } return true; } /// /// Executes a function stored in the interpreter. /// /// The name of the function to execute. /// The arguments to pass to the function. /// The value computed from the function execution. internal decimal ExecuteFunction(string name, decimal[] args) { foreach (ICodedFunction codedFunction in this.codedFunctions) { if (codedFunction.FunctionName == name) { return codedFunction.Execute(args); } } foreach (InterpretedFunction interpretedFunction in this.interpretedFunctions) { if (interpretedFunction.Name == name) { string answer = interpretedFunction.Compute(args); decimal parsed = 0; if (decimal.TryParse(answer, out parsed)) { return parsed; } } } return 0; } #endregion #region Private Methods /// /// Loads the default coded functions supported by the interpreter. /// private void LoadDefaultCodedFunctions() { this.codedFunctions.Add(new CF_Sin()); this.codedFunctions.Add(new CF_Cos()); this.codedFunctions.Add(new CF_Tan()); this.codedFunctions.Add(new CF_Round()); this.codedFunctions.Add(new CF_Sqrt()); this.codedFunctions.Add(new CF_Abs()); this.codedFunctions.Add(new CF_Exp()); this.codedFunctions.Add(new CF_Log()); this.codedFunctions.Add(new CF_Precision()); } /// /// Produces output given a single object. /// /// The object representing the output. internal void ProduceOutput(object output) { if (output is bool) { bool o = (bool)output; if (o) { this.calculatedValue = 1; } else { this.calculatedValue = 0; } } this.output = string.Format("{0}", output); } /// /// Produces output given an object and a message. /// /// The object representing the output. /// The message to be displayed with the output. private void ProduceOutput(object output, string message) { if (!string.IsNullOrEmpty(message)) { this.output = string.Format("{0}", output); this.message = message; } else { ProduceOutput(output); this.message = string.Empty; } } #endregion #region Asynchronous /// Interpret an input asynchronously. /// The input to interpret and execute. /// The callback. public void InterpretAsync(string input, Action callback) { InterpretAsyncDelegate del = new InterpretAsyncDelegate(InterpretAsyncThreadingWork); del.BeginInvoke(input, (result) => { AsyncResult returned = result as AsyncResult; if (returned != null) { InterpretAsyncDelegate end = returned.AsyncDelegate as InterpretAsyncDelegate; if (end != null) { InputInterpreter returnValue = end.EndInvoke(result); callback(returnValue); } } }, null); } /// Interpret asynchronous threading work. /// The input to interpret and execute. private InputInterpreter InterpretAsyncThreadingWork(string input) { Interpret(input); return this; } /// Interpret asynchronous delegate. /// The input. /// . private delegate InputInterpreter InterpretAsyncDelegate(string input); #endregion #region Computable /// Converts this object to a computable. /// This object as a Computable. public Computable ToComputable() { return new Computable(this.Decimal.ToString(), this); } /// Treats the current object as a computable and performs an action in that context. /// The action to execute as a computable object. /// This object as a Computable, after the given action. public Computable AsComputable(Func action) { return action(this.ToComputable()); } #endregion } }