CCCPaste

Thread Task

//This example requires the 'Xamarin.Android GUI' option to be enabled
//This is a calculator using Mono.CSharp.Evaluator to evaluate expressions

//+ref=Mono.CSharp.dll
using System;
using System.Text.RegularExpressions;
using System.IO;
using Mono.CSharp;
using Android.Widget;
using Android.Content;
using Android.Views.InputMethods;
using System.Threading.Tasks;
using NativeUiLib; //This adds the UI controls

namespace CSharp_Shell
{

    public static class Program 
    {
        static Evaluator evaluator;
        static StringWriter sw; //Results and errors are printed here
		static string evalCodeStart = "public static class Calc { public static void Eval(){ result=";
		static EditText txtDisplay; //calculator display/result line
		static TextView lblError; //calculator evaluation error line
		static LinearLayout layNumpad;
		static LinearLayout layArithmetics;
		
        public static void Main() 
        {
		   var layMain = new LinearLayout(); //Primary layout
		   layMain.Orientation = Orientation.Vertical;   
		   txtDisplay = layMain.AddEditText(true); 
		   txtDisplay.Click += txtDisplay_Click;
		   lblError = layMain.AddTextView(true);
		   var hpx = Ui.Context.Resources.DisplayMetrics.HeightPixels * 0.1;
		   txtDisplay.SetMinHeight((int)hpx);
		   
		   
           var layHor = new LinearLayout(); //This layout will contains 2 others
           layHor.Orientation = Orientation.Horizontal;   
		   layMain.AddView(layHor); //Add to primary layout

           layNumpad = GenerateNumpad();
		   layArithmetics = GenerateArithmetics();
           
           layHor.AddView(layNumpad);
		   layHor.AddView(layArithmetics);
		   
		   
		   //Set layNumpad and layArithmetics to take up a specific percantage of space
		   var np= new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WrapContent);
		   np.Weight = 0.85f;//Take up 85% of parent layout (parent layout is width=fill_parent)
		   layNumpad.LayoutParameters = np;
		   
		   var ap= new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WrapContent);
		   ap.Weight = 0.25f; //Take up 25% of parent layout
		   layArithmetics.LayoutParameters = ap;
		   
		   
           layMain.Show(); //Show primary layout
           txtDisplay_Click(null, null);//Hide keyboard popup
           
        }
		
		//Hide the keyboard on click
		private static async void txtDisplay_Click(object sender, EventArgs args)
		{
			var im = ((InputMethodManager)Ui.Context.GetSystemService(Context.InputMethodService));
			await Task.Delay(10); //Hiding keyboard doesn't work without a delay
			im.HideSoftInputFromWindow(txtDisplay.WindowToken, Android.Views.InputMethods.HideSoftInputFlags.None);
		}
        
        private static LinearLayout GenerateNumpad()
        {
			//This layout is basically a table with 6 rows and 3 columns			
            var linNum = new LinearLayout();
            linNum.Orientation = Orientation.Vertical;

            var linRow1 = new LinearLayout();
			linRow1.Orientation = Orientation.Horizontal;
            linRow1.AddView(NewCalcButton("1"));
			linRow1.AddView(NewCalcButton("2"));
			linRow1.AddView(NewCalcButton("3"));
			
			var linRow2 = new LinearLayout();
			linRow2.Orientation = Orientation.Horizontal;
            linRow2.AddView(NewCalcButton("4"));
			linRow2.AddView(NewCalcButton("5"));
			linRow2.AddView(NewCalcButton("6"));
			
			var linRow3 = new LinearLayout();
			linRow3.Orientation = Orientation.Horizontal;
            linRow3.AddView(NewCalcButton("7"));
			linRow3.AddView(NewCalcButton("8"));
			linRow3.AddView(NewCalcButton("9"));
			
			var linRow4 = new LinearLayout();
			linRow4.Orientation = Orientation.Horizontal;
            linRow4.AddView(NewCalcButton("0"));
			linRow4.AddView(NewCalcButton("."));
			linRow4.AddView(NewCalcButton("="));
			
			var linRow5 = new LinearLayout();
			linRow5.Orientation = Orientation.Horizontal;
            linRow5.AddView(NewCalcButton("("));
			linRow5.AddView(NewCalcButton(")"));
			linRow5.AddView(NewCalcButton("%"));
			
			var linRow6 = new LinearLayout();
			linRow6.Orientation = Orientation.Horizontal;
            linRow6.AddView(NewCalcButton("sqrt"));
			linRow6.AddView(NewCalcButton("pow"));
			linRow6.AddView(NewCalcButton("mod"));
            
            linNum.AddView(linRow1);
			linNum.AddView(linRow2);
			linNum.AddView(linRow3);
			linNum.AddView(linRow4);
			linNum.AddView(linRow5);
			linNum.AddView(linRow6);
			
            return linNum;
        }
		
		private static LinearLayout GenerateArithmetics()
		{
			var linArth = new LinearLayout();
			linArth.Orientation = Orientation.Vertical;
			
			linArth.AddView(NewCalcButton("+"));	
			linArth.AddView(NewCalcButton("-"));	
			linArth.AddView(NewCalcButton("*"));	
			linArth.AddView(NewCalcButton("/"));
			linArth.AddView(NewCalcButton("DEL"));
			linArth.AddView(NewCalcButton("CLR"));
			return linArth;
		}
        
		//Helper method for adding buttons to the calculator
        private static Button NewCalcButton(string text)
        {
           var btn = new Button(); 
           btn.Text = text;
           btn.Click += PerformButtonAction;
           return btn;
        }
        
		
		//Decide what to do when a button is pressed
        private static void PerformButtonAction(object sender, EventArgs args)
        {
            var btn = (Button)sender;
			if (btn.Text == "=") Task.Run(()=> Calculate(txtDisplay.Text));
			else if (btn.Text == "CLR")
			{
			    txtDisplay.Text = string.Empty;
			    lblError.Text=string.Empty;
			}
			else if (btn.Text == "DEL") //Remove character at cursor
			{
				if (txtDisplay.Text.Length == 0) return;
			    var ind = txtDisplay.SelectionStart;
			    txtDisplay.Text = txtDisplay.Text.Remove(ind - 1, 1);
			    txtDisplay.SetSelection(--ind);
			}
			else
			{
				var text = btn.Text;
				var ind = txtDisplay.SelectionStart;
				if (text == "pow") text = "pow(,)";
				else if (text == "sqrt") text = "sqrt()";
			    else if (text == "mod") text = "mod()";
				
			    txtDisplay.Text = txtDisplay.Text.Insert(ind, text);
			    txtDisplay.SetSelection(ind + text.Length);
			}
        }
		
		
		private static void Calculate(string expr)
		{
			try
			{
			    InitEvaluator();
			    
				//Convert shorthand pseudo-methods into real ones
				expr = expr.Split('=')[0];
				expr = expr.Replace("pow", "Math.Pow");
				expr = expr.Replace("sqrt", "Math.Sqrt");
				expr = expr.Replace("mod", "Math.Abs");
				
				var ok = evaluator.Run(evalCodeStart + expr + ";}}"); //Create evaluation class
				if (!ok) throw new ApplicationException(sw.ToString());
				ok = evaluator.Run("Calc.Eval()"); //Start evaluatuion
				if (!ok || !string.IsNullOrWhiteSpace(sw.ToString())) 
				{
					//throw exception due to evaluation error.
				    throw new ApplicationException(sw.ToString());
				}
				//Show the result
				Ui.RunOnUiThread(()=>
				{
				  txtDisplay.Text = txtDisplay.Text.Split('=')[0] + "=" + GetEvalResult();
				  txtDisplay.SetSelection(txtDisplay.Text.Length);
				  lblError.Text = string.Empty;
				});
			}
			catch(Exception ex)
			{
				//Show the exception message
			    Ui.RunOnUiThread(()=>
			    {
			    	lblError.Text = ex.Message;
				    if (ex.InnerException != null)
				    {
				       lblError.Text +=  ex.InnerException.Message;
				    }
			    });
			}
		}
		
		//Get evaluator result variable and process it for display
		private static string GetEvalResult()
		{
			var errorRegex = @"\((\d*),\s*(\d*)\):(.*)";
			var res = evaluator.GetVars().Replace("object result = ", string.Empty).Trim();
			if (Regex.IsMatch(res,  errorRegex))
			{
				var offset = evalCodeStart.Length +  "using System;\nobject result;".Length;
				var matches = Regex.Matches(res, errorRegex);
				res = string.Empty;
				foreach (Match match in matches)
				{
					var pos = int.Parse(match.Groups[2].Value) - offset;
					var error = match.Groups[3].Value;
					res += $"{pos}: {error}";
				}
			}
			
			return res;
		}
		
		private static void InitEvaluator()
		{
		   sw = new StringWriter();
           var setting = new CompilerSettings();
           var pr = new ConsoleReportPrinter(sw);
           var ctx = new CompilerContext(setting, pr);
           evaluator = new Evaluator(ctx);
			
		   evaluator.Run("using System;"); //We probably need this
		   evaluator.Run("object result;"); //Result variable, very important.
		}
    }
}