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
}
}