225 lines
7.9 KiB
C#
225 lines
7.9 KiB
C#
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using ZelWiki.Engine.Function.Exceptions;
|
|
|
|
namespace ZelWiki.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>
|
|
/// 解析函数调用及其参数,并将其与已定义的函数及其原型进行匹配
|
|
/// </summary>
|
|
/// <param name="prototypes"></param>
|
|
/// <param name="functionCall"></param>
|
|
/// <param name="parseEndIndex"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="WikiFunctionPrototypeNotDefinedException"></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(
|
|
$"函数 ({parsed.Name}) 没有定义的原型.");
|
|
}
|
|
|
|
parseEndIndex = parsed.EndIndex;
|
|
|
|
return new FunctionCall(prototype, parsed.RawArguments);
|
|
}
|
|
|
|
public static ParsedFunctionCall ParseFunctionCall(FunctionPrototypeCollection prototypes, string functionCall)
|
|
{
|
|
var functionName = string.Empty;
|
|
var parseEndIndex = 0;
|
|
var rawArguments = new List<string>();
|
|
|
|
var firstLine = functionCall.Split('\n')?.FirstOrDefault();
|
|
|
|
if (firstLine == null || firstLine.Count(x => x == '(') != firstLine.Count(x => x == ')'))
|
|
{
|
|
throw new WikiFunctionPrototypeSyntaxError($"函数括号不匹配.");
|
|
}
|
|
|
|
var 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 //函数调用没有参数.
|
|
{
|
|
var endOfLine =
|
|
functionCall.Substring(2).TakeWhile(c => char.IsLetterOrDigit(c))
|
|
.Count();
|
|
functionName = functionCall.Substring(2, endOfLine).ToLower().TrimStart(['{', '#', '@']).Trim();
|
|
parseEndIndex = endOfLine + 2;
|
|
}
|
|
|
|
return new ParsedFunctionCall(functionPrefix, functionName, parseEndIndex, rawArguments);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="paramString"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="WikiFunctionPrototypeSyntaxError"></exception>
|
|
public static List<string> ParseRawArgumentsAddParenthesis(string paramString)
|
|
{
|
|
if (paramString.StartsWith('(') || paramString.EndsWith(')'))
|
|
{
|
|
throw new WikiFunctionPrototypeSyntaxError($"Unexpected '(' or ')'.");
|
|
}
|
|
|
|
return ParseRawArguments($"({paramString})");
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="paramString"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="WikiFunctionPrototypeSyntaxError"></exception>
|
|
public static List<string> ParseRawArguments(string paramString)
|
|
{
|
|
List<string> ps = new();
|
|
|
|
var readPos = 0;
|
|
|
|
var singleParam = new StringBuilder();
|
|
|
|
if (paramString[readPos] != '(')
|
|
{
|
|
throw new WikiFunctionPrototypeSyntaxError($"Expected '('.");
|
|
}
|
|
|
|
int parenNest = 1;
|
|
|
|
readPos++;
|
|
|
|
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++;
|
|
|
|
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++;
|
|
|
|
var 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)
|
|
{
|
|
readPos++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
singleParam.Append(paramString[readPos]);
|
|
}
|
|
|
|
escapeChar = false;
|
|
}
|
|
|
|
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
|
|
}
|
|
else if (paramString[readPos] == ',')
|
|
{
|
|
readPos++;
|
|
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 (var i = 0; i < ps.Count; i++)
|
|
{
|
|
ps[i] = ps[i].Trim();
|
|
}
|
|
|
|
return ps;
|
|
}
|
|
}
|
|
} |