mirror of
https://github.com/Stone-Red-Code/YesNt-Interpreter.git
synced 2026-09-04 00:56:31 +02:00
Add XML docs
This commit is contained in:
@@ -4,19 +4,69 @@ using YesNt.Interpreter.Enums;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Attributes;
|
namespace YesNt.Interpreter.Attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks a method as a YesNt statement handler.
|
||||||
|
/// The interpreter matches source lines against the <see cref="Name"/> keyword according to
|
||||||
|
/// <see cref="SearchMode"/> and <see cref="SpaceAround"/> rules, then invokes the decorated method
|
||||||
|
/// with the remaining argument text.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Methods decorated with this attribute must be instance methods on a class that inherits
|
||||||
|
/// <see cref="Runtime.StatementRuntimeInformation"/> and must accept a single <see cref="string"/> parameter.
|
||||||
|
/// </remarks>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
public class StatementAttribute : Attribute
|
public class StatementAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets the keyword that identifies this statement in source code.</summary>
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets where in the line the keyword is searched for.</summary>
|
||||||
public SearchMode SearchMode { get; }
|
public SearchMode SearchMode { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets which sides of the keyword must be padded with a space.</summary>
|
||||||
public SpaceAround SpaceAround { get; }
|
public SpaceAround SpaceAround { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the syntax-highlight colour used by the code editor.</summary>
|
||||||
public ConsoleColor Color { get; set; }
|
public ConsoleColor Color { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the execution priority. Statements with a lower <see cref="Priority"/> value
|
||||||
|
/// run before those with a higher value. Defaults to <see cref="Priority.Normal"/>.
|
||||||
|
/// </summary>
|
||||||
public Priority Priority { get; set; } = Priority.Normal;
|
public Priority Priority { get; set; } = Priority.Normal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this statement is still invoked while the interpreter
|
||||||
|
/// is in search mode (scanning for a label or function definition). Defaults to <see langword="false"/>.
|
||||||
|
/// </summary>
|
||||||
public bool ExecuteInSearchMode { get; set; }
|
public bool ExecuteInSearchMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the full current line (including the keyword itself)
|
||||||
|
/// is passed as the argument, rather than stripping the keyword prefix/suffix first.
|
||||||
|
/// Defaults to <see langword="false"/>.
|
||||||
|
/// </summary>
|
||||||
public bool KeepStatementInArgs { get; set; }
|
public bool KeepStatementInArgs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this statement should be excluded from syntax highlighting.
|
||||||
|
/// Set to <see langword="true"/> when no <see cref="Color"/> is provided.
|
||||||
|
/// </summary>
|
||||||
public bool IgnoreSyntaxHighlighting { get; }
|
public bool IgnoreSyntaxHighlighting { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an optional sub-string that must also be present in the line for this statement
|
||||||
|
/// to match. Used to differentiate overloaded keywords (e.g. <c>call</c> vs <c>call … with …</c>).
|
||||||
|
/// </summary>
|
||||||
public string Separator { get; set; }
|
public string Separator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialises a new <see cref="StatementAttribute"/> with a syntax-highlight colour.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The keyword that identifies this statement.</param>
|
||||||
|
/// <param name="searchMode">Where in the line the keyword is matched.</param>
|
||||||
|
/// <param name="spaceAround">Which sides of the keyword require a surrounding space.</param>
|
||||||
|
/// <param name="color">The colour used for syntax highlighting in the code editor.</param>
|
||||||
public StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor color)
|
public StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor color)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -25,6 +75,13 @@ public class StatementAttribute : Attribute
|
|||||||
Color = color;
|
Color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialises a new <see cref="StatementAttribute"/> without a syntax-highlight colour.
|
||||||
|
/// The statement will be excluded from syntax highlighting.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The keyword that identifies this statement.</param>
|
||||||
|
/// <param name="searchMode">Where in the line the keyword is matched.</param>
|
||||||
|
/// <param name="spaceAround">Which sides of the keyword require a surrounding space.</param>
|
||||||
public StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround)
|
public StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|||||||
@@ -4,9 +4,28 @@ using YesNt.Interpreter.Enums;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Attributes;
|
namespace YesNt.Interpreter.Attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks a parameterless method as a YesNt static statement handler.
|
||||||
|
/// Static statements are invoked once per line before regular statement matching begins,
|
||||||
|
/// regardless of whether the line matches any keyword. They are typically used for
|
||||||
|
/// pre-processing tasks such as transforming the current line before other statements run.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Methods decorated with this attribute must be instance methods on a class that inherits
|
||||||
|
/// <see cref="Runtime.StatementRuntimeInformation"/> and must have no parameters.
|
||||||
|
/// </remarks>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
public class StaticStatementAttribute : Attribute
|
public class StaticStatementAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this handler is still invoked while the interpreter
|
||||||
|
/// is in search mode (scanning for a label or function definition). Defaults to <see langword="false"/>.
|
||||||
|
/// </summary>
|
||||||
public bool ExecuteInSearchMode { get; set; }
|
public bool ExecuteInSearchMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the execution priority relative to other static statements.
|
||||||
|
/// Defaults to <see cref="Priority.Normal"/>.
|
||||||
|
/// </summary>
|
||||||
public Priority Priority { get; set; } = Priority.Normal;
|
public Priority Priority { get; set; } = Priority.Normal;
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,28 @@
|
|||||||
namespace YesNt.Interpreter.Enums;
|
namespace YesNt.Interpreter.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls the execution order of statements. Lower values run first.
|
||||||
|
/// </summary>
|
||||||
public enum Priority
|
public enum Priority
|
||||||
{
|
{
|
||||||
|
/// <summary>Runs before all other statements. Used for syntax pre-processing such as string literals.</summary>
|
||||||
PreProcessing,
|
PreProcessing,
|
||||||
|
|
||||||
|
/// <summary>Runs very early. Used for inline substitutions such as variable reads and parameter pops.</summary>
|
||||||
Highest,
|
Highest,
|
||||||
|
|
||||||
|
/// <summary>Runs early.</summary>
|
||||||
VeryHigh,
|
VeryHigh,
|
||||||
|
|
||||||
|
/// <summary>Runs above normal order.</summary>
|
||||||
High,
|
High,
|
||||||
|
|
||||||
|
/// <summary>Default execution order.</summary>
|
||||||
Normal,
|
Normal,
|
||||||
|
|
||||||
|
/// <summary>Runs below normal order.</summary>
|
||||||
Low,
|
Low,
|
||||||
|
|
||||||
|
/// <summary>Runs last. Used for control-flow and variable definitions that depend on substitutions being complete.</summary>
|
||||||
VeryLow
|
VeryLow
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
namespace YesNt.Interpreter.Enums;
|
namespace YesNt.Interpreter.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines where in a source line the interpreter searches for a statement keyword.
|
||||||
|
/// </summary>
|
||||||
public enum SearchMode
|
public enum SearchMode
|
||||||
{
|
{
|
||||||
|
/// <summary>The keyword must appear at the beginning of the line.</summary>
|
||||||
StartOfLine,
|
StartOfLine,
|
||||||
|
|
||||||
|
/// <summary>The keyword must appear at the end of the line.</summary>
|
||||||
EndOfLine,
|
EndOfLine,
|
||||||
|
|
||||||
|
/// <summary>The keyword may appear anywhere in the line.</summary>
|
||||||
Contains,
|
Contains,
|
||||||
|
|
||||||
|
/// <summary>The entire line must exactly match the keyword.</summary>
|
||||||
Exact
|
Exact
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
namespace YesNt.Interpreter.Enums;
|
namespace YesNt.Interpreter.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies which sides of a statement keyword must be surrounded by a space when matching.
|
||||||
|
/// </summary>
|
||||||
public enum SpaceAround
|
public enum SpaceAround
|
||||||
{
|
{
|
||||||
|
/// <summary>A space is required both before and after the keyword.</summary>
|
||||||
StartEnd,
|
StartEnd,
|
||||||
|
|
||||||
|
/// <summary>A space is required before the keyword only.</summary>
|
||||||
Start,
|
Start,
|
||||||
|
|
||||||
|
/// <summary>A space is required after the keyword only.</summary>
|
||||||
End,
|
End,
|
||||||
|
|
||||||
|
/// <summary>No surrounding spaces are required.</summary>
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,32 @@
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides per-line execution data raised through <see cref="YesNtInterpreter.OnLineExecuted"/>.
|
||||||
|
/// </summary>
|
||||||
public class DebugEventArgs : EventArgs
|
public class DebugEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets the 1-based line number of the executed line within its source file.</summary>
|
||||||
public int LineNumber { get; internal set; }
|
public int LineNumber { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the line content after all statement transformations have been applied
|
||||||
|
/// (e.g. after variable substitution). May differ from <see cref="OriginalLine"/>.
|
||||||
|
/// </summary>
|
||||||
public string CurrentLine { get; internal set; }
|
public string CurrentLine { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the raw line content as it appeared in the source file.</summary>
|
||||||
public string OriginalLine { get; internal set; }
|
public string OriginalLine { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the task identifier of the task that executed this line, or <c>0</c> if the line
|
||||||
|
/// was executed on the main thread.
|
||||||
|
/// </summary>
|
||||||
public int TaskId { get; internal set; }
|
public int TaskId { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this line was executed inside a background task
|
||||||
|
/// (spawned with the <c>task</c> statement).
|
||||||
|
/// </summary>
|
||||||
public bool IsTask { get; internal set; }
|
public bool IsTask { get; internal set; }
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Central repository of all exit/error message strings used by <see cref="RuntimeInformation.Exit"/>.
|
||||||
|
/// Keeping messages here ensures consistency and makes them easy to find or localise.
|
||||||
|
/// </summary>
|
||||||
internal static class ExitMessages
|
internal static class ExitMessages
|
||||||
{
|
{
|
||||||
internal const string InvalidSyntax = "Invalid syntax";
|
internal const string InvalidSyntax = "Invalid syntax";
|
||||||
|
|||||||
@@ -2,12 +2,27 @@
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents one frame on the function call stack. Created when a <c>call</c> statement is
|
||||||
|
/// executed and popped when the matching <c>return</c> is reached.
|
||||||
|
/// </summary>
|
||||||
internal class FunctionScope(int callerLine, Stack<string> arguments)
|
internal class FunctionScope(int callerLine, Stack<string> arguments)
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets the zero-based line index to return to after this function completes.</summary>
|
||||||
public int CallerLine { get; } = callerLine;
|
public int CallerLine { get; } = callerLine;
|
||||||
|
|
||||||
|
/// <summary>Gets the local variable table for this function invocation.</summary>
|
||||||
public Dictionary<string, string> Variables { get; } = [];
|
public Dictionary<string, string> Variables { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>Gets the local list table for this function invocation.</summary>
|
||||||
public Dictionary<string, List<string>> Lists { get; } = [];
|
public Dictionary<string, List<string>> Lists { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>Gets the local label table for this function invocation.</summary>
|
||||||
public Dictionary<string, int> Labels { get; } = [];
|
public Dictionary<string, int> Labels { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>Gets the stack of input arguments passed to this function via <c>push_in</c>.</summary>
|
||||||
public Stack<string> Arguments { get; } = arguments;
|
public Stack<string> Arguments { get; } = arguments;
|
||||||
|
|
||||||
|
/// <summary>Gets the stack of output values pushed via <c>push_out</c>, consumed by the caller via <c>%out</c>.</summary>
|
||||||
public Stack<string> Results { get; } = new();
|
public Stack<string> Results { get; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single source line together with its location metadata.
|
||||||
|
/// </summary>
|
||||||
internal class Line(string content, string fileName, int lineNumber)
|
internal class Line(string content, string fileName, int lineNumber)
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets or sets the raw text content of the line.</summary>
|
||||||
public string Content { get; set; } = content;
|
public string Content { get; set; } = content;
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the name of the source file this line originated from.</summary>
|
||||||
public string FileName { get; set; } = fileName;
|
public string FileName { get; set; } = fileName;
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the zero-based line index within <see cref="FileName"/>.</summary>
|
||||||
public int LineNumber { get; set; } = lineNumber;
|
public int LineNumber { get; set; } = lineNumber;
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,13 @@ using YesNt.Interpreter.Utilities;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds all mutable runtime state for a single script execution, including variables, lists,
|
||||||
|
/// labels, functions, the call stack, the line counter, and stop flags.
|
||||||
|
/// Each background task spawned by the <c>task</c> statement owns its own
|
||||||
|
/// <see cref="RuntimeInformation"/> whose <see cref="ParentRuntimeInformation"/> points back
|
||||||
|
/// to the main execution context.
|
||||||
|
/// </summary>
|
||||||
internal sealed class RuntimeInformation
|
internal sealed class RuntimeInformation
|
||||||
{
|
{
|
||||||
public event Action<string> OnDebugOutput;
|
public event Action<string> OnDebugOutput;
|
||||||
|
|||||||
@@ -4,12 +4,32 @@ using YesNt.Interpreter.Enums;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only snapshot of a registered statement's metadata, used for tooling such as
|
||||||
|
/// syntax highlighters. Instances are obtained from <see cref="YesNtInterpreter.StatementInformation"/>.
|
||||||
|
/// </summary>
|
||||||
public class StatementInformation
|
public class StatementInformation
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets the keyword that identifies this statement in source code.</summary>
|
||||||
public string Name { get; internal set; }
|
public string Name { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets where in the line the keyword is searched for.</summary>
|
||||||
public SearchMode SearchMode { get; internal set; }
|
public SearchMode SearchMode { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets which sides of the keyword must be padded with a space.</summary>
|
||||||
public SpaceAround SpaceAround { get; internal set; }
|
public SpaceAround SpaceAround { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the syntax-highlight colour for this statement.</summary>
|
||||||
public ConsoleColor Color { get; internal set; }
|
public ConsoleColor Color { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this statement is excluded from syntax highlighting.
|
||||||
|
/// </summary>
|
||||||
public bool IgnoreSyntaxHighlighting { get; internal set; }
|
public bool IgnoreSyntaxHighlighting { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the optional sub-string that must be present in the line for this statement to match,
|
||||||
|
/// or <see langword="null"/> if no separator is required.
|
||||||
|
/// </summary>
|
||||||
public string Separator { get; set; }
|
public string Separator { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for all classes that host statement handler methods.
|
||||||
|
/// Subclasses declare methods decorated with <see cref="Attributes.StatementAttribute"/> or
|
||||||
|
/// <see cref="Attributes.StaticStatementAttribute"/>; the source generator
|
||||||
|
/// (<c>GeneratedStatementRegistry</c>) discovers these at compile time and wires them up.
|
||||||
|
/// </summary>
|
||||||
internal abstract class StatementRuntimeInformation
|
internal abstract class StatementRuntimeInformation
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the runtime state for the current execution context.
|
||||||
|
/// Injected by the generated registry before any handler is invoked.
|
||||||
|
/// </summary>
|
||||||
public RuntimeInformation RuntimeInfo { get; set; }
|
public RuntimeInformation RuntimeInfo { get; set; }
|
||||||
}
|
}
|
||||||
@@ -10,16 +10,45 @@ using YesNt.Interpreter.Utilities;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Runtime;
|
namespace YesNt.Interpreter.Runtime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main entry point for executing YesNt scripts.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// Running a script file:
|
||||||
|
/// <code>
|
||||||
|
/// var interpreter = new YesNtInterpreter();
|
||||||
|
/// interpreter.Execute("path/to/script.ynt");
|
||||||
|
/// </code>
|
||||||
|
/// Running script lines in memory with a custom statement:
|
||||||
|
/// <code>
|
||||||
|
/// var interpreter = new YesNtInterpreter();
|
||||||
|
/// interpreter.AddStatement("log", SearchMode.StartOfLine, SpaceAround.End, args =>
|
||||||
|
/// Console.WriteLine($"[LOG] {args}"));
|
||||||
|
/// interpreter.Execute(new List<string> { "log hello world" });
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
public class YesNtInterpreter
|
public class YesNtInterpreter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Raised after each line is executed in debug mode. The argument is <see langword="null"/>
|
||||||
|
/// when execution ends (either normally or due to an error), allowing callers to detect completion.
|
||||||
|
/// </summary>
|
||||||
public event Action<DebugEventArgs> OnLineExecuted;
|
public event Action<DebugEventArgs> OnLineExecuted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised in debug mode whenever the script produces output (e.g. via <c>print_line</c>).
|
||||||
|
/// In non-debug mode output is written directly to <see cref="Console"/>.
|
||||||
|
/// </summary>
|
||||||
public event Action<string> OnDebugOutput;
|
public event Action<string> OnDebugOutput;
|
||||||
|
|
||||||
private readonly RuntimeInformation runtimeInfo = new RuntimeInformation();
|
private readonly RuntimeInformation runtimeInfo = new RuntimeInformation();
|
||||||
private Dictionary<StatementAttribute, Action<string>> statements;
|
private Dictionary<StatementAttribute, Action<string>> statements;
|
||||||
private readonly List<KeyValuePair<StaticStatementAttribute, Action>> staticStatements;
|
private readonly List<KeyValuePair<StaticStatementAttribute, Action>> staticStatements;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only snapshot of all currently registered statements.
|
||||||
|
/// Useful for building syntax highlighters or documentation tools.
|
||||||
|
/// </summary>
|
||||||
public ReadOnlyCollection<StatementInformation> StatementInformation
|
public ReadOnlyCollection<StatementInformation> StatementInformation
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -41,6 +70,16 @@ public class YesNtInterpreter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a custom statement using a pre-built <see cref="StatementAttribute"/>.
|
||||||
|
/// If a statement with the same attribute key already exists it will be replaced.
|
||||||
|
/// The statement list is re-sorted by priority after insertion.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attribute">The attribute describing the keyword, search mode, and priority.</param>
|
||||||
|
/// <param name="handler">
|
||||||
|
/// The delegate invoked when the statement matches. Receives the argument text
|
||||||
|
/// (the part of the line after the keyword, unless <see cref="StatementAttribute.KeepStatementInArgs"/> is set).
|
||||||
|
/// </param>
|
||||||
public void AddStatement(StatementAttribute attribute, Action<string> handler)
|
public void AddStatement(StatementAttribute attribute, Action<string> handler)
|
||||||
{
|
{
|
||||||
statements[attribute] = handler;
|
statements[attribute] = handler;
|
||||||
@@ -50,16 +89,34 @@ public class YesNtInterpreter
|
|||||||
.ToDictionary(x => x.Key, x => x.Value);
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a custom statement without a syntax-highlight colour.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The keyword that identifies this statement in source code.</param>
|
||||||
|
/// <param name="searchMode">Where in the line the keyword is matched.</param>
|
||||||
|
/// <param name="spaceAround">Which sides of the keyword require a surrounding space.</param>
|
||||||
|
/// <param name="handler">The delegate invoked when the statement matches.</param>
|
||||||
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string> handler)
|
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string> handler)
|
||||||
{
|
{
|
||||||
AddStatement(new StatementAttribute(name, searchMode, spaceAround), handler);
|
AddStatement(new StatementAttribute(name, searchMode, spaceAround), handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a custom statement with a syntax-highlight colour.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The keyword that identifies this statement in source code.</param>
|
||||||
|
/// <param name="searchMode">Where in the line the keyword is matched.</param>
|
||||||
|
/// <param name="spaceAround">Which sides of the keyword require a surrounding space.</param>
|
||||||
|
/// <param name="consoleColor">The colour used for syntax highlighting in the code editor.</param>
|
||||||
|
/// <param name="handler">The delegate invoked when the statement matches.</param>
|
||||||
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action<string> handler)
|
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action<string> handler)
|
||||||
{
|
{
|
||||||
AddStatement(new StatementAttribute(name, searchMode, spaceAround, consoleColor), handler);
|
AddStatement(new StatementAttribute(name, searchMode, spaceAround, consoleColor), handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialises a new <see cref="YesNtInterpreter"/> and registers all built-in statements.
|
||||||
|
/// </summary>
|
||||||
public YesNtInterpreter()
|
public YesNtInterpreter()
|
||||||
{
|
{
|
||||||
GeneratedStatementRegistry.Register(runtimeInfo, out statements, out staticStatements);
|
GeneratedStatementRegistry.Register(runtimeInfo, out statements, out staticStatements);
|
||||||
@@ -68,11 +125,23 @@ public class YesNtInterpreter
|
|||||||
runtimeInfo.OnLineExecuted += e => OnLineExecuted?.Invoke(e);
|
runtimeInfo.OnLineExecuted += e => OnLineExecuted?.Invoke(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests a graceful stop of the currently executing script.
|
||||||
|
/// The interpreter will terminate at the next line boundary.
|
||||||
|
/// </summary>
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
runtimeInfo.Exit(ExitMessages.TerminatedByExternalProcess, true);
|
runtimeInfo.Exit(ExitMessages.TerminatedByExternalProcess, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a YesNt script file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the <c>.ynt</c> script file.</param>
|
||||||
|
/// <param name="isDebugMode">
|
||||||
|
/// When <see langword="true"/>, output is routed through <see cref="OnDebugOutput"/> instead of
|
||||||
|
/// <see cref="Console"/> and line-execution events are raised via <see cref="OnLineExecuted"/>.
|
||||||
|
/// </param>
|
||||||
public void Execute(string path, bool isDebugMode = false)
|
public void Execute(string path, bool isDebugMode = false)
|
||||||
{
|
{
|
||||||
runtimeInfo.Reset();
|
runtimeInfo.Reset();
|
||||||
@@ -83,6 +152,14 @@ public class YesNtInterpreter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a YesNt script supplied as an in-memory list of lines.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lines">The script lines to execute.</param>
|
||||||
|
/// <param name="isDebugMode">
|
||||||
|
/// When <see langword="true"/>, output is routed through <see cref="OnDebugOutput"/> and
|
||||||
|
/// line-execution events are raised via <see cref="OnLineExecuted"/>.
|
||||||
|
/// </param>
|
||||||
public void Execute(List<string> lines, bool isDebugMode = false)
|
public void Execute(List<string> lines, bool isDebugMode = false)
|
||||||
{
|
{
|
||||||
runtimeInfo.Reset();
|
runtimeInfo.Reset();
|
||||||
|
|||||||
@@ -4,8 +4,19 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Utilities;
|
namespace YesNt.Interpreter.Utilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides expression evaluation used by conditional and arithmetic statements.
|
||||||
|
/// </summary>
|
||||||
internal static partial class Evaluator
|
internal static partial class Evaluator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates a boolean condition string such as <c>a == b</c>, <c>x > 3</c>, or <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The condition expression, which may contain safe-string encoded values.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true"/> or <see langword="false"/> if the condition could be evaluated;
|
||||||
|
/// <see langword="null"/> if the expression is not a recognised condition form (treated as an error by callers).
|
||||||
|
/// </returns>
|
||||||
public static bool? EvaluateCondition(string input)
|
public static bool? EvaluateCondition(string input)
|
||||||
{
|
{
|
||||||
if (input.ToLower().FromSafeString().Trim() == "true")
|
if (input.ToLower().FromSafeString().Trim() == "true")
|
||||||
@@ -68,6 +79,13 @@ internal static partial class Evaluator
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates a numeric arithmetic expression string and returns the result as a string.
|
||||||
|
/// Supports <c>+</c>, <c>-</c>, <c>*</c>, and <c>/</c> operators.
|
||||||
|
/// Adjacent sign characters (<c>++</c>, <c>--</c>, <c>-+</c>, <c>+-</c>) are normalised before evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The arithmetic expression to evaluate.</param>
|
||||||
|
/// <returns>The result as a culture-invariant numeric string, or <c>"NaN"</c> if evaluation failed.</returns>
|
||||||
public static string Calculate(string input)
|
public static string Calculate(string input)
|
||||||
{
|
{
|
||||||
input = PlusPlusRegex().Replace(input, "+");
|
input = PlusPlusRegex().Replace(input, "+");
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ public delegate void DataReceivedEventHandler(object sender, DataReceivedEventAr
|
|||||||
|
|
||||||
internal delegate void UserCallBack(string data);
|
internal delegate void UserCallBack(string data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A workaround replacement for <see cref="System.Diagnostics.Process"/> that fixes a buffering
|
||||||
|
/// issue in <see cref="System.Diagnostics.Process.BeginOutputReadLine"/> / <see cref="System.Diagnostics.Process.BeginErrorReadLine"/>:
|
||||||
|
/// the BCL implementation only delivers data when a newline is encountered, which means partial
|
||||||
|
/// lines are not raised until the process writes another newline or exits.
|
||||||
|
/// <see cref="FixedProcess"/> flushes whatever is in the read buffer immediately, enabling real-time
|
||||||
|
/// output forwarding for interactive child processes.
|
||||||
|
/// </summary>
|
||||||
public class FixedProcess : Process
|
public class FixedProcess : Process
|
||||||
{
|
{
|
||||||
public new event DataReceivedEventHandler OutputDataReceived;
|
public new event DataReceivedEventHandler OutputDataReceived;
|
||||||
|
|||||||
@@ -6,10 +6,27 @@ using System.Text;
|
|||||||
|
|
||||||
namespace YesNt.Interpreter.Utilities;
|
namespace YesNt.Interpreter.Utilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for string manipulation used throughout the interpreter.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// YesNt uses a "safe string" encoding to pass values through the interpreter pipeline without
|
||||||
|
/// accidentally triggering keyword matching. Special characters (spaces, operators, punctuation,
|
||||||
|
/// control characters) are replaced with tilde-prefixed three-letter codes
|
||||||
|
/// (e.g. space → <c>~spc</c>, newline → <c>~nli</c>). The mapping is defined in
|
||||||
|
/// <see cref="ReplacementRules"/>. Use <see cref="ToSafeString"/> to encode and
|
||||||
|
/// <see cref="FromSafeString"/> to decode.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
public static class StringExtensions
|
public static class StringExtensions
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, string> reverseReplacementRules;
|
private static readonly Dictionary<string, string> reverseReplacementRules;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the table that maps special characters to their safe-string escape codes.
|
||||||
|
/// Keys are the original characters; values are the three-letter tilde codes.
|
||||||
|
/// </summary>
|
||||||
public static Dictionary<string, string> ReplacementRules { get; } = new()
|
public static Dictionary<string, string> ReplacementRules { get; } = new()
|
||||||
{
|
{
|
||||||
{"~", "~til" },
|
{"~", "~til" },
|
||||||
@@ -34,6 +51,13 @@ public static class StringExtensions
|
|||||||
reverseReplacementRules = ReplacementRules.ToDictionary(x => x.Value, x => x.Key);
|
reverseReplacementRules = ReplacementRules.ToDictionary(x => x.Value, x => x.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a string into safe-string format so that special characters cannot accidentally
|
||||||
|
/// trigger interpreter keyword matching. Each character is wrapped with vertical-tab sentinels
|
||||||
|
/// before rule substitution so that multi-character replacements do not overlap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The plain string to encode.</param>
|
||||||
|
/// <returns>The safe-string encoded representation.</returns>
|
||||||
public static string ToSafeString(this string input)
|
public static string ToSafeString(this string input)
|
||||||
{
|
{
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
@@ -45,28 +69,53 @@ public static class StringExtensions
|
|||||||
return ReplaceOnce(output.ToString(), ReplacementRules);
|
return ReplaceOnce(output.ToString(), ReplacementRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a safe-string back to its original plain-text form.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">A safe-string encoded string.</param>
|
||||||
|
/// <returns>The decoded plain string.</returns>
|
||||||
public static string FromSafeString(this string input)
|
public static string FromSafeString(this string input)
|
||||||
{
|
{
|
||||||
return ReplaceOnce(input.Replace("\v", string.Empty), reverseReplacementRules);
|
return ReplaceOnce(input.Replace("\v", string.Empty), reverseReplacementRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to parse the string as a <see cref="double"/>, first decoding safe-string encoding
|
||||||
|
/// and normalising decimal separators (comma → period).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The string to parse (may be safe-string encoded).</param>
|
||||||
|
/// <param name="result">When this method returns, contains the parsed value if successful.</param>
|
||||||
|
/// <returns><see langword="true"/> if parsing succeeded; otherwise <see langword="false"/>.</returns>
|
||||||
public static bool ToStandardizedNumber(this string input, out double result)
|
public static bool ToStandardizedNumber(this string input, out double result)
|
||||||
{
|
{
|
||||||
return double.TryParse(input.FromSafeString().Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
return double.TryParse(input.FromSafeString().Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Replaces only the first occurrence of <paramref name="oldValue"/> in the string.</summary>
|
||||||
|
/// <param name="input">The source string.</param>
|
||||||
|
/// <param name="oldValue">The substring to find.</param>
|
||||||
|
/// <param name="newValue">The replacement value.</param>
|
||||||
|
/// <returns>A new string with the first occurrence replaced.</returns>
|
||||||
public static string ReplaceFirstOccurrence(this string input, string oldValue, string newValue)
|
public static string ReplaceFirstOccurrence(this string input, string oldValue, string newValue)
|
||||||
{
|
{
|
||||||
int place = input.IndexOf(oldValue);
|
int place = input.IndexOf(oldValue);
|
||||||
return input.Remove(place, oldValue.Length).Insert(place, newValue);
|
return input.Remove(place, oldValue.Length).Insert(place, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Replaces only the last occurrence of <paramref name="oldValue"/> in the string.</summary>
|
||||||
|
/// <param name="input">The source string.</param>
|
||||||
|
/// <param name="oldValue">The substring to find.</param>
|
||||||
|
/// <param name="newValue">The replacement value.</param>
|
||||||
|
/// <returns>A new string with the last occurrence replaced.</returns>
|
||||||
public static string ReplaceLastOccurrence(this string input, string oldValue, string newValue)
|
public static string ReplaceLastOccurrence(this string input, string oldValue, string newValue)
|
||||||
{
|
{
|
||||||
int place = input.LastIndexOf(oldValue);
|
int place = input.LastIndexOf(oldValue);
|
||||||
return input.Remove(place, Math.Min(oldValue.Length, input.Length - place)).Insert(place, newValue);
|
return input.Remove(place, Math.Min(oldValue.Length, input.Length - place)).Insert(place, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Counts the number of trailing whitespace characters in the string.</summary>
|
||||||
|
/// <param name="input">The source string.</param>
|
||||||
|
/// <returns>The number of whitespace characters at the end of the string.</returns>
|
||||||
public static int WhiteSpaceAtEnd(this string input)
|
public static int WhiteSpaceAtEnd(this string input)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user