From dd6fbf3be773db1dd5d6fe942d7c7ca06189513c Mon Sep 17 00:00:00 2001
From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com>
Date: Tue, 24 May 2022 19:49:25 +0200
Subject: [PATCH] Add system statements ('exc')
---
.editorconfig | 3 +
YesNt-Interpreter.sln | 16 +-
.../Properties/launchSettings.json | 12 +
YesNt.CodeEditor/YesNt.CodeEditor.csproj | 1 +
.../YesNt.Interpreter.Tests.csproj | 2 +
.../Statements/SystemStatements.cs | 111 ++++++
YesNt.Interpreter/Utilities/FixedProcess.cs | 316 ++++++++++++++++++
YesNt.Interpreter/YesNt.Interpreter.csproj | 1 +
8 files changed, 461 insertions(+), 1 deletion(-)
create mode 100644 YesNt.CodeEditor/Properties/launchSettings.json
create mode 100644 YesNt.Interpreter/Statements/SystemStatements.cs
create mode 100644 YesNt.Interpreter/Utilities/FixedProcess.cs
diff --git a/.editorconfig b/.editorconfig
index 051cab7..0ec1ee4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -64,6 +64,9 @@ dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
+# S1172: Unused method parameters should be removed
+dotnet_diagnostic.S1172.severity = none
+
[*.{cs,vb}]
#### Naming styles ####
diff --git a/YesNt-Interpreter.sln b/YesNt-Interpreter.sln
index ed6df67..0181ad5 100644
--- a/YesNt-Interpreter.sln
+++ b/YesNt-Interpreter.sln
@@ -12,26 +12,40 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YesNt.Interpreter.Tests", "YesNt.Interpreter.Tests\YesNt.Interpreter.Tests.csproj", "{2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YesNt.Interpreter.Tests", "YesNt.Interpreter.Tests\YesNt.Interpreter.Tests.csproj", "{2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Debug|x64.ActiveCfg = Debug|x64
+ {33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Debug|x64.Build.0 = Debug|x64
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Release|x64.ActiveCfg = Release|x64
+ {33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Release|x64.Build.0 = Release|x64
{E40573DB-912A-4871-BB65-3EE6E7D72E79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E40573DB-912A-4871-BB65-3EE6E7D72E79}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E40573DB-912A-4871-BB65-3EE6E7D72E79}.Debug|x64.ActiveCfg = Debug|x64
+ {E40573DB-912A-4871-BB65-3EE6E7D72E79}.Debug|x64.Build.0 = Debug|x64
{E40573DB-912A-4871-BB65-3EE6E7D72E79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E40573DB-912A-4871-BB65-3EE6E7D72E79}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E40573DB-912A-4871-BB65-3EE6E7D72E79}.Release|x64.ActiveCfg = Release|x64
+ {E40573DB-912A-4871-BB65-3EE6E7D72E79}.Release|x64.Build.0 = Release|x64
{2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Debug|x64.ActiveCfg = Debug|x64
+ {2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Debug|x64.Build.0 = Debug|x64
{2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Release|x64.ActiveCfg = Release|x64
+ {2F95DCA7-3E43-4F2E-8FB2-067F0EC962B1}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/YesNt.CodeEditor/Properties/launchSettings.json b/YesNt.CodeEditor/Properties/launchSettings.json
new file mode 100644
index 0000000..161eba8
--- /dev/null
+++ b/YesNt.CodeEditor/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "YesNt.CodeEditor": {
+ "commandName": "Project"
+ },
+ "WSL": {
+ "commandName": "WSL2",
+ "environmentVariables": {},
+ "distributionName": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/YesNt.CodeEditor/YesNt.CodeEditor.csproj b/YesNt.CodeEditor/YesNt.CodeEditor.csproj
index 68e1220..16be40b 100644
--- a/YesNt.CodeEditor/YesNt.CodeEditor.csproj
+++ b/YesNt.CodeEditor/YesNt.CodeEditor.csproj
@@ -3,6 +3,7 @@
Exe
net5.0
+ AnyCPU;x64
diff --git a/YesNt.Interpreter.Tests/YesNt.Interpreter.Tests.csproj b/YesNt.Interpreter.Tests/YesNt.Interpreter.Tests.csproj
index cf7cd15..9e6dacd 100644
--- a/YesNt.Interpreter.Tests/YesNt.Interpreter.Tests.csproj
+++ b/YesNt.Interpreter.Tests/YesNt.Interpreter.Tests.csproj
@@ -5,6 +5,8 @@
enable
false
+
+ AnyCPU;x64
diff --git a/YesNt.Interpreter/Statements/SystemStatements.cs b/YesNt.Interpreter/Statements/SystemStatements.cs
new file mode 100644
index 0000000..fb48103
--- /dev/null
+++ b/YesNt.Interpreter/Statements/SystemStatements.cs
@@ -0,0 +1,111 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+
+using YesNt.Interpreter.Attributes;
+using YesNt.Interpreter.Enums;
+using YesNt.Interpreter.Runtime;
+using YesNt.Interpreter.Utilities;
+
+namespace YesNt.Interpreter.Statements
+{
+ internal class SystemStatements : StatementRuntimeInformation
+ {
+ [Statement("exc", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Magenta, Priority = Priority.Low, Seperator = "|")]
+ public void ExecuteProgramWithArgs(string input)
+ {
+ string[] parts = input.FromSaveString().Split('|');
+ parts[0] = parts[0].Trim();
+
+ string[] functionArgumets = parts[1].Split(',');
+
+ foreach (string argumanet in functionArgumets)
+ {
+ RuntimeInfo.InParametersStack.Push(argumanet.Trim());
+ }
+
+ try
+ {
+ StartProcess(parts[0], string.Join(string.Empty, RuntimeInfo.InParametersStack.Reverse()));
+ }
+ catch (FileNotFoundException)
+ {
+ RuntimeInfo.Exit($"Cannot find file \"{parts[0]}\".", false);
+ }
+ catch (Win32Exception ex)
+ {
+ RuntimeInfo.Exit($"Failed to start \"{parts[0]}\". {ex.Message}", false);
+ }
+
+ //HACK: Clear line to avoid execution from other "exc" statement
+ RuntimeInfo.CurrentLine = string.Empty;
+ }
+
+ [Statement("exc", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Magenta, Priority = Priority.VeryLow)]
+ public void ExecuteProgram(string input)
+ {
+ try
+ {
+ StartProcess(input, string.Join(string.Empty, RuntimeInfo.InParametersStack.Reverse()));
+ }
+ catch (FileNotFoundException)
+ {
+ RuntimeInfo.Exit($"Cannot find file \"{input}\".", false);
+ }
+ catch (Win32Exception ex)
+ {
+ RuntimeInfo.Exit($"Failed to start \"{input}\". {ex.Message}", false);
+ }
+ }
+
+ private void StartProcess(string name, string args)
+ {
+ FixedProcess process = new FixedProcess
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = name,
+ Arguments = args,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true
+ }
+ };
+
+ process.OutputDataReceived += Process_OutputDataReceived;
+ process.ErrorDataReceived += Process_ErrorDataReceived;
+
+ process.Start();
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ process.WaitForExit();
+
+ RuntimeInfo.InParametersStack.Clear();
+ RuntimeInfo.OutParametersStack.Clear();
+ RuntimeInfo.OutParametersStack.Push(process.ExitCode.ToString());
+ }
+
+ private void Process_ErrorDataReceived(object sender, Utilities.DataReceivedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(e.Data))
+ {
+ return;
+ }
+
+ RuntimeInfo.OutParametersStack.Push(e.Data);
+ Console.Write("Error: " + e.Data);
+ }
+
+ private void Process_OutputDataReceived(object sender, Utilities.DataReceivedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(e.Data))
+ {
+ return;
+ }
+
+ RuntimeInfo.OutParametersStack.Push(e.Data);
+ Console.Write(e.Data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Utilities/FixedProcess.cs b/YesNt.Interpreter/Utilities/FixedProcess.cs
new file mode 100644
index 0000000..65a32e2
--- /dev/null
+++ b/YesNt.Interpreter/Utilities/FixedProcess.cs
@@ -0,0 +1,316 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Threading;
+
+namespace YesNt.Interpreter.Utilities
+{
+ internal delegate void UserCallBack(string data);
+
+ public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e);
+
+ public class FixedProcess : Process
+ {
+ internal AsyncStreamReader output;
+ internal AsyncStreamReader error;
+
+ public new event DataReceivedEventHandler OutputDataReceived;
+
+ public new event DataReceivedEventHandler ErrorDataReceived;
+
+ public new void BeginOutputReadLine()
+ {
+ Stream baseStream = StandardOutput.BaseStream;
+ output = new AsyncStreamReader(this, baseStream, new UserCallBack(FixedOutputReadNotifyUser), StandardOutput.CurrentEncoding);
+ output.BeginReadLine();
+ }
+
+ public new void BeginErrorReadLine()
+ {
+ Stream baseStream = StandardError.BaseStream;
+ error = new AsyncStreamReader(this, baseStream, new UserCallBack(FixedErrorReadNotifyUser), StandardError.CurrentEncoding);
+ error.BeginReadLine();
+ }
+
+ internal void FixedOutputReadNotifyUser(string data)
+ {
+ DataReceivedEventHandler outputDataReceived = OutputDataReceived;
+ if (outputDataReceived != null)
+ {
+ DataReceivedEventArgs dataReceivedEventArgs = new DataReceivedEventArgs(data);
+ if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
+ {
+ SynchronizingObject.Invoke(outputDataReceived, new object[]
+ {
+ this,
+ dataReceivedEventArgs
+ });
+ return;
+ }
+ outputDataReceived(this, dataReceivedEventArgs);
+ }
+ }
+
+ internal void FixedErrorReadNotifyUser(string data)
+ {
+ DataReceivedEventHandler errorDataReceived = ErrorDataReceived;
+ if (errorDataReceived != null)
+ {
+ DataReceivedEventArgs dataReceivedEventArgs = new DataReceivedEventArgs(data);
+ if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
+ {
+ SynchronizingObject.Invoke(errorDataReceived, new object[]
+ {
+ this,
+ dataReceivedEventArgs
+ });
+ return;
+ }
+ errorDataReceived(this, dataReceivedEventArgs);
+ }
+ }
+ }
+
+ internal class AsyncStreamReader : IDisposable
+ {
+ internal const int DefaultBufferSize = 1024;
+ private Stream stream;
+ private Encoding encoding;
+ private Decoder decoder;
+ private byte[] byteBuffer;
+ private char[] charBuffer;
+ private UserCallBack userCallBack;
+ private bool cancelOperation;
+ private ManualResetEvent eofEvent;
+ private readonly Queue messageQueue;
+ private StringBuilder sb;
+ private bool bLastCarriageReturn;
+ public virtual Encoding CurrentEncoding => encoding;
+ public virtual Stream BaseStream => stream;
+
+ internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding) : this(process, stream, callback, encoding, 1024)
+ {
+ }
+
+ internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize)
+ {
+ Init(process, stream, callback, encoding, bufferSize);
+ messageQueue = new Queue();
+ }
+
+ private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize)
+ {
+ this.stream = stream;
+ this.encoding = encoding;
+ userCallBack = callback;
+ decoder = encoding.GetDecoder();
+ if (bufferSize < 128)
+ {
+ bufferSize = 128;
+ }
+ byteBuffer = new byte[bufferSize];
+ int _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
+ charBuffer = new char[_maxCharsPerBuffer];
+ cancelOperation = false;
+ eofEvent = new ManualResetEvent(false);
+ sb = null;
+ bLastCarriageReturn = false;
+ }
+
+ public virtual void Close()
+ {
+ Dispose(true);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing && stream != null)
+ {
+ stream.Close();
+ }
+ if (stream != null)
+ {
+ stream = null;
+ encoding = null;
+ decoder = null;
+ byteBuffer = null;
+ charBuffer = null;
+ }
+ if (eofEvent != null)
+ {
+ eofEvent.Close();
+ eofEvent = null;
+ }
+ }
+
+ internal void BeginReadLine()
+ {
+ if (cancelOperation)
+ {
+ cancelOperation = false;
+ }
+ if (sb == null)
+ {
+ sb = new StringBuilder(1024);
+ stream.BeginRead(byteBuffer, 0, byteBuffer.Length, new AsyncCallback(ReadBuffer), null);
+ return;
+ }
+ FlushMessageQueue();
+ }
+
+ internal void CancelOperation()
+ {
+ cancelOperation = true;
+ }
+
+ private void ReadBuffer(IAsyncResult ar)
+ {
+ int num;
+ try
+ {
+ num = stream.EndRead(ar);
+ }
+ catch (IOException)
+ {
+ num = 0;
+ }
+ catch (OperationCanceledException)
+ {
+ num = 0;
+ }
+ if (num == 0)
+ {
+ lock (messageQueue)
+ {
+ if (sb.Length != 0)
+ {
+ messageQueue.Enqueue(sb.ToString());
+ sb.Length = 0;
+ }
+ messageQueue.Enqueue(null);
+ }
+ try
+ {
+ FlushMessageQueue();
+ return;
+ }
+ finally
+ {
+ eofEvent.Set();
+ }
+ }
+ int chars = decoder.GetChars(byteBuffer, 0, num, charBuffer, 0);
+ sb.Append(charBuffer, 0, chars);
+ GetLinesFromStringBuilder();
+ stream.BeginRead(byteBuffer, 0, byteBuffer.Length, new AsyncCallback(ReadBuffer), null);
+ }
+
+ private void GetLinesFromStringBuilder()
+ {
+ int i = 0;
+ int num = 0;
+ int length = sb.Length;
+ if (bLastCarriageReturn && length > 0 && sb[0] == '\n')
+ {
+ i = 1;
+ num = 1;
+ bLastCarriageReturn = false;
+ }
+ while (i < length)
+ {
+ char c = sb[i];
+ if (c == '\r' || c == '\n')
+ {
+ if (c == '\r' && i + 1 < length && sb[i + 1] == '\n')
+ {
+ i++;
+ }
+
+ string obj = sb.ToString(num, i + 1 - num);
+
+ num = i + 1;
+
+ lock (messageQueue)
+ {
+ messageQueue.Enqueue(obj);
+ }
+ }
+ i++;
+ }
+
+ // Flush Fix: Send Whatever is left in the buffer
+ string endOfBuffer = sb.ToString(num, length - num);
+ lock (messageQueue)
+ {
+ messageQueue.Enqueue(endOfBuffer);
+ num = length;
+ }
+ // End Flush Fix
+
+ if (sb[length - 1] == '\r')
+ {
+ bLastCarriageReturn = true;
+ }
+ if (num < length)
+ {
+ sb.Remove(0, num);
+ }
+ else
+ {
+ sb.Length = 0;
+ }
+ FlushMessageQueue();
+ }
+
+ private void FlushMessageQueue()
+ {
+ while (messageQueue.Count > 0)
+ {
+ lock (messageQueue)
+ {
+ if (messageQueue.Count > 0)
+ {
+ string data = (string)messageQueue.Dequeue();
+ if (!cancelOperation)
+ {
+ userCallBack(data);
+ }
+ }
+ }
+ }
+ }
+
+ internal void WaitUtilEOF()
+ {
+ if (eofEvent != null)
+ {
+ eofEvent.WaitOne();
+ eofEvent.Close();
+ eofEvent = null;
+ }
+ }
+ }
+
+ public class DataReceivedEventArgs : EventArgs
+ {
+ internal string _data;
+
+ /// Gets the line of characters that was written to a redirected output stream.
+ /// The line that was written by an associated to its redirected or stream.
+ /// 2
+ public string Data => _data;
+
+ internal DataReceivedEventArgs(string data)
+ {
+ _data = data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/YesNt.Interpreter/YesNt.Interpreter.csproj b/YesNt.Interpreter/YesNt.Interpreter.csproj
index c017137..bd4ef12 100644
--- a/YesNt.Interpreter/YesNt.Interpreter.csproj
+++ b/YesNt.Interpreter/YesNt.Interpreter.csproj
@@ -6,6 +6,7 @@
Exe
+ AnyCPU;x64