From a2f621d3f4fe2c05d50a1386b9db31a35ed4622b Mon Sep 17 00:00:00 2001
From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com>
Date: Wed, 4 Mar 2026 20:44:06 +0100
Subject: [PATCH] Add XML docs
---
.../Attributes/StatementAttribute.cs | 57 ++++++++++++++
.../Attributes/StaticStatementAttribute.cs | 19 +++++
YesNt.Interpreter/Enums/Priority.cs | 16 ++++
YesNt.Interpreter/Enums/SearchMode.cs | 10 +++
YesNt.Interpreter/Enums/SpaceAround.cs | 10 +++
YesNt.Interpreter/Runtime/DebugEventArgs.cs | 21 +++++
YesNt.Interpreter/Runtime/ExitMessages.cs | 4 +
YesNt.Interpreter/Runtime/FunctionScope.cs | 15 ++++
YesNt.Interpreter/Runtime/Line.cs | 6 ++
.../Runtime/RuntimeInformation.cs | 7 ++
.../Runtime/StatementInformation.cs | 20 +++++
.../Runtime/StatementRuntimeInfo.cs | 10 +++
YesNt.Interpreter/Runtime/YesNtInterpreter.cs | 77 +++++++++++++++++++
YesNt.Interpreter/Utilities/Evaluator.cs | 18 +++++
YesNt.Interpreter/Utilities/FixedProcess.cs | 8 ++
.../Utilities/StringExtentions.cs | 49 ++++++++++++
16 files changed, 347 insertions(+)
diff --git a/YesNt.Interpreter/Attributes/StatementAttribute.cs b/YesNt.Interpreter/Attributes/StatementAttribute.cs
index b2aabf4..6f8f543 100644
--- a/YesNt.Interpreter/Attributes/StatementAttribute.cs
+++ b/YesNt.Interpreter/Attributes/StatementAttribute.cs
@@ -4,19 +4,69 @@ using YesNt.Interpreter.Enums;
namespace YesNt.Interpreter.Attributes;
+///
+/// Marks a method as a YesNt statement handler.
+/// The interpreter matches source lines against the keyword according to
+/// and rules, then invokes the decorated method
+/// with the remaining argument text.
+///
+///
+/// Methods decorated with this attribute must be instance methods on a class that inherits
+/// and must accept a single parameter.
+///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class StatementAttribute : Attribute
{
+ /// Gets the keyword that identifies this statement in source code.
public string Name { get; }
+
+ /// Gets where in the line the keyword is searched for.
public SearchMode SearchMode { get; }
+
+ /// Gets which sides of the keyword must be padded with a space.
public SpaceAround SpaceAround { get; }
+
+ /// Gets or sets the syntax-highlight colour used by the code editor.
public ConsoleColor Color { get; set; }
+
+ ///
+ /// Gets or sets the execution priority. Statements with a lower value
+ /// run before those with a higher value. Defaults to .
+ ///
public Priority Priority { get; set; } = Priority.Normal;
+
+ ///
+ /// 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 .
+ ///
public bool ExecuteInSearchMode { get; set; }
+
+ ///
+ /// 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 .
+ ///
public bool KeepStatementInArgs { get; set; }
+
+ ///
+ /// Gets a value indicating whether this statement should be excluded from syntax highlighting.
+ /// Set to when no is provided.
+ ///
public bool IgnoreSyntaxHighlighting { get; }
+
+ ///
+ /// 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. call vs call … with …).
+ ///
public string Separator { get; set; }
+ ///
+ /// Initialises a new with a syntax-highlight colour.
+ ///
+ /// The keyword that identifies this statement.
+ /// Where in the line the keyword is matched.
+ /// Which sides of the keyword require a surrounding space.
+ /// The colour used for syntax highlighting in the code editor.
public StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor color)
{
Name = name;
@@ -25,6 +75,13 @@ public class StatementAttribute : Attribute
Color = color;
}
+ ///
+ /// Initialises a new without a syntax-highlight colour.
+ /// The statement will be excluded from syntax highlighting.
+ ///
+ /// The keyword that identifies this statement.
+ /// Where in the line the keyword is matched.
+ /// Which sides of the keyword require a surrounding space.
public StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround)
{
Name = name;
diff --git a/YesNt.Interpreter/Attributes/StaticStatementAttribute.cs b/YesNt.Interpreter/Attributes/StaticStatementAttribute.cs
index 59f80e3..4ed6682 100644
--- a/YesNt.Interpreter/Attributes/StaticStatementAttribute.cs
+++ b/YesNt.Interpreter/Attributes/StaticStatementAttribute.cs
@@ -4,9 +4,28 @@ using YesNt.Interpreter.Enums;
namespace YesNt.Interpreter.Attributes;
+///
+/// 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.
+///
+///
+/// Methods decorated with this attribute must be instance methods on a class that inherits
+/// and must have no parameters.
+///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class StaticStatementAttribute : Attribute
{
+ ///
+ /// 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 .
+ ///
public bool ExecuteInSearchMode { get; set; }
+
+ ///
+ /// Gets or sets the execution priority relative to other static statements.
+ /// Defaults to .
+ ///
public Priority Priority { get; set; } = Priority.Normal;
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Enums/Priority.cs b/YesNt.Interpreter/Enums/Priority.cs
index eb8353d..8fbaf51 100644
--- a/YesNt.Interpreter/Enums/Priority.cs
+++ b/YesNt.Interpreter/Enums/Priority.cs
@@ -1,12 +1,28 @@
namespace YesNt.Interpreter.Enums;
+///
+/// Controls the execution order of statements. Lower values run first.
+///
public enum Priority
{
+ /// Runs before all other statements. Used for syntax pre-processing such as string literals.
PreProcessing,
+
+ /// Runs very early. Used for inline substitutions such as variable reads and parameter pops.
Highest,
+
+ /// Runs early.
VeryHigh,
+
+ /// Runs above normal order.
High,
+
+ /// Default execution order.
Normal,
+
+ /// Runs below normal order.
Low,
+
+ /// Runs last. Used for control-flow and variable definitions that depend on substitutions being complete.
VeryLow
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Enums/SearchMode.cs b/YesNt.Interpreter/Enums/SearchMode.cs
index 0226142..a13e749 100644
--- a/YesNt.Interpreter/Enums/SearchMode.cs
+++ b/YesNt.Interpreter/Enums/SearchMode.cs
@@ -1,9 +1,19 @@
namespace YesNt.Interpreter.Enums;
+///
+/// Determines where in a source line the interpreter searches for a statement keyword.
+///
public enum SearchMode
{
+ /// The keyword must appear at the beginning of the line.
StartOfLine,
+
+ /// The keyword must appear at the end of the line.
EndOfLine,
+
+ /// The keyword may appear anywhere in the line.
Contains,
+
+ /// The entire line must exactly match the keyword.
Exact
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Enums/SpaceAround.cs b/YesNt.Interpreter/Enums/SpaceAround.cs
index 0ee0990..c9e4292 100644
--- a/YesNt.Interpreter/Enums/SpaceAround.cs
+++ b/YesNt.Interpreter/Enums/SpaceAround.cs
@@ -1,9 +1,19 @@
namespace YesNt.Interpreter.Enums;
+///
+/// Specifies which sides of a statement keyword must be surrounded by a space when matching.
+///
public enum SpaceAround
{
+ /// A space is required both before and after the keyword.
StartEnd,
+
+ /// A space is required before the keyword only.
Start,
+
+ /// A space is required after the keyword only.
End,
+
+ /// No surrounding spaces are required.
None
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Runtime/DebugEventArgs.cs b/YesNt.Interpreter/Runtime/DebugEventArgs.cs
index 0f2d2fe..0a0a842 100644
--- a/YesNt.Interpreter/Runtime/DebugEventArgs.cs
+++ b/YesNt.Interpreter/Runtime/DebugEventArgs.cs
@@ -2,11 +2,32 @@
namespace YesNt.Interpreter.Runtime;
+///
+/// Provides per-line execution data raised through .
+///
public class DebugEventArgs : EventArgs
{
+ /// Gets the 1-based line number of the executed line within its source file.
public int LineNumber { get; internal set; }
+
+ ///
+ /// Gets the line content after all statement transformations have been applied
+ /// (e.g. after variable substitution). May differ from .
+ ///
public string CurrentLine { get; internal set; }
+
+ /// Gets the raw line content as it appeared in the source file.
public string OriginalLine { get; internal set; }
+
+ ///
+ /// Gets the task identifier of the task that executed this line, or 0 if the line
+ /// was executed on the main thread.
+ ///
public int TaskId { get; internal set; }
+
+ ///
+ /// Gets a value indicating whether this line was executed inside a background task
+ /// (spawned with the task statement).
+ ///
public bool IsTask { get; internal set; }
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Runtime/ExitMessages.cs b/YesNt.Interpreter/Runtime/ExitMessages.cs
index a86b289..4d08b09 100644
--- a/YesNt.Interpreter/Runtime/ExitMessages.cs
+++ b/YesNt.Interpreter/Runtime/ExitMessages.cs
@@ -1,5 +1,9 @@
namespace YesNt.Interpreter.Runtime;
+///
+/// Central repository of all exit/error message strings used by .
+/// Keeping messages here ensures consistency and makes them easy to find or localise.
+///
internal static class ExitMessages
{
internal const string InvalidSyntax = "Invalid syntax";
diff --git a/YesNt.Interpreter/Runtime/FunctionScope.cs b/YesNt.Interpreter/Runtime/FunctionScope.cs
index 4bea9c6..e7c7abd 100644
--- a/YesNt.Interpreter/Runtime/FunctionScope.cs
+++ b/YesNt.Interpreter/Runtime/FunctionScope.cs
@@ -2,12 +2,27 @@
namespace YesNt.Interpreter.Runtime;
+///
+/// Represents one frame on the function call stack. Created when a call statement is
+/// executed and popped when the matching return is reached.
+///
internal class FunctionScope(int callerLine, Stack arguments)
{
+ /// Gets the zero-based line index to return to after this function completes.
public int CallerLine { get; } = callerLine;
+
+ /// Gets the local variable table for this function invocation.
public Dictionary Variables { get; } = [];
+
+ /// Gets the local list table for this function invocation.
public Dictionary> Lists { get; } = [];
+
+ /// Gets the local label table for this function invocation.
public Dictionary Labels { get; } = [];
+
+ /// Gets the stack of input arguments passed to this function via push_in.
public Stack Arguments { get; } = arguments;
+
+ /// Gets the stack of output values pushed via push_out, consumed by the caller via %out.
public Stack Results { get; } = new();
}
diff --git a/YesNt.Interpreter/Runtime/Line.cs b/YesNt.Interpreter/Runtime/Line.cs
index 7f3447d..fcb53af 100644
--- a/YesNt.Interpreter/Runtime/Line.cs
+++ b/YesNt.Interpreter/Runtime/Line.cs
@@ -1,10 +1,16 @@
namespace YesNt.Interpreter.Runtime;
+///
+/// Represents a single source line together with its location metadata.
+///
internal class Line(string content, string fileName, int lineNumber)
{
+ /// Gets or sets the raw text content of the line.
public string Content { get; set; } = content;
+ /// Gets or sets the name of the source file this line originated from.
public string FileName { get; set; } = fileName;
+ /// Gets or sets the zero-based line index within .
public int LineNumber { get; set; } = lineNumber;
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Runtime/RuntimeInformation.cs b/YesNt.Interpreter/Runtime/RuntimeInformation.cs
index 3e9a49c..7ad1ca1 100644
--- a/YesNt.Interpreter/Runtime/RuntimeInformation.cs
+++ b/YesNt.Interpreter/Runtime/RuntimeInformation.cs
@@ -5,6 +5,13 @@ using YesNt.Interpreter.Utilities;
namespace YesNt.Interpreter.Runtime;
+///
+/// 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 task statement owns its own
+/// whose points back
+/// to the main execution context.
+///
internal sealed class RuntimeInformation
{
public event Action OnDebugOutput;
diff --git a/YesNt.Interpreter/Runtime/StatementInformation.cs b/YesNt.Interpreter/Runtime/StatementInformation.cs
index ab7e9a0..e08f4a6 100644
--- a/YesNt.Interpreter/Runtime/StatementInformation.cs
+++ b/YesNt.Interpreter/Runtime/StatementInformation.cs
@@ -4,12 +4,32 @@ using YesNt.Interpreter.Enums;
namespace YesNt.Interpreter.Runtime;
+///
+/// A read-only snapshot of a registered statement's metadata, used for tooling such as
+/// syntax highlighters. Instances are obtained from .
+///
public class StatementInformation
{
+ /// Gets the keyword that identifies this statement in source code.
public string Name { get; internal set; }
+
+ /// Gets where in the line the keyword is searched for.
public SearchMode SearchMode { get; internal set; }
+
+ /// Gets which sides of the keyword must be padded with a space.
public SpaceAround SpaceAround { get; internal set; }
+
+ /// Gets the syntax-highlight colour for this statement.
public ConsoleColor Color { get; internal set; }
+
+ ///
+ /// Gets a value indicating whether this statement is excluded from syntax highlighting.
+ ///
public bool IgnoreSyntaxHighlighting { get; internal set; }
+
+ ///
+ /// Gets the optional sub-string that must be present in the line for this statement to match,
+ /// or if no separator is required.
+ ///
public string Separator { get; set; }
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Runtime/StatementRuntimeInfo.cs b/YesNt.Interpreter/Runtime/StatementRuntimeInfo.cs
index 3a0c30a..a8b89e7 100644
--- a/YesNt.Interpreter/Runtime/StatementRuntimeInfo.cs
+++ b/YesNt.Interpreter/Runtime/StatementRuntimeInfo.cs
@@ -1,6 +1,16 @@
namespace YesNt.Interpreter.Runtime;
+///
+/// Base class for all classes that host statement handler methods.
+/// Subclasses declare methods decorated with or
+/// ; the source generator
+/// (GeneratedStatementRegistry) discovers these at compile time and wires them up.
+///
internal abstract class StatementRuntimeInformation
{
+ ///
+ /// Gets or sets the runtime state for the current execution context.
+ /// Injected by the generated registry before any handler is invoked.
+ ///
public RuntimeInformation RuntimeInfo { get; set; }
}
\ No newline at end of file
diff --git a/YesNt.Interpreter/Runtime/YesNtInterpreter.cs b/YesNt.Interpreter/Runtime/YesNtInterpreter.cs
index 02a3f65..462918a 100644
--- a/YesNt.Interpreter/Runtime/YesNtInterpreter.cs
+++ b/YesNt.Interpreter/Runtime/YesNtInterpreter.cs
@@ -10,16 +10,45 @@ using YesNt.Interpreter.Utilities;
namespace YesNt.Interpreter.Runtime;
+///
+/// The main entry point for executing YesNt scripts.
+///
+///
+/// Running a script file:
+///
+/// var interpreter = new YesNtInterpreter();
+/// interpreter.Execute("path/to/script.ynt");
+///
+/// Running script lines in memory with a custom statement:
+///
+/// var interpreter = new YesNtInterpreter();
+/// interpreter.AddStatement("log", SearchMode.StartOfLine, SpaceAround.End, args =>
+/// Console.WriteLine($"[LOG] {args}"));
+/// interpreter.Execute(new List<string> { "log hello world" });
+///
+///
public class YesNtInterpreter
{
+ ///
+ /// Raised after each line is executed in debug mode. The argument is
+ /// when execution ends (either normally or due to an error), allowing callers to detect completion.
+ ///
public event Action OnLineExecuted;
+ ///
+ /// Raised in debug mode whenever the script produces output (e.g. via print_line).
+ /// In non-debug mode output is written directly to .
+ ///
public event Action OnDebugOutput;
private readonly RuntimeInformation runtimeInfo = new RuntimeInformation();
private Dictionary> statements;
private readonly List> staticStatements;
+ ///
+ /// Gets a read-only snapshot of all currently registered statements.
+ /// Useful for building syntax highlighters or documentation tools.
+ ///
public ReadOnlyCollection StatementInformation
{
get
@@ -41,6 +70,16 @@ public class YesNtInterpreter
}
}
+ ///
+ /// Registers a custom statement using a pre-built .
+ /// If a statement with the same attribute key already exists it will be replaced.
+ /// The statement list is re-sorted by priority after insertion.
+ ///
+ /// The attribute describing the keyword, search mode, and priority.
+ ///
+ /// The delegate invoked when the statement matches. Receives the argument text
+ /// (the part of the line after the keyword, unless is set).
+ ///
public void AddStatement(StatementAttribute attribute, Action handler)
{
statements[attribute] = handler;
@@ -50,16 +89,34 @@ public class YesNtInterpreter
.ToDictionary(x => x.Key, x => x.Value);
}
+ ///
+ /// Registers a custom statement without a syntax-highlight colour.
+ ///
+ /// The keyword that identifies this statement in source code.
+ /// Where in the line the keyword is matched.
+ /// Which sides of the keyword require a surrounding space.
+ /// The delegate invoked when the statement matches.
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action handler)
{
AddStatement(new StatementAttribute(name, searchMode, spaceAround), handler);
}
+ ///
+ /// Registers a custom statement with a syntax-highlight colour.
+ ///
+ /// The keyword that identifies this statement in source code.
+ /// Where in the line the keyword is matched.
+ /// Which sides of the keyword require a surrounding space.
+ /// The colour used for syntax highlighting in the code editor.
+ /// The delegate invoked when the statement matches.
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action handler)
{
AddStatement(new StatementAttribute(name, searchMode, spaceAround, consoleColor), handler);
}
+ ///
+ /// Initialises a new and registers all built-in statements.
+ ///
public YesNtInterpreter()
{
GeneratedStatementRegistry.Register(runtimeInfo, out statements, out staticStatements);
@@ -68,11 +125,23 @@ public class YesNtInterpreter
runtimeInfo.OnLineExecuted += e => OnLineExecuted?.Invoke(e);
}
+ ///
+ /// Requests a graceful stop of the currently executing script.
+ /// The interpreter will terminate at the next line boundary.
+ ///
public void Stop()
{
runtimeInfo.Exit(ExitMessages.TerminatedByExternalProcess, true);
}
+ ///
+ /// Executes a YesNt script file.
+ ///
+ /// The path to the .ynt script file.
+ ///
+ /// When , output is routed through instead of
+ /// and line-execution events are raised via .
+ ///
public void Execute(string path, bool isDebugMode = false)
{
runtimeInfo.Reset();
@@ -83,6 +152,14 @@ public class YesNtInterpreter
}
}
+ ///
+ /// Executes a YesNt script supplied as an in-memory list of lines.
+ ///
+ /// The script lines to execute.
+ ///
+ /// When , output is routed through and
+ /// line-execution events are raised via .
+ ///
public void Execute(List lines, bool isDebugMode = false)
{
runtimeInfo.Reset();
diff --git a/YesNt.Interpreter/Utilities/Evaluator.cs b/YesNt.Interpreter/Utilities/Evaluator.cs
index a13fa91..427731a 100644
--- a/YesNt.Interpreter/Utilities/Evaluator.cs
+++ b/YesNt.Interpreter/Utilities/Evaluator.cs
@@ -4,8 +4,19 @@ using System.Text.RegularExpressions;
namespace YesNt.Interpreter.Utilities;
+///
+/// Provides expression evaluation used by conditional and arithmetic statements.
+///
internal static partial class Evaluator
{
+ ///
+ /// Evaluates a boolean condition string such as a == b, x > 3, or true.
+ ///
+ /// The condition expression, which may contain safe-string encoded values.
+ ///
+ /// or if the condition could be evaluated;
+ /// if the expression is not a recognised condition form (treated as an error by callers).
+ ///
public static bool? EvaluateCondition(string input)
{
if (input.ToLower().FromSafeString().Trim() == "true")
@@ -68,6 +79,13 @@ internal static partial class Evaluator
return null;
}
+ ///
+ /// Evaluates a numeric arithmetic expression string and returns the result as a string.
+ /// Supports +, -, *, and / operators.
+ /// Adjacent sign characters (++, --, -+, +-) are normalised before evaluation.
+ ///
+ /// The arithmetic expression to evaluate.
+ /// The result as a culture-invariant numeric string, or "NaN" if evaluation failed.
public static string Calculate(string input)
{
input = PlusPlusRegex().Replace(input, "+");
diff --git a/YesNt.Interpreter/Utilities/FixedProcess.cs b/YesNt.Interpreter/Utilities/FixedProcess.cs
index 28b401a..9430c88 100644
--- a/YesNt.Interpreter/Utilities/FixedProcess.cs
+++ b/YesNt.Interpreter/Utilities/FixedProcess.cs
@@ -11,6 +11,14 @@ public delegate void DataReceivedEventHandler(object sender, DataReceivedEventAr
internal delegate void UserCallBack(string data);
+///
+/// A workaround replacement for that fixes a buffering
+/// issue in / :
+/// 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.
+/// flushes whatever is in the read buffer immediately, enabling real-time
+/// output forwarding for interactive child processes.
+///
public class FixedProcess : Process
{
public new event DataReceivedEventHandler OutputDataReceived;
diff --git a/YesNt.Interpreter/Utilities/StringExtentions.cs b/YesNt.Interpreter/Utilities/StringExtentions.cs
index a28f1ea..71d13f7 100644
--- a/YesNt.Interpreter/Utilities/StringExtentions.cs
+++ b/YesNt.Interpreter/Utilities/StringExtentions.cs
@@ -6,10 +6,27 @@ using System.Text;
namespace YesNt.Interpreter.Utilities;
+///
+/// Extension methods for string manipulation used throughout the interpreter.
+///
+///
+///
+/// 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 → ~spc, newline → ~nli). The mapping is defined in
+/// . Use to encode and
+/// to decode.
+///
+///
public static class StringExtensions
{
private static readonly Dictionary reverseReplacementRules;
+ ///
+ /// 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.
+ ///
public static Dictionary ReplacementRules { get; } = new()
{
{"~", "~til" },
@@ -34,6 +51,13 @@ public static class StringExtensions
reverseReplacementRules = ReplacementRules.ToDictionary(x => x.Value, x => x.Key);
}
+ ///
+ /// 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.
+ ///
+ /// The plain string to encode.
+ /// The safe-string encoded representation.
public static string ToSafeString(this string input)
{
StringBuilder output = new StringBuilder();
@@ -45,28 +69,53 @@ public static class StringExtensions
return ReplaceOnce(output.ToString(), ReplacementRules);
}
+ ///
+ /// Decodes a safe-string back to its original plain-text form.
+ ///
+ /// A safe-string encoded string.
+ /// The decoded plain string.
public static string FromSafeString(this string input)
{
return ReplaceOnce(input.Replace("\v", string.Empty), reverseReplacementRules);
}
+ ///
+ /// Tries to parse the string as a , first decoding safe-string encoding
+ /// and normalising decimal separators (comma → period).
+ ///
+ /// The string to parse (may be safe-string encoded).
+ /// When this method returns, contains the parsed value if successful.
+ /// if parsing succeeded; otherwise .
public static bool ToStandardizedNumber(this string input, out double result)
{
return double.TryParse(input.FromSafeString().Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
}
+ /// Replaces only the first occurrence of in the string.
+ /// The source string.
+ /// The substring to find.
+ /// The replacement value.
+ /// A new string with the first occurrence replaced.
public static string ReplaceFirstOccurrence(this string input, string oldValue, string newValue)
{
int place = input.IndexOf(oldValue);
return input.Remove(place, oldValue.Length).Insert(place, newValue);
}
+ /// Replaces only the last occurrence of in the string.
+ /// The source string.
+ /// The substring to find.
+ /// The replacement value.
+ /// A new string with the last occurrence replaced.
public static string ReplaceLastOccurrence(this string input, string oldValue, string newValue)
{
int place = input.LastIndexOf(oldValue);
return input.Remove(place, Math.Min(oldValue.Length, input.Length - place)).Insert(place, newValue);
}
+ /// Counts the number of trailing whitespace characters in the string.
+ /// The source string.
+ /// The number of whitespace characters at the end of the string.
public static int WhiteSpaceAtEnd(this string input)
{
int count = 0;