using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using Interpreter; using Microsoft.FSharp.Core; namespace InterpreterGui { public partial class MainWindow : Window { // The F# interpreter engine. Persists variables across evaluations. private readonly Engine _engine = new Engine(); // Pixels per one unit on the maths axes. private const double Scale = 25.0; public MainWindow() { InitializeComponent(); Loaded += (s, e) => DrawAxes(); } private void InputBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { RunInput(); } } private void RunButton_Click(object sender, RoutedEventArgs e) { RunInput(); } private void ClearButton_Click(object sender, RoutedEventArgs e) { DrawCanvas.Children.Clear(); DrawAxes(); StatusText.Text = "Canvas cleared."; } private void DrawCanvas_SizeChanged(object sender, SizeChangedEventArgs e) { // Redraw axes (and nothing else) whenever the window is resized. DrawCanvas.Children.Clear(); DrawAxes(); } /// /// Decides whether the input is a line equation ("y = ax + b") or a /// plain expression / assignment, and routes it accordingly. /// private void RunInput() { string text = InputBox.Text; if (text == null) { return; } string trimmed = text.Trim(); if (trimmed.Length == 0) { return; } // Detect "y = ..." form (allowing for whitespace) to route to the // line-drawing parser instead of the general expression evaluator. string noLeadingWs = trimmed.TrimStart(); bool looksLikeLine = noLeadingWs.Length > 0 && (noLeadingWs[0] == 'y' || noLeadingWs[0] == 'Y') && noLeadingWs.TrimStart('y', 'Y', ' ', '\t').StartsWith("="); if (looksLikeLine) { FSharpResult, string> result = _engine.ParseLine(trimmed); if (result.IsOk) { double a = result.ResultValue.Item1; double b = result.ResultValue.Item2; OutputBox.Text = "Line: y = " + a + "x + " + b; StatusText.Text = "Drew line y = " + a + "x + " + b; DrawLine(a, b); } else { OutputBox.Text = result.ErrorValue; StatusText.Text = "Line parse failed."; } } else { FSharpResult result = _engine.Evaluate(trimmed); if (result.IsOk) { OutputBox.Text = result.ResultValue; StatusText.Text = "OK."; } else { OutputBox.Text = result.ErrorValue; StatusText.Text = "Error."; } } } // ------------------------------------------------------------------ // Drawing (uses the WPF Canvas / Shapes API - explicitly permitted) // ------------------------------------------------------------------ private double OriginX => DrawCanvas.ActualWidth / 2.0; private double OriginY => DrawCanvas.ActualHeight / 2.0; /// Converts a maths coordinate (x, y) into a canvas pixel point. private Point ToCanvas(double x, double y) { return new Point(OriginX + x * Scale, OriginY - y * Scale); } private void DrawAxes() { double w = DrawCanvas.ActualWidth; double h = DrawCanvas.ActualHeight; if (w <= 0 || h <= 0) { return; } var xAxis = new Line { X1 = 0, Y1 = OriginY, X2 = w, Y2 = OriginY, Stroke = Brushes.Black, StrokeThickness = 1.5 }; var yAxis = new Line { X1 = OriginX, Y1 = 0, X2 = OriginX, Y2 = h, Stroke = Brushes.Black, StrokeThickness = 1.5 }; DrawCanvas.Children.Add(xAxis); DrawCanvas.Children.Add(yAxis); // Tick marks every unit, labelled every 5 units to avoid clutter. int maxTicksX = (int)(w / (2 * Scale)) + 1; for (int i = -maxTicksX; i <= maxTicksX; i++) { if (i == 0) continue; double px = OriginX + i * Scale; var tick = new Line { X1 = px, Y1 = OriginY - 4, X2 = px, Y2 = OriginY + 4, Stroke = Brushes.Gray, StrokeThickness = 1 }; DrawCanvas.Children.Add(tick); if (i % 5 == 0) { var label = new TextBlock { Text = i.ToString(), FontSize = 10, Foreground = Brushes.Gray }; Canvas.SetLeft(label, px - 6); Canvas.SetTop(label, OriginY + 6); DrawCanvas.Children.Add(label); } } int maxTicksY = (int)(h / (2 * Scale)) + 1; for (int i = -maxTicksY; i <= maxTicksY; i++) { if (i == 0) continue; double py = OriginY - i * Scale; var tick = new Line { X1 = OriginX - 4, Y1 = py, X2 = OriginX + 4, Y2 = py, Stroke = Brushes.Gray, StrokeThickness = 1 }; DrawCanvas.Children.Add(tick); if (i % 5 == 0) { var label = new TextBlock { Text = i.ToString(), FontSize = 10, Foreground = Brushes.Gray }; Canvas.SetLeft(label, OriginX + 6); Canvas.SetTop(label, py - 6); DrawCanvas.Children.Add(label); } } } /// Draws the line y = a*x + b across the full visible width of the canvas. private void DrawLine(double a, double b) { double w = DrawCanvas.ActualWidth; if (w <= 0) { return; } double xLeftMath = -OriginX / Scale; double xRightMath = (w - OriginX) / Scale; double yLeftMath = a * xLeftMath + b; double yRightMath = a * xRightMath + b; Point p1 = ToCanvas(xLeftMath, yLeftMath); Point p2 = ToCanvas(xRightMath, yRightMath); var line = new Line { X1 = p1.X, Y1 = p1.Y, X2 = p2.X, Y2 = p2.Y, Stroke = Brushes.Blue, StrokeThickness = 2 }; DrawCanvas.Children.Add(line); } } }