123123
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
namespace ZelWiki.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.
|
||||
/// T
|
||||
/// </summary>
|
||||
public FunctionParameters Parameters { get; private set; }
|
||||
|
||||
@@ -25,7 +27,7 @@
|
||||
{
|
||||
if (arg.StartsWith(':') && arg.Contains('='))
|
||||
{
|
||||
var parsed = arg.Substring(1); //Skip the colon.
|
||||
var parsed = arg.Substring(1);
|
||||
int index = parsed.IndexOf('=');
|
||||
var name = parsed.Substring(0, index).Trim().ToLower();
|
||||
var value = parsed.Substring(index + 1).Trim();
|
||||
@@ -42,9 +44,9 @@
|
||||
}
|
||||
|
||||
/// <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="param"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void EnforcePrototypeParamValue(PrototypeParameter param, string value)
|
||||
@@ -53,21 +55,25 @@
|
||||
{
|
||||
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.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 无法转化成布尔");
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 无法转化成整数.");
|
||||
}
|
||||
}
|
||||
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.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 无法转化成小数.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,24 +81,22 @@
|
||||
{
|
||||
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)}].");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 为非法数据. 合法值为 [{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;
|
||||
var 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];
|
||||
@@ -101,15 +105,15 @@
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (param.IsInfinite == true)
|
||||
|
||||
if (param.IsInfinite)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Parameters.Ordinals.Count > index)
|
||||
{
|
||||
//Good, we have a value.
|
||||
string value = Parameters.Ordinals[index].Value;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
|
||||
@@ -123,33 +127,30 @@
|
||||
|
||||
bool hasEncounteredOptionalParameter = false;
|
||||
|
||||
//Handle remaining optional parameters:
|
||||
for (; index < Prototype.Parameters.Count; index++)
|
||||
{
|
||||
var param = Prototype.Parameters[index];
|
||||
|
||||
if (param.IsInfinite == true)
|
||||
if (param.IsInfinite)
|
||||
{
|
||||
if (param.IsRequired == true)
|
||||
if (param.IsRequired)
|
||||
{
|
||||
//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;
|
||||
var 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.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 参数 [{param.Name}] 未通过.");
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
namedToAddLater.Add(new NamedParameter(param.Name, value));
|
||||
@@ -163,18 +164,20 @@
|
||||
hasEncounteredOptionalParameter = true;
|
||||
}
|
||||
|
||||
if (param.IsRequired == true && hasEncounteredOptionalParameter)
|
||||
if (param.IsRequired && hasEncounteredOptionalParameter)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the required parameter [{param.Name}] was found after other optional parameters.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 所必参数 [{param.Name}] 在其他可选参数之后找到.");
|
||||
}
|
||||
else if (param.IsInfinite == true)
|
||||
else if (param.IsInfinite)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], encountered an unexpected number of infinite parameters in prototype for [{param.Name}].");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 参数溢出 [{param.Name}].");
|
||||
}
|
||||
|
||||
if (Parameters.Ordinals.Count > index)
|
||||
{
|
||||
string value = Parameters.Ordinals[index].Value;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
namedToAddLater.Add(new NamedParameter(param.Name, value));
|
||||
@@ -183,8 +186,10 @@
|
||||
|
||||
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.");
|
||||
var param = Prototype.Parameters
|
||||
.FirstOrDefault(o => o.Name.Equals(named.Name, StringComparison.InvariantCultureIgnoreCase))
|
||||
?? throw new Exception(
|
||||
$"函数 [{Name}], 命名参数 [{named.Name}] 未在函数原型中定义.");
|
||||
|
||||
EnforcePrototypeParamValue(param, named.Value);
|
||||
}
|
||||
@@ -194,17 +199,20 @@
|
||||
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}].");
|
||||
throw new Exception($"函数 [{Name}], 不匹配的参数值 [{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);
|
||||
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}].");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 多次指定参数: [{group.Key}].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,38 +4,42 @@ namespace ZelWiki.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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<OrdinalParameter> Ordinals { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<NamedParameter> Named { get; private set; } = new();
|
||||
|
||||
|
||||
|
||||
public T Get<T>(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value;
|
||||
var value = Named
|
||||
.FirstOrDefault(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))?.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");
|
||||
var prototype = _owner.Prototype.Parameters.First(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
return Converters.ConvertTo<T>(prototype.DefaultValue) ??
|
||||
throw new Exception("值不能为空");
|
||||
}
|
||||
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("Value cannot be null");
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("值不能为空");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
|
||||
throw new Exception($"函数 [{_owner.Name}], {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,17 +47,18 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value;
|
||||
var value = Named
|
||||
.FirstOrDefault(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))?.Value;
|
||||
if (value == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("Value cannot be null");
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("值不能为空");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
|
||||
throw new Exception($"函数 [{_owner.Name}], {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,14 +67,15 @@ namespace ZelWiki.Engine.Function
|
||||
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();
|
||||
.Select(o => Converters.ConvertTo<T>(o.Value) ?? throw new Exception("值不能为空"))
|
||||
?.ToList();
|
||||
|
||||
return values ?? new List<T>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
|
||||
throw new Exception($"函数 [{_owner.Name}], {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,15 @@ namespace ZelWiki.Engine.Function
|
||||
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="prototypes"></param>
|
||||
/// <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)
|
||||
/// <exception cref="WikiFunctionPrototypeNotDefinedException"></exception>
|
||||
public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall,
|
||||
out int parseEndIndex)
|
||||
{
|
||||
var rawArguments = new List<string>();
|
||||
|
||||
@@ -25,7 +27,8 @@ namespace ZelWiki.Engine.Function
|
||||
var prototype = prototypes.Get(parsed.Prefix, parsed.Name);
|
||||
if (prototype == null)
|
||||
{
|
||||
throw new WikiFunctionPrototypeNotDefinedException($"Function ({parsed.Name}) does not have a defined prototype.");
|
||||
throw new WikiFunctionPrototypeNotDefinedException(
|
||||
$"函数 ({parsed.Name}) 没有定义的原型.");
|
||||
}
|
||||
|
||||
parseEndIndex = parsed.EndIndex;
|
||||
@@ -35,18 +38,18 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
public static ParsedFunctionCall ParseFunctionCall(FunctionPrototypeCollection prototypes, string functionCall)
|
||||
{
|
||||
string functionName = string.Empty;
|
||||
int parseEndIndex = 0;
|
||||
var functionName = string.Empty;
|
||||
var 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())
|
||||
if (firstLine == null || firstLine.Count(x => x == '(') != firstLine.Count(x => x == ')'))
|
||||
{
|
||||
throw new WikiFunctionPrototypeSyntaxError($"Function parentheses mismatch.");
|
||||
throw new WikiFunctionPrototypeSyntaxError($"函数括号不匹配.");
|
||||
}
|
||||
|
||||
string functionPrefix = functionCall.Substring(0, 2);
|
||||
var functionPrefix = functionCall.Substring(0, 2);
|
||||
|
||||
var parameterMatches = FunctionCallParser().Matches(firstLine);
|
||||
if (parameterMatches.Count > 0)
|
||||
@@ -57,12 +60,15 @@ namespace ZelWiki.Engine.Function
|
||||
functionName = match.Value[..paramStartIndex].ToLower().TrimStart(['{', '#', '@']).Trim();
|
||||
parseEndIndex = match.Index + match.Length;
|
||||
|
||||
string rawArgTrimmed = match.ToString().Substring(paramStartIndex, (match.ToString().Length - paramStartIndex));
|
||||
string rawArgTrimmed = match.ToString()
|
||||
.Substring(paramStartIndex, (match.ToString().Length - paramStartIndex));
|
||||
rawArguments = ParseRawArguments(rawArgTrimmed);
|
||||
}
|
||||
else //The function call has no parameters.
|
||||
else //函数调用没有参数.
|
||||
{
|
||||
int endOfLine = functionCall.Substring(2).TakeWhile(c => char.IsLetterOrDigit(c)).Count(); //Find the first non-alphanumeric after the function identifier (##, @@, etc).
|
||||
var endOfLine =
|
||||
functionCall.Substring(2).TakeWhile(c => char.IsLetterOrDigit(c))
|
||||
.Count();
|
||||
functionName = functionCall.Substring(2, endOfLine).ToLower().TrimStart(['{', '#', '@']).Trim();
|
||||
parseEndIndex = endOfLine + 2;
|
||||
}
|
||||
@@ -71,12 +77,11 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <exception cref="WikiFunctionPrototypeSyntaxError"></exception>
|
||||
public static List<string> ParseRawArgumentsAddParenthesis(string paramString)
|
||||
{
|
||||
if (paramString.StartsWith('(') || paramString.EndsWith(')'))
|
||||
@@ -88,17 +93,16 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <exception cref="WikiFunctionPrototypeSyntaxError"></exception>
|
||||
public static List<string> ParseRawArguments(string paramString)
|
||||
{
|
||||
List<string> ps = new();
|
||||
|
||||
int readPos = 0;
|
||||
var readPos = 0;
|
||||
|
||||
var singleParam = new StringBuilder();
|
||||
|
||||
@@ -109,7 +113,7 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
int parenNest = 1;
|
||||
|
||||
readPos++; //Skip the (
|
||||
readPos++;
|
||||
|
||||
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
|
||||
|
||||
@@ -130,7 +134,7 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == ')' && parenNest == 0)
|
||||
{
|
||||
readPos++; //Skip the )
|
||||
readPos++;
|
||||
|
||||
if (parenNest == 0 && readPos != paramString.Length)
|
||||
{
|
||||
@@ -141,6 +145,7 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
ps.Add(singleParam.ToString());
|
||||
}
|
||||
|
||||
singleParam.Clear();
|
||||
|
||||
if (parenNest == 0)
|
||||
@@ -150,10 +155,10 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == '\"')
|
||||
{
|
||||
readPos++; //Skip the ".
|
||||
readPos++;
|
||||
|
||||
bool escapeChar = false;
|
||||
for (; ; readPos++)
|
||||
var escapeChar = false;
|
||||
for (;; readPos++)
|
||||
{
|
||||
if (readPos == paramString.Length)
|
||||
{
|
||||
@@ -166,14 +171,14 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == '\"' && escapeChar == false)
|
||||
{
|
||||
//Found the end of the string:
|
||||
readPos++; //Skip the ".
|
||||
readPos++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
singleParam.Append(paramString[readPos]);
|
||||
}
|
||||
|
||||
escapeChar = false;
|
||||
}
|
||||
|
||||
@@ -181,7 +186,7 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == ',')
|
||||
{
|
||||
readPos++; //Skip the ,
|
||||
readPos++;
|
||||
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
|
||||
|
||||
ps.Add(singleParam.ToString());
|
||||
@@ -209,7 +214,7 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ps.Count; i++)
|
||||
for (var i = 0; i < ps.Count; i++)
|
||||
{
|
||||
ps[i] = ps[i].Trim();
|
||||
}
|
||||
@@ -217,4 +222,4 @@ namespace ZelWiki.Engine.Function
|
||||
return ps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,19 @@
|
||||
{
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FunctionPrototype()
|
||||
{
|
||||
Parameters = new List<PrototypeParameter>();
|
||||
FunctionPrefix = string.Empty;
|
||||
ProperName = string.Empty;
|
||||
FunctionName = string.Empty;
|
||||
Parameters = new();
|
||||
}
|
||||
public string FunctionPrefix { get; set; }
|
||||
public string ProperName { get; set; }
|
||||
public string FunctionName { get; set; }
|
||||
public List<PrototypeParameter> Parameters { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ namespace ZelWiki.Engine.Function
|
||||
public WikiFunctionType FunctionTypes { get; private set; }
|
||||
public List<PrototypeSet> Items { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="functionTypes"></param>
|
||||
public FunctionPrototypeCollection(WikiFunctionType functionTypes)
|
||||
{
|
||||
FunctionTypes = functionTypes;
|
||||
@@ -36,40 +40,40 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
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);
|
||||
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;
|
||||
var functionPrototype = Items.FirstOrDefault(o =>
|
||||
(o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName)
|
||||
?.Value;
|
||||
|
||||
return functionPrototype
|
||||
?? throw new WikiFunctionPrototypeNotDefinedException($"Function ({functionName}) does not have a defined prototype.");
|
||||
?? throw new WikiFunctionPrototypeNotDefinedException(
|
||||
$"函数 ({functionName}) 没有定义的原型.");
|
||||
}
|
||||
|
||||
#region Private
|
||||
|
||||
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();
|
||||
var nameStartIndex = prototypeString.TakeWhile(c => char.IsLetterOrDigit(c) == false).Count();
|
||||
var nameEndIndex = prototypeString.IndexOf(':');
|
||||
var properName = prototypeString.Substring(nameStartIndex, nameEndIndex - nameStartIndex).Trim();
|
||||
var functionName = properName.ToLower();
|
||||
var functionPrefix = prototypeString.Substring(0, nameStartIndex).Trim();
|
||||
|
||||
prototypeString = prototypeString.Substring(nameEndIndex + 1).Trim();
|
||||
|
||||
var prototype = new FunctionPrototype() { FunctionPrefix = functionPrefix, ProperName = properName, FunctionName = functionName };
|
||||
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());
|
||||
|
||||
@@ -77,13 +81,13 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
var prototypeSegment = new PrototypeParameter();
|
||||
|
||||
int index = 0;
|
||||
var index = 0;
|
||||
|
||||
if (segment[index] == '<')
|
||||
{
|
||||
index++; //Skip the '<'
|
||||
index++;
|
||||
prototypeSegment.Type = Tok(segment, ref index);
|
||||
index++; //Skip the '>'
|
||||
index++;
|
||||
|
||||
if (prototypeSegment.Type.Contains(':'))
|
||||
{
|
||||
@@ -94,12 +98,14 @@ namespace ZelWiki.Engine.Function
|
||||
prototypeSegment.IsInfinite = true;
|
||||
if (prototype.Parameters.Any(o => o.IsInfinite))
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: cannot contain more than one [infinite] parameter.");
|
||||
throw new Exception(
|
||||
$"函数 [{functionName}], 原型错误: cannot contain more than one [infinite] parameter.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [infinite] got [{splitSeg[1]}].");
|
||||
throw new Exception(
|
||||
$"函数 [{functionName}], 原型错误: expected [infinite] got [{splitSeg[1]}].");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,41 +118,42 @@ namespace ZelWiki.Engine.Function
|
||||
prototypeSegment.IsRequired = true;
|
||||
}
|
||||
|
||||
index++; //Skip the '[' or '{'
|
||||
index++;
|
||||
|
||||
prototypeSegment.Name = Tok(segment, ref index);
|
||||
|
||||
if (index < segment.Length && segment[index] == '(') //Parse allowed values.
|
||||
if (index < segment.Length && segment[index] == '(')
|
||||
{
|
||||
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();
|
||||
prototypeSegment.AllowedValues = roteRequiredValues.Trim().Split(',')
|
||||
.Select(o => o.Trim().ToLower()).ToList();
|
||||
|
||||
index = allowedValueEndIndex;
|
||||
index++; //Skip the ')'
|
||||
index++;
|
||||
SkipWhiteSpace(segment, ref index);
|
||||
}
|
||||
|
||||
index++; //Skip the ']' or '}'
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [{{] or [[].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [{{] or [[].");
|
||||
}
|
||||
|
||||
SkipWhiteSpace(segment, ref index);
|
||||
|
||||
if (index < segment.Length && segment[index] == '=')
|
||||
{
|
||||
index++; //Skip the '='
|
||||
index++;
|
||||
SkipWhiteSpace(segment, ref index);
|
||||
|
||||
if (segment[index] != '\'')
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [\'].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [\'].");
|
||||
}
|
||||
|
||||
index++; //Skip the '''
|
||||
index++;
|
||||
|
||||
prototypeSegment.DefaultValue = segment.Substring(index, (segment.Length - index) - 1);
|
||||
|
||||
@@ -154,13 +161,13 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
if (index < segment.Length && segment[index] != '\'')
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [\'].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [\'].");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [<].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [<].");
|
||||
}
|
||||
|
||||
prototype.Parameters.Add(prototypeSegment);
|
||||
@@ -168,9 +175,8 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
return prototype;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next token in a string.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="index"></param>
|
||||
@@ -203,5 +209,7 @@ namespace ZelWiki.Engine.Function
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,11 @@
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
public NamedParameter(string name, string value)
|
||||
{
|
||||
Name = name;
|
||||
|
||||
@@ -2,27 +2,32 @@
|
||||
{
|
||||
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;
|
||||
|
||||
/// <param name="value"></param>
|
||||
public OrdinalParameter(string value)
|
||||
{
|
||||
Value = value;
|
||||
IsMatched = false;
|
||||
ParameterName = string.Empty;
|
||||
}
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsMatched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ParameterName { get; set; }
|
||||
|
||||
public void AssociateWithPrototypeParam(string paramName)
|
||||
{
|
||||
IsMatched = true;
|
||||
ParameterName = paramName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
{
|
||||
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>();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="endIndex"></param>
|
||||
/// <param name="rawArguments"></param>
|
||||
public ParsedFunctionCall(string prefix, string name, int endIndex, List<string> rawArguments)
|
||||
{
|
||||
Prefix = prefix;
|
||||
@@ -14,5 +16,9 @@
|
||||
EndIndex = endIndex;
|
||||
RawArguments = rawArguments;
|
||||
}
|
||||
public string Prefix { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int EndIndex { get; set; }
|
||||
public List<string> RawArguments { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,24 @@
|
||||
{
|
||||
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();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PrototypeParameter()
|
||||
{
|
||||
Type = string.Empty;
|
||||
Name = string.Empty;
|
||||
DefaultValue = string.Empty;
|
||||
IsRequired = false;
|
||||
IsInfinite = false;
|
||||
AllowedValues = new();
|
||||
}
|
||||
|
||||
public string Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string DefaultValue { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
public bool IsInfinite { get; set; }
|
||||
public List<string> AllowedValues { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,19 @@
|
||||
{
|
||||
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();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PrototypeSet()
|
||||
{
|
||||
FunctionPrefix = string.Empty;
|
||||
ProperName = string.Empty;
|
||||
FunctionName = string.Empty;
|
||||
Value = new();
|
||||
}
|
||||
public string FunctionPrefix { get; set; }
|
||||
public string ProperName { get; set; }
|
||||
public string FunctionName { get; set; }
|
||||
public FunctionPrototype Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
namespace ZelWiki.Engine.Function
|
||||
{
|
||||
[Obsolete("弃用了")]
|
||||
public static class SelfDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't ever look at this. :(
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("弃用了")]
|
||||
public static void CreateNotExisting()
|
||||
{
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user