添加项目文件。

This commit is contained in:
Zel
2025-01-22 23:31:03 +08:00
parent 1b8ba6771f
commit 2ae76476fb
894 changed files with 774558 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
namespace TightWiki.Engine.Function.Exceptions
{
public class WikiFunctionPrototypeNotDefinedException : Exception
{
public WikiFunctionPrototypeNotDefinedException()
{
}
public WikiFunctionPrototypeNotDefinedException(string message)
: base(message)
{
}
}
}

View File

@@ -0,0 +1,14 @@
namespace TightWiki.Engine.Function.Exceptions
{
public class WikiFunctionPrototypeSyntaxError : Exception
{
public WikiFunctionPrototypeSyntaxError()
{
}
public WikiFunctionPrototypeSyntaxError(string message)
: base(message)
{
}
}
}

View File

@@ -0,0 +1,210 @@
namespace TightWiki.Engine.Function
{
/// <summary>
/// Contains information about an actual function call, its supplied parameters, and is matched with a defined function.
/// </summary>
public class FunctionCall
{
/// <summary>
/// The name of the function being called.
/// </summary>
public string Name { get; private set; }
public FunctionPrototype Prototype { get; set; }
/// <summary>
/// The arguments supplied by the caller.
/// </summary>
public FunctionParameters Parameters { get; private set; }
public FunctionCall(FunctionPrototype prototype, List<string> args)
{
Prototype = prototype;
Parameters = new FunctionParameters(this);
Name = prototype.FunctionName;
foreach (var arg in args)
{
if (arg.StartsWith(':') && arg.Contains('='))
{
var parsed = arg.Substring(1); //Skip the colon.
int index = parsed.IndexOf('=');
var name = parsed.Substring(0, index).Trim().ToLower();
var value = parsed.Substring(index + 1).Trim();
Parameters.Named.Add(new NamedParameter(name, value));
}
else
{
Parameters.Ordinals.Add(new OrdinalParameter(arg));
}
}
ApplyPrototype();
}
/// <summary>
/// Checks the passed value against the function prototype to ensure that the variable is the correct type, value, etc.
/// </summary>
/// <param name="segment"></param>
/// <param name="value"></param>
/// <exception cref="Exception"></exception>
private void EnforcePrototypeParamValue(PrototypeParameter param, string value)
{
if (param.Type == "bool")
{
if (bool.TryParse(value, out bool _) == false)
{
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] could not be converted to boolean.");
}
}
if (param.Type == "integer")
{
if (int.TryParse(value, out int _) == false)
{
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] could not be converted to integer.");
}
}
else if (param.Type == "float")
{
if (double.TryParse(value, out double _) == false)
{
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] could not be converted to float.");
}
}
if (param.AllowedValues != null && param.AllowedValues.Count > 0)
{
if (param.AllowedValues.Contains(value.ToLower()) == false)
{
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] is not allowed. Allowed values are [{string.Join(",", param.AllowedValues)}].");
}
}
}
/// <summary>
/// Rolls through the supplied arguments and applies them to the prototype. Also identifies which supplied arguments are associated with each
/// prototype argument and adds the ordinal based arguments to the name based collection. Ensures that each argument conforms with the prototype.
/// </summary>
/// <exception cref="Exception"></exception>
private void ApplyPrototype()
{
int index = 0;
//Keep a list of the arguments as they are associated with the prototype so that we can later reference them by name.
var namedToAddLater = new List<NamedParameter>();
//Handle non-infinite ordinal based required parameters:
for (; index < Prototype.Parameters.Count; index++)
{
var param = Prototype.Parameters[index];
if (param.IsRequired == false)
{
break;
}
if (param.IsInfinite == true)
{
break;
}
if (Parameters.Ordinals.Count > index)
{
//Good, we have a value.
string value = Parameters.Ordinals[index].Value;
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
EnforcePrototypeParamValue(param, value.ToLower());
namedToAddLater.Add(new NamedParameter(param.Name, value));
}
else
{
throw new Exception($"Function [{Name}], the required parameter [{param.Name}] was not specified.");
}
}
bool hasEncounteredOptionalParameter = false;
//Handle remaining optional parameters:
for (; index < Prototype.Parameters.Count; index++)
{
var param = Prototype.Parameters[index];
if (param.IsInfinite == true)
{
if (param.IsRequired == true)
{
//Make sure we have at least one of these required infinite parameters passed.
if (Parameters.Ordinals.Count > index)
{
//Good, we have a value.
string value = Parameters.Ordinals[index].Value;
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
EnforcePrototypeParamValue(param, value.ToLower());
}
else
{
throw new Exception($"Function [{Name}], the required infinite parameter [{param.Name}] was not passed.");
}
}
//Now that we have encountered an infinite parameter, it will swallow up all other ordinal based arguments. Might as well check the types and exit the loop.
for (; index < Parameters.Ordinals.Count; index++)
{
string value = Parameters.Ordinals[index].Value;
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
EnforcePrototypeParamValue(param, value.ToLower());
namedToAddLater.Add(new NamedParameter(param.Name, value));
}
break;
}
if (param.IsRequired == false)
{
hasEncounteredOptionalParameter = true;
}
if (param.IsRequired == true && hasEncounteredOptionalParameter)
{
throw new Exception($"Function [{Name}], the required parameter [{param.Name}] was found after other optional parameters.");
}
else if (param.IsInfinite == true)
{
throw new Exception($"Function [{Name}], encountered an unexpected number of infinite parameters in prototype for [{param.Name}].");
}
if (Parameters.Ordinals.Count > index)
{
string value = Parameters.Ordinals[index].Value;
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
EnforcePrototypeParamValue(param, value.ToLower());
namedToAddLater.Add(new NamedParameter(param.Name, value));
}
}
foreach (var named in Parameters.Named)
{
var param = Prototype.Parameters.Where(o => o.Name.Equals(named.Name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()
?? throw new Exception($"Function [{Name}], the named parameter [{named.Name}] is not defined in the function prototype.");
EnforcePrototypeParamValue(param, named.Value);
}
Parameters.Named.AddRange(namedToAddLater);
var unmatchedParams = Parameters.Ordinals.Where(o => o.IsMatched == false).ToList();
if (unmatchedParams.Count != 0)
{
throw new Exception($"Function [{Name}], unmatched parameter value [{unmatchedParams.First().Value}].");
}
var nonInfiniteParams = Prototype.Parameters.Where(o => o.IsInfinite == false).Select(o => o.Name.ToLower());
var groups = Parameters.Named.Where(o => nonInfiniteParams.Contains(o.Name.ToLower())).GroupBy(o => o.Name.ToLower()).Where(o => o.Count() > 1);
if (groups.Any())
{
var group = groups.First();
throw new Exception($"Function [{Name}], non-infinite parameter specified more than once: [{group.Key}].");
}
}
}
}

View File

@@ -0,0 +1,75 @@
using NTDLS.Helpers;
namespace TightWiki.Engine.Function
{
public class FunctionParameters
{
/// <summary>
/// Variables set by ordinal.
/// </summary>
public List<OrdinalParameter> Ordinals { get; set; } = new();
/// <summary>
/// Variables set by name.
/// </summary>
public List<NamedParameter> Named { get; private set; } = new();
private readonly FunctionCall _owner;
public FunctionParameters(FunctionCall owner)
{
_owner = owner;
}
public T Get<T>(string name)
{
try
{
var value = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value;
if (value == null)
{
var prototype = _owner.Prototype.Parameters.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).First();
return Converters.ConvertTo<T>(prototype.DefaultValue) ?? throw new Exception("Value cannot be null");
}
return Converters.ConvertTo<T>(value) ?? throw new Exception("Value cannot be null");
}
catch (Exception ex)
{
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
}
}
public T Get<T>(string name, T defaultValue)
{
try
{
var value = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value;
if (value == null)
{
return defaultValue;
}
return Converters.ConvertTo<T>(value) ?? throw new Exception("Value cannot be null");
}
catch (Exception ex)
{
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
}
}
public List<T> GetList<T>(string name)
{
try
{
var values = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))?
.Select(o => Converters.ConvertTo<T>(o.Value) ?? throw new Exception("Value cannot be null"))?.ToList();
return values ?? new List<T>();
}
catch (Exception ex)
{
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,220 @@
using System.Text;
using System.Text.RegularExpressions;
using TightWiki.Engine.Function.Exceptions;
namespace TightWiki.Engine.Function
{
public static partial class FunctionParser
{
[GeneratedRegex(@"(##|{{|@@)([a-zA-Z_\s{][a-zA-Z0-9_\s{]*)\(((?<BR>\()|(?<-BR>\))|[^()]*)+\)")]
private static partial Regex FunctionCallParser();
/// <summary>
/// Parsed a function call, its parameters and matches it to a defined function and its prototype.
/// </summary>
/// <param name="functionCall"></param>
/// <param name="parseEndIndex"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall, out int parseEndIndex)
{
var rawArguments = new List<string>();
var parsed = ParseFunctionCall(prototypes, functionCall);
var prototype = prototypes.Get(parsed.Prefix, parsed.Name);
if (prototype == null)
{
throw new WikiFunctionPrototypeNotDefinedException($"Function ({parsed.Name}) does not have a defined prototype.");
}
parseEndIndex = parsed.EndIndex;
return new FunctionCall(prototype, parsed.RawArguments);
}
public static ParsedFunctionCall ParseFunctionCall(FunctionPrototypeCollection prototypes, string functionCall)
{
string functionName = string.Empty;
int parseEndIndex = 0;
var rawArguments = new List<string>();
var firstLine = functionCall.Split('\n')?.FirstOrDefault();
if (firstLine == null || firstLine.Where(x => x == '(').Count() != firstLine.Where(x => x == ')').Count())
{
throw new WikiFunctionPrototypeSyntaxError($"Function parentheses mismatch.");
}
string functionPrefix = functionCall.Substring(0, 2);
var parameterMatches = FunctionCallParser().Matches(firstLine);
if (parameterMatches.Count > 0)
{
var match = parameterMatches[0];
int paramStartIndex = match.Value.IndexOf('(');
functionName = match.Value[..paramStartIndex].ToLower().TrimStart(['{', '#', '@']).Trim();
parseEndIndex = match.Index + match.Length;
string rawArgTrimmed = match.ToString().Substring(paramStartIndex, (match.ToString().Length - paramStartIndex));
rawArguments = ParseRawArguments(rawArgTrimmed);
}
else //The function call has no parameters.
{
int endOfLine = functionCall.Substring(2).TakeWhile(c => char.IsLetterOrDigit(c)).Count(); //Find the first non-alphanumeric after the function identifier (##, @@, etc).
functionName = functionCall.Substring(2, endOfLine).ToLower().TrimStart(['{', '#', '@']).Trim();
parseEndIndex = endOfLine + 2;
}
return new ParsedFunctionCall(functionPrefix, functionName, parseEndIndex, rawArguments);
}
/// <summary>
/// Parses function parameters into a list of arguments based on comma separation.
/// String do not need to be enclosed in double-quotes unless they contain commas.
/// </summary>
/// <param name="paramString"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static List<string> ParseRawArgumentsAddParenthesis(string paramString)
{
if (paramString.StartsWith('(') || paramString.EndsWith(')'))
{
throw new WikiFunctionPrototypeSyntaxError($"Unexpected '(' or ')'.");
}
return ParseRawArguments($"({paramString})");
}
/// <summary>
/// Parses function parameters into a list of arguments based on comma separation.
/// String do not need to be enclosed in double-quotes unless they contain commas.
/// </summary>
/// <param name="paramString"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static List<string> ParseRawArguments(string paramString)
{
List<string> ps = new();
int readPos = 0;
var singleParam = new StringBuilder();
if (paramString[readPos] != '(')
{
throw new WikiFunctionPrototypeSyntaxError($"Expected '('.");
}
int parenNest = 1;
readPos++; //Skip the (
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
while (true)
{
if (paramString[readPos] == '(')
{
parenNest++;
}
else if (paramString[readPos] == ')')
{
parenNest--;
}
if (readPos == paramString.Length)
{
throw new WikiFunctionPrototypeSyntaxError($"Expected ')'.");
}
else if (paramString[readPos] == ')' && parenNest == 0)
{
readPos++; //Skip the )
if (parenNest == 0 && readPos != paramString.Length)
{
throw new WikiFunctionPrototypeSyntaxError($"Expected end of statement.");
}
if (singleParam.Length > 0)
{
ps.Add(singleParam.ToString());
}
singleParam.Clear();
if (parenNest == 0)
{
break;
}
}
else if (paramString[readPos] == '\"')
{
readPos++; //Skip the ".
bool escapeChar = false;
for (; ; readPos++)
{
if (readPos == paramString.Length)
{
throw new WikiFunctionPrototypeSyntaxError($"Expected end of string.");
}
else if (paramString[readPos] == '\\')
{
escapeChar = true;
continue;
}
else if (paramString[readPos] == '\"' && escapeChar == false)
{
//Found the end of the string:
readPos++; //Skip the ".
break;
}
else
{
singleParam.Append(paramString[readPos]);
}
escapeChar = false;
}
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
}
else if (paramString[readPos] == ',')
{
readPos++; //Skip the ,
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
ps.Add(singleParam.ToString());
singleParam.Clear();
continue;
}
else
{
singleParam.Append(paramString[readPos]);
if (paramString[readPos] == '(')
{
readPos++;
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
}
else if (paramString[readPos] == ')')
{
readPos++;
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
}
else
{
readPos++;
}
}
}
for (int i = 0; i < ps.Count; i++)
{
ps[i] = ps[i].Trim();
}
return ps;
}
}
}

View File

@@ -0,0 +1,15 @@
namespace TightWiki.Engine.Function
{
public class FunctionPrototype
{
public string FunctionPrefix { get; set; } = string.Empty;
public string ProperName { get; set; } = string.Empty;
public string FunctionName { get; set; } = string.Empty;
public List<PrototypeParameter> Parameters { get; set; }
public FunctionPrototype()
{
Parameters = new List<PrototypeParameter>();
}
}
}

View File

@@ -0,0 +1,207 @@
using TightWiki.Engine.Function.Exceptions;
namespace TightWiki.Engine.Function
{
public class FunctionPrototypeCollection
{
public enum WikiFunctionType
{
Standard,
Scoped,
Instruction
}
public WikiFunctionType FunctionTypes { get; private set; }
public List<PrototypeSet> Items { get; set; } = new();
public FunctionPrototypeCollection(WikiFunctionType functionTypes)
{
FunctionTypes = functionTypes;
}
public void Add(string prototypeString)
{
var prototype = ParsePrototype(prototypeString);
Items.Add(new PrototypeSet()
{
FunctionPrefix = prototype.FunctionPrefix,
ProperName = prototype.ProperName,
FunctionName = prototype.FunctionName.ToLower(),
Value = prototype
});
}
public bool Exists(string functionPrefix, string functionName)
{
functionName = functionName.ToLower();
//$$ are scope functions and are not called by prefix, we only have prefixes to make it easier to parse
// the functions in the wikiText and scope functions are easy enough since they start with curly braces.
return Items.Any(o => (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName);
}
public FunctionPrototype Get(string functionPrefix, string functionName)
{
functionName = functionName.ToLower();
//$$ are scope functions and are not called by prefix, we only have prefixes to make it easier to parse
// the functions in the wikiText and scope functions are easy enough since they start with curly braces.
var functionPrototype = Items.Where(o => (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName).FirstOrDefault()?.Value;
return functionPrototype
?? throw new WikiFunctionPrototypeNotDefinedException($"Function ({functionName}) does not have a defined prototype.");
}
private FunctionPrototype ParsePrototype(string prototypeString)
{
int nameStartIndex = prototypeString.TakeWhile(c => char.IsLetterOrDigit(c) == false).Count();
int nameEndIndex = prototypeString.IndexOf(':');
string properName = prototypeString.Substring(nameStartIndex, nameEndIndex - nameStartIndex).Trim();
string functionName = properName.ToLower();
string functionPrefix = prototypeString.Substring(0, nameStartIndex).Trim();
prototypeString = prototypeString.Substring(nameEndIndex + 1).Trim();
var prototype = new FunctionPrototype() { FunctionPrefix = functionPrefix, ProperName = properName, FunctionName = functionName };
if (prototypeString.Length == 0)
{
//No parameters.
return prototype;
}
var segments = prototypeString.Trim().Split('|').Select(o => o.Trim());
foreach (var segment in segments)
{
var prototypeSegment = new PrototypeParameter();
int index = 0;
if (segment[index] == '<')
{
index++; //Skip the '<'
prototypeSegment.Type = Tok(segment, ref index);
index++; //Skip the '>'
if (prototypeSegment.Type.Contains(':'))
{
var splitSeg = prototypeSegment.Type.Split(':');
prototypeSegment.Type = splitSeg[0];
if (splitSeg[1].Equals("infinite", StringComparison.InvariantCultureIgnoreCase))
{
prototypeSegment.IsInfinite = true;
if (prototype.Parameters.Any(o => o.IsInfinite))
{
throw new Exception($"Function [{functionName}], prototype error: cannot contain more than one [infinite] parameter.");
}
}
else
{
throw new Exception($"Function [{functionName}], prototype error: expected [infinite] got [{splitSeg[1]}].");
}
}
SkipWhiteSpace(segment, ref index);
if (index < segment.Length && segment[index] == '{' || segment[index] == '[')
{
if (index < segment.Length && segment[index] == '[')
{
prototypeSegment.IsRequired = true;
}
index++; //Skip the '[' or '{'
prototypeSegment.Name = Tok(segment, ref index);
if (index < segment.Length && segment[index] == '(') //Parse allowed values.
{
int allowedValueEndIndex = segment.IndexOf(')', index);
string roteRequiredValues = segment.Substring(index + 1, allowedValueEndIndex - index - 1);
prototypeSegment.AllowedValues = roteRequiredValues.Trim().Split(',').Select(o => o.Trim().ToLower()).ToList();
index = allowedValueEndIndex;
index++; //Skip the ')'
SkipWhiteSpace(segment, ref index);
}
index++; //Skip the ']' or '}'
}
else
{
throw new Exception($"Function [{functionName}], prototype error: expected [{{] or [[].");
}
SkipWhiteSpace(segment, ref index);
if (index < segment.Length && segment[index] == '=')
{
index++; //Skip the '='
SkipWhiteSpace(segment, ref index);
if (segment[index] != '\'')
{
throw new Exception($"Function [{functionName}], prototype error: expected [\'].");
}
index++; //Skip the '''
prototypeSegment.DefaultValue = segment.Substring(index, (segment.Length - index) - 1);
index = segment.Length - 1;
if (index < segment.Length && segment[index] != '\'')
{
throw new Exception($"Function [{functionName}], prototype error: expected [\'].");
}
}
}
else
{
throw new Exception($"Function [{functionName}], prototype error: expected [<].");
}
prototype.Parameters.Add(prototypeSegment);
}
return prototype;
}
/// <summary>
/// Gets the next token in a string.
/// </summary>
/// <param name="str"></param>
/// <param name="index"></param>
/// <returns></returns>
private string Tok(string str, ref int index)
{
var token = string.Empty;
SkipWhiteSpace(str, ref index);
for (; index < str.Length; index++)
{
if ("<>{}[]()".Contains(str[index]))
{
break;
}
token += str[index];
}
SkipWhiteSpace(str, ref index);
return token;
}
private static void SkipWhiteSpace(string str, ref int index)
{
while (index < str.Length && char.IsWhiteSpace(str[index]))
{
index++;
}
}
}
}

View File

@@ -0,0 +1,14 @@
namespace TightWiki.Engine.Function
{
public class NamedParameter
{
public string Name { get; set; }
public string Value { get; set; }
public NamedParameter(string name, string value)
{
Name = name;
Value = value;
}
}
}

View File

@@ -0,0 +1,28 @@
namespace TightWiki.Engine.Function
{
public class OrdinalParameter
{
public string Value { get; set; }
/// <summary>
/// Has been matched to a prototype parameter?
/// </summary>
public bool IsMatched { get; set; } = false;
/// <summary>
/// If matched to a prototype parameter, this is the name of the parameter.
/// </summary>
public string ParameterName { get; set; } = string.Empty;
public OrdinalParameter(string value)
{
Value = value;
}
public void AssociateWithPrototypeParam(string paramName)
{
IsMatched = true;
ParameterName = paramName;
}
}
}

View File

@@ -0,0 +1,18 @@
namespace TightWiki.Engine.Function
{
public class ParsedFunctionCall
{
public string Prefix { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public int EndIndex { get; set; }
public List<string> RawArguments { get; set; } = new List<string>();
public ParsedFunctionCall(string prefix, string name, int endIndex, List<string> rawArguments)
{
Prefix = prefix;
Name = name;
EndIndex = endIndex;
RawArguments = rawArguments;
}
}
}

View File

@@ -0,0 +1,12 @@
namespace TightWiki.Engine.Function
{
public class PrototypeParameter
{
public string Type { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string DefaultValue { get; set; } = string.Empty;
public bool IsRequired { get; set; } = false;
public bool IsInfinite { get; set; } = false;
public List<string> AllowedValues { get; set; } = new();
}
}

View File

@@ -0,0 +1,10 @@
namespace TightWiki.Engine.Function
{
public class PrototypeSet
{
public string FunctionPrefix { get; set; } = string.Empty;
public string ProperName { get; set; } = string.Empty;
public string FunctionName { get; set; } = string.Empty;
public FunctionPrototype Value { get; set; } = new();
}
}

View File

@@ -0,0 +1,176 @@
namespace TightWiki.Engine.Function
{
public static class SelfDocument
{
/// <summary>
/// Don't ever look at this. :(
/// </summary>
public static void CreateNotExisting()
{
/*
System.Threading.Thread.Sleep(500);
foreach (var item in FunctionPrototypeDefinitions.Collection.Items)
{
string functionType = "Function";
string functionPrefix = item.FunctionPrefix;
if (item.FunctionPrefix == "##")
{
functionType = "Standard Function";
}
if (item.FunctionPrefix == "@@")
{
functionType = "Instruction Function";
}
if (item.FunctionPrefix == "$$")
{
functionType = "Scope Function";
functionPrefix = string.Empty;
}
string topic = $"Wiki Help :: {item.ProperName}";
if (functionType == "Instruction Function")
{
topic = $"Wiki Help :: {item.ProperName} - Instruction";
}
string navigation = CanonicalNavigation.CleanAndValidate(topic);
var page = PageRepository.GetPageInfoByNavigation(navigation);
if (page == null)
{
var html = new System.Text.StringBuilder();
html.AppendLine("@@draft");
html.AppendLine("@@protect(true)");
html.AppendLine("##Image(Wiki Help :: Wiki Help/TightWiki Logo.png, 15)");
html.AppendLine($"##title ##Tag(Official-Help, Help, Wiki, Official, {functionType})");
html.AppendLine("{{Card(Default, Table of Contents) ##toc }}");
html.AppendLine("");
html.AppendLine("${metaColor = #ee2401}");
html.AppendLine("${keywordColor = #318000}");
html.AppendLine("${identifierColor = #c6680e}");
html.AppendLine("==Overview");
html.AppendLine($"The {item.ProperName} {functionType.ToLower()} is !!FILL_IN_THE_BLANK!!");
html.AppendLine("");
html.AppendLine("");
html.AppendLine("==Prototype");
html.Append($"##Color(${{keywordColor}}, **#{{ {functionPrefix}{item.ProperName} }}#**)");
if ((item.Value.Parameters?.Count ?? 0) == 0)
{
html.AppendLine("()");
}
else
{
html.Append("(");
foreach (var p in item.Value.Parameters)
{
html.Append($"##Color(${{keywordColor}}, {p.Type}{(p.IsInfinite ? ":Infinite" : "")})");
if (p.IsRequired)
{
html.Append($" [##Color(${{identifierColor}}, {p.Name})]");
}
else
{
html.Append($" {{##Color(${{identifierColor}}, {p.Name})}}");
}
html.Append(", ");
}
html.Length -= 3;
html.Append(")");
}
html.AppendLine("");
html.AppendLine("");
html.AppendLine("");
html.AppendLine("===Parameters");
html.AppendLine("{{Bullets");
if (item.Value.Parameters.Count == 0)
{
html.AppendLine($"None.");
}
foreach (var p in item.Value.Parameters)
{
html.AppendLine($"**Name:** ##Color(${{identifierColor}}, {p.Name}) ##Color(${{metaColor}}, {(p.IsRequired ? "[Required]" : "{Optional}")})");
html.AppendLine($">**Type:** ##Color(${{keywordColor}}, {p.Type}{(p.IsInfinite ? ":Infinite" : "")})");
if (string.IsNullOrEmpty(p.DefaultValue) == false)
{
html.AppendLine($">**Default:** ##Color(${{identifierColor}}, {p.DefaultValue})");
}
if (p.AllowedValues != null)
{
html.AppendLine($">**Values:** ##Color(${{identifierColor}}, \"{string.Join(", ", p.AllowedValues)}\")");
}
html.AppendLine($">**Description:** !!FILL_IN_THE_BLANK!!");
}
html.AppendLine("}}");
html.AppendLine("");
html.AppendLine("==Examples");
html.AppendLine("{{Code(wiki)#{");
if (item.FunctionPrefix == "$$")
{
html.Append("{{ " + $"{item.ProperName}");
if ((item.Value.Parameters?.Count ?? 0) == 0)
{
html.AppendLine("()");
}
else
{
html.AppendLine($"({string.Join(", ", item.Value.Parameters.Select(o => o.Name))})");
}
html.AppendLine("This is the body content of the function scope.");
html.AppendLine("}}");
}
else
{
html.Append($"{item.FunctionPrefix}{item.ProperName}");
if ((item.Value.Parameters?.Count ?? 0) == 0)
{
html.AppendLine("()");
}
else
{
html.AppendLine($"({string.Join(", ", item.Value.Parameters.Select(o => o.Name))})");
}
}
html.AppendLine("}#}}");
html.AppendLine("");
html.AppendLine("==See Also");
html.AppendLine("[[Wiki Help :: Function Calling Convention]]");
html.AppendLine("[[Wiki Help :: Scope Function]]");
html.AppendLine("[[Wiki Help :: Instruction Function]]");
html.AppendLine("[[Wiki Help :: Standard Function]]");
html.AppendLine("");
html.AppendLine("");
html.AppendLine("==Related");
html.AppendLine("##related");
page = new Page()
{
Navigation = navigation,
Name = topic,
Description = $"Documentation of the built-in {item.ProperName.ToLower()} {functionType.ToLower()} !!FILL_IN_THE_BLANK!!.",
CreatedByUserId = 1,
CreatedDate = DateTime.UtcNow,
ModifiedByUserId = 1,
ModifiedDate = DateTime.UtcNow,
Body = html.ToString()
};
PageRepository.SavePage(page);
}
}
*/
}
}
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>2.20.1</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NTDLS.Helpers" Version="1.3.11" />
</ItemGroup>
</Project>