- Add functions

- Code improvements
This commit is contained in:
Stone-Red-Code
2021-11-19 20:49:55 +01:00
parent 812b780942
commit 2a7e8074ba
27 changed files with 356 additions and 156 deletions
@@ -0,0 +1,13 @@
using System;
namespace YesNt.Interpreter.Runtime
{
public class DebugEventArgs : EventArgs
{
public int LineNumber { get; internal set; }
public string CurrentLine { get; internal set; }
public string OriginalLine { get; internal set; }
public int TaskId { get; internal set; }
public bool IsTask { get; internal set; }
}
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace YesNt.Interpreter.Runtime
{
internal class FunctionScope
{
public int CallerLine { get; }
public Dictionary<string, string> Variables { get; } = new();
public FunctionScope(int callerLine)
{
CallerLine = callerLine;
}
}
}
@@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using YesNt.Interpreter.Runtime;
using YesNt.Interpreter.Utilities;
namespace YesNt.Interpreter
{
internal class RuntimeInformation
{
private RuntimeInformation parentRuntimeInformation;
private static int internalTaskId = 0;
private int taskId = 0;
private readonly Dictionary<string, string> topVariables = new();
public Dictionary<string, string> GloablVariables { get; set; } = new();
public Dictionary<string, int> Labels { get; } = new();
public Dictionary<string, int> Functions { get; } = new();
public Stack<FunctionScope> FunctionCallStack { get; } = new();
public List<string> Lines { get; set; } = new();
public string CurrentLine { get; set; } = string.Empty;
public string SearchLabel { get; set; } = string.Empty;
public string SearchFunction { get; set; } = string.Empty;
public int LineNumber { get; set; } = 0;
public bool Stop { get; private set; } = false;
public bool StopAllTasks { get; private set; } = false;
public bool IsDebugMode { get; set; } = false;
public string CurrentFilePath { get; set; } = string.Empty;
public bool IsTask => ParentRuntimeInformation is not null;
public int TaskId => IsTask ? taskId : 0;
public bool InternalIsInFunction { get; private set; } = false;
public bool IsInFunction
{
get => InternalIsInFunction || FunctionCallStack.Count > 0;
set => InternalIsInFunction = value;
}
public Dictionary<string, string> Variables
{
get
{
if (FunctionCallStack.Count == 0)
{
return topVariables;
}
else
{
return FunctionCallStack.Peek().Variables;
}
}
}
public RuntimeInformation ParentRuntimeInformation
{
get => parentRuntimeInformation;
set
{
parentRuntimeInformation = value;
if (parentRuntimeInformation is not null)
{
parentRuntimeInformation.OnExit += ParentRuntimeInformation_OnExit;
}
}
}
public bool IsSearching => !string.IsNullOrWhiteSpace(SearchLabel + SearchFunction) || IsInFunction && FunctionCallStack.Count == 0;
private event Action<string, bool> OnExit;
public event Action<string> OnDebugOutput;
public event Action<DebugEventArgs> OnLineExecuted;
private void ParentRuntimeInformation_OnExit(string exitMessage, bool stopChildTasks)
{
Exit($"Terminated by parent task", stopChildTasks);
}
public void WriteLine(string output, bool forceWrite = false)
{
if (Stop && !forceWrite || parentRuntimeInformation?.StopAllTasks == true && !forceWrite)
{
return;
}
if (IsDebugMode)
{
if (IsTask)
{
parentRuntimeInformation.WriteLine(output.FromSaveString(), forceWrite);
}
else
{
OnDebugOutput?.Invoke(output.FromSaveString() + Environment.NewLine);
}
}
else
{
Console.WriteLine(output.FromSaveString());
}
}
public void Write(string output, bool forceWrite = false)
{
if (Stop && !forceWrite || parentRuntimeInformation?.StopAllTasks == true && !forceWrite)
{
return;
}
if (IsDebugMode)
{
if (IsTask)
{
parentRuntimeInformation.Write(output.FromSaveString(), forceWrite);
}
else
{
OnDebugOutput?.Invoke(output.FromSaveString());
}
}
else
{
Console.Write(output.FromSaveString());
}
}
public void Exit(string message, bool stopAllTasks)
{
if (!Stop)
{
WriteLine($"{Environment.NewLine}[{(IsTask ? $"Task: {TaskId}" : "The process")} was terminated at line {LineNumber + 1} with the message: {message}]", true);
Stop = true;
}
if (stopAllTasks == true && StopAllTasks == false)
{
StopAllTasks = true;
OnExit?.Invoke(message, StopAllTasks);
parentRuntimeInformation?.Exit("Terminated by child task", true);
}
}
public void LineExecuted(DebugEventArgs debugEventArgs)
{
if (IsTask)
{
parentRuntimeInformation.LineExecuted(debugEventArgs);
}
else
{
OnLineExecuted?.Invoke(debugEventArgs);
}
}
public void Reset()
{
topVariables.Clear();
Lines.Clear();
GloablVariables.Clear();
Labels.Clear();
Functions.Clear();
FunctionCallStack.Clear();
ParentRuntimeInformation = null;
SearchLabel = string.Empty;
SearchFunction = string.Empty;
CurrentFilePath = string.Empty;
CurrentLine = string.Empty;
Stop = false;
StopAllTasks = false;
IsDebugMode = false;
IsInFunction = false;
InternalIsInFunction = false;
LineNumber = 0;
taskId = internalTaskId + 1;
internalTaskId++;
}
}
}
@@ -0,0 +1,16 @@
using System;
using YesNt.Interpreter.Enums;
namespace YesNt.Interpreter.Runtime
{
public class StatementInformation
{
public string Name { get; internal set; }
public SearchMode SearchMode { get; internal set; }
public SpaceAround SpaceAround { get; internal set; }
public ConsoleColor Color { get; internal set; }
public bool IgnoreSyntaxHighlighting { get; internal set; }
public string Seperator { get; set; }
}
}
@@ -0,0 +1,7 @@
namespace YesNt.Interpreter
{
internal abstract class StatementRuntimeInformation
{
public RuntimeInformation RuntimeInfo { get; set; }
}
}
@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using YesNt.Interpreter.Attributes;
using YesNt.Interpreter.Enums;
using YesNt.Interpreter.Runtime;
using YesNt.Interpreter.Utilities;
namespace YesNt.Interpreter
{
public class YesNtInterpreter
{
private readonly RuntimeInformation runtimeInfo = new RuntimeInformation();
private Dictionary<StatementAttribute, Action<string>> statements = new();
private List<KeyValuePair<StaticStatementAttribute, Action>> staticStatements = new();
public ReadOnlyCollection<StatementInformation> StatementInformation
{
get
{
List<StatementInformation> informations = statements.Select(s =>
{
return new StatementInformation()
{
Name = s.Key.Name,
SearchMode = s.Key.SearchMode,
SpaceAround = s.Key.SpaceAround,
Color = s.Key.Color,
IgnoreSyntaxHighlighting = s.Key.IgnoreSyntaxHighlighting,
Seperator = s.Key.Seperator
};
}).ToList();
return new ReadOnlyCollection<StatementInformation>(informations);
}
}
public event Action<DebugEventArgs> OnLineExecuted;
public event Action<string> OnDebugOutput;
public void Stop()
{
runtimeInfo.Exit("Terminated by external process", true);
}
public void Initialize()
{
Assembly assembly = Assembly.GetExecutingAssembly();
Type[] types = assembly.GetTypes();
IEnumerable<Type> statementRuntimeInfos = types.Where(t => t.IsSubclassOf(typeof(StatementRuntimeInformation)));
statements.Clear();
foreach (Type type in statementRuntimeInfos)
{
object statementInfo = Activator.CreateInstance(type);
MethodInfo[] methodInfos = statementInfo.GetType().GetMethods();
StatementRuntimeInformation statementRuntimeInfo = statementInfo as StatementRuntimeInformation;
statementRuntimeInfo.RuntimeInfo = runtimeInfo;
foreach (MethodInfo methodInfo in methodInfos)
{
StatementAttribute statementAttribute = methodInfo.GetCustomAttribute<StatementAttribute>();
if (statementAttribute is not null)
{
Action<string> method = methodInfo.CreateDelegate(typeof(Action<string>), statementInfo) as Action<string>;
statements.Add(statementAttribute, method);
}
StaticStatementAttribute staticStatementAttribute = methodInfo.GetCustomAttribute<StaticStatementAttribute>();
if (staticStatementAttribute is not null)
{
Action method = methodInfo.CreateDelegate(typeof(Action), statementInfo) as Action;
staticStatements.Add(new(staticStatementAttribute, method));
}
}
}
statements = statements.OrderBy(s => s.Key.Priority).ToDictionary(x => x.Key, x => x.Value);
staticStatements = staticStatements.OrderBy(s => s.Key.Priority).ToList();
runtimeInfo.OnDebugOutput += (s) => OnDebugOutput?.Invoke(s);
runtimeInfo.OnLineExecuted += (DebugEventArgs e) => OnLineExecuted.Invoke(e);
}
public void Execute(string path, bool isDebugMode = false)
{
runtimeInfo.Reset();
runtimeInfo.IsDebugMode = isDebugMode;
LoadFile(path);
Execute();
}
internal void Execute(List<string> lines, Dictionary<string, string> gloablVariables, int startLine, RuntimeInformation parentRuntimeInformation)
{
runtimeInfo.Reset();
runtimeInfo.IsDebugMode = parentRuntimeInformation.IsDebugMode;
runtimeInfo.Lines = lines;
runtimeInfo.LineNumber = startLine;
runtimeInfo.ParentRuntimeInformation = parentRuntimeInformation;
runtimeInfo.GloablVariables = gloablVariables;
if (parentRuntimeInformation.StopAllTasks)
{
runtimeInfo.Exit($"Parent task was terminated!", parentRuntimeInformation.StopAllTasks);
return;
}
Execute();
}
private void Execute()
{
for (; runtimeInfo.LineNumber < runtimeInfo.Lines.Count; runtimeInfo.LineNumber++)
{
if (runtimeInfo.Stop)
{
break;
}
runtimeInfo.CurrentLine = runtimeInfo.Lines[runtimeInfo.LineNumber].TrimEnd().Replace("\r", string.Empty);
DebugEventArgs debugEventArgs = new DebugEventArgs()
{
LineNumber = runtimeInfo.LineNumber + 1,
OriginalLine = runtimeInfo.CurrentLine.FromSaveString(),
IsTask = runtimeInfo.IsTask,
TaskId = runtimeInfo.TaskId
};
if (string.IsNullOrWhiteSpace(runtimeInfo.CurrentLine))
{
continue;
}
foreach (KeyValuePair<StaticStatementAttribute, Action> staticStatement in staticStatements)
{
StaticStatementAttribute staticStatementAttribute = staticStatement.Key;
if (staticStatementAttribute.ExecuteInSearchLabelMode == false && runtimeInfo.IsSearching)
{
continue;
}
staticStatement.Value.Invoke();
}
bool statementFound = false;
bool notSearchingLabel = !runtimeInfo.IsSearching;
foreach (KeyValuePair<StatementAttribute, Action<string>> statement in statements)
{
StatementAttribute statementAttribute = statement.Key;
if (statementAttribute.ExecuteInSearchMode == false && runtimeInfo.IsSearching)
{
statementFound = true;
continue;
}
if (runtimeInfo.Stop)
{
break;
}
string name = statementAttribute.SpaceAround switch
{
SpaceAround.StartEnd => $" {statementAttribute.Name.Trim()} ",
SpaceAround.Start => $" {statementAttribute.Name.Trim()}",
SpaceAround.End => $"{statementAttribute.Name.Trim()} ",
_ => statementAttribute.Name
};
if (statementAttribute.SearchMode == SearchMode.StartOfLine && runtimeInfo.CurrentLine.StartsWith(name))
{
string copyLine = statementAttribute.KeepStatementInArgs ? runtimeInfo.CurrentLine : runtimeInfo.CurrentLine.Remove(0, name.Length);
statement.Value.Invoke(copyLine);
statementFound = true;
}
else if (statementAttribute.SearchMode == SearchMode.Contains && $" {runtimeInfo.CurrentLine} ".Contains(name))
{
bool leadingWhitespace = runtimeInfo.CurrentLine.StartsWith(' ');
runtimeInfo.CurrentLine = $" {runtimeInfo.CurrentLine} ";
string copyLine = statementAttribute.KeepStatementInArgs ? runtimeInfo.CurrentLine : runtimeInfo.CurrentLine.Replace(name, string.Empty);
statement.Value.Invoke(copyLine);
statementFound = true;
if (!leadingWhitespace)
{
runtimeInfo.CurrentLine = runtimeInfo.CurrentLine.Trim();
}
else
{
runtimeInfo.CurrentLine = runtimeInfo.CurrentLine.TrimEnd();
}
}
else if (statementAttribute.SearchMode == SearchMode.EndOfLine && runtimeInfo.CurrentLine.EndsWith(name))
{
string copyLine = statementAttribute.KeepStatementInArgs ? runtimeInfo.CurrentLine : runtimeInfo.CurrentLine.Remove(runtimeInfo.CurrentLine.Length - name.Length);
statement.Value.Invoke(copyLine);
statementFound = true;
}
else if (statementAttribute.SearchMode == SearchMode.Exact && runtimeInfo.CurrentLine.Equals(name))
{
statement.Value.Invoke(runtimeInfo.CurrentLine);
statementFound = true;
}
}
if (!statementFound)
{
runtimeInfo.Exit("Invalid statement", true);
}
if (runtimeInfo.IsDebugMode && notSearchingLabel)
{
debugEventArgs.CurrentLine = runtimeInfo.CurrentLine.FromSaveString();
runtimeInfo.LineExecuted(debugEventArgs);
}
}
if (runtimeInfo.Stop == false)
{
if (!string.IsNullOrWhiteSpace(runtimeInfo.SearchLabel))
{
runtimeInfo.Exit($"Label \"{runtimeInfo.SearchLabel}\" not found", true);
}
else if (!string.IsNullOrWhiteSpace(runtimeInfo.SearchFunction))
{
runtimeInfo.Exit($"Function \"{runtimeInfo.SearchLabel}\" not found", true);
}
else
{
runtimeInfo.Exit("End of file", false);
}
if (runtimeInfo.IsDebugMode)
{
runtimeInfo.LineExecuted(null);
}
}
}
private void LoadFile(string path)
{
if (!File.Exists(path))
{
runtimeInfo.Exit($"File \"{path}\" not found!", true);
return;
}
IEnumerable<string> lines = File.ReadAllLines(path);
runtimeInfo.Lines.AddRange(lines);
}
}
}