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>\))|[^()]*)+\)")]
private static partial Regex FunctionCallParser();
///
/// 解析函数调用及其参数,并将其与已定义的函数及其原型进行匹配
///
///
///
///
///
///
public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall,
out int parseEndIndex)
{
var rawArguments = new List();
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();
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);
}
///
///
///
///
///
///
public static List ParseRawArgumentsAddParenthesis(string paramString)
{
if (paramString.StartsWith('(') || paramString.EndsWith(')'))
{
throw new WikiFunctionPrototypeSyntaxError($"Unexpected '(' or ')'.");
}
return ParseRawArguments($"({paramString})");
}
///
///
///
///
///
///
public static List ParseRawArguments(string paramString)
{
List ps = new();
var readPos = 0;
var singleParam = new StringBuilder();
if (paramString[readPos] != '(')
{
throw new WikiFunctionPrototypeSyntaxError($"Expected '('.");
}
var 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;
}
}
}