using ZelWiki.Engine.Function.Exceptions; namespace ZelWiki.Engine.Function { public class FunctionPrototypeCollection { public enum WikiFunctionType { Standard, Scoped, Instruction } public WikiFunctionType FunctionTypes { get; private set; } public List 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(); return Items.Any(o => (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName); } public FunctionPrototype Get(string functionPrefix, string functionName) { functionName = functionName.ToLower(); var functionPrototype = Items.FirstOrDefault(o => (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName) ?.Value; return functionPrototype ?? throw new WikiFunctionPrototypeNotDefinedException( $"函数 ({functionName}) 没有定义的原型."); } #region Private private FunctionPrototype ParsePrototype(string prototypeString) { 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 }; if (prototypeString.Length == 0) return prototype; var segments = prototypeString.Trim().Split('|').Select(o => o.Trim()); foreach (var segment in segments) { var prototypeSegment = new PrototypeParameter(); var index = 0; if (segment[index] == '<') { index++; prototypeSegment.Type = Tok(segment, ref index); index++; 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( $"函数 [{functionName}], 原型错误: cannot contain more than one [infinite] parameter."); } } else { throw new Exception( $"函数 [{functionName}], 原型错误: 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++; prototypeSegment.Name = Tok(segment, ref index); 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(); index = allowedValueEndIndex; index++; SkipWhiteSpace(segment, ref index); } index++; } else { throw new Exception($"函数 [{functionName}], 原型错误: expected [{{] or [[]."); } SkipWhiteSpace(segment, ref index); if (index < segment.Length && segment[index] == '=') { index++; SkipWhiteSpace(segment, ref index); if (segment[index] != '\'') { throw new Exception($"函数 [{functionName}], 原型错误: expected [\']."); } index++; prototypeSegment.DefaultValue = segment.Substring(index, (segment.Length - index) - 1); index = segment.Length - 1; if (index < segment.Length && segment[index] != '\'') { throw new Exception($"函数 [{functionName}], 原型错误: expected [\']."); } } } else { throw new Exception($"函数 [{functionName}], 原型错误: expected [<]."); } prototype.Parameters.Add(prototypeSegment); } return prototype; } /// /// /// /// /// /// 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++; } } #endregion } }