public IEnumerator <Parselet> Parse() { if (!parseletNames.Any()) { throw new RantInternalException($"{GetType().Name}.Parse(): Parselet name stack is empty."); } if (!tokens.Any()) { throw new RantInternalException($"{GetType().Name}.Parse(): Token stack is empty."); } if (!outputDelegates.Any()) { throw new RantInternalException($"{GetType().Name}.Parse(): Output delegate stack is empty."); } var name = parseletNames.Pop(); var token = tokens.Pop(); TokenParserDelegate tokenParser = null; if (!tokenParserMethods.TryGetValue(name, out tokenParser)) { if (defaultParserMethod == null) { throw new RantInternalException($"{GetType().Name}.Parse(): No valid implementation exists for R.{token.ID}."); } tokenParser = defaultParserMethod; } foreach (var parselet in tokenParser(token)) { yield return(parselet); } outputDelegates.Pop(); }
protected Parselet() { tokenParserMethods = new Dictionary <string, TokenParserDelegate>(); parseletNames = new Stack <string>(); tokens = new Stack <Token <R> >(); outputDelegates = new Stack <Action <RantAction> >(); // it doesn't matter if the methods are private, we can still call them because reflection #if UNITY var methods = from method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) let attrib = method.GetCustomAttributes(typeof(TokenParserAttribute), true).Cast <TokenParserAttribute>().FirstOrDefault() let defaultAttrib = method.GetCustomAttributes(typeof(DefaultParserAttribute), true).Cast <DefaultParserAttribute>().FirstOrDefault() where attrib != null || defaultAttrib != null select new { Method = method, Attrib = attrib, IsDefault = defaultAttrib != null }; #else var methods = from method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) let attrib = method.GetCustomAttribute <TokenParserAttribute>() let defaultAttrib = method.GetCustomAttribute <DefaultParserAttribute>() where attrib != null || defaultAttrib != null select new { Method = method, Attrib = attrib, IsDefault = defaultAttrib != null }; #endif if (!methods.Any()) { throw new RantInternalException($"{GetType().Name}.ctor: No parselet implementations found."); } foreach (var method in methods) { if (method.Method.IsStatic) { throw new RantInternalException($"{GetType().Name}.ctor: Parselet method '{method.Method.Name}' musn't be static."); } if (method.Method.IsGenericMethod) { throw new RantInternalException($"{GetType().Name}.ctor: Parselet method '{method.Method.Name}' musn't be generic:."); } if (method.Method.GetParameters().Length != 1) { throw new RantInternalException($"{GetType().Name}.ctor: Invalid amount of parameters for parselet method '{method.Method.Name}'."); } if (method.Method.GetParameters().First().ParameterType != typeof(Token <R>)) { throw new RantInternalException($"{GetType().Name}.ctor: Wrong parameter type for parselet method '{method.Method.Name}'."); } if (method.IsDefault && method.Attrib != null) { throw new RantInternalException($"{GetType().Name}.ctor: Parselet method '{method.Method.Name}' has both TokenParser and DefaultParser attributes."); } var parseletName = method.Method.Name; // this could be a default method so it may not have the TokenParser attribute if (method.Attrib != null && !Util.IsNullOrWhiteSpace(method.Attrib.Name)) { parseletName = method.Attrib.Name; } if (method.IsDefault) { if (defaultParserMethod != null) { throw new RantInternalException( $"{GetType().Name}.ctor: Default parser method already defined: '{defaultParserMethod.Method.Name}'. " + $"Cannot define '{method.Method.Name}' as default parser method."); } // associate our default method with us parseletNameDict.Add(parseletName, this); defaultParserMethod = Delegate.CreateDelegate(typeof(TokenParserDelegate), this, method.Method) as TokenParserDelegate; continue; } Parselet existingParselet; if (parseletNameDict.TryGetValue(parseletName, out existingParselet)) { throw new RantInternalException($"{GetType().Name}.ctor: '{existingParselet.GetType().Name}' already has an implementation called '{parseletName}'."); } // associate our method with us parseletNameDict.Add(parseletName, this); tokenParserMethods.Add(parseletName, Delegate.CreateDelegate(typeof(TokenParserDelegate), this, method.Method) as TokenParserDelegate); if (method.Attrib.TokenType != null) { var type = method.Attrib.TokenType.Value; var existingName = ""; if (tokenTypeParseletNameDict.TryGetValue(type, out existingName)) { throw new RantInternalException($"{GetType().Name}.ctor: '{existingName}' is already associated with R.{type}."); } // associate the method's name with the token type tokenTypeParseletNameDict.Add(type, parseletName); } } }
protected Parselet() { tokenParserMethods = new Dictionary<string, TokenParserDelegate>(); parseletNames = new Stack<string>(); tokens = new Stack<Token<R>>(); outputDelegates = new Stack<Action<RantAction>>(); // it doesn't matter if the methods are private, we can still call them because reflection #if UNITY var methods = from method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) let attrib = method.GetCustomAttributes(typeof(TokenParserAttribute), true).Cast<TokenParserAttribute>().FirstOrDefault() let defaultAttrib = method.GetCustomAttributes(typeof(DefaultParserAttribute), true).Cast<DefaultParserAttribute>().FirstOrDefault() where attrib != null || defaultAttrib != null select new { Method = method, Attrib = attrib, IsDefault = defaultAttrib != null }; #else var methods = from method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) let attrib = method.GetCustomAttribute<TokenParserAttribute>() let defaultAttrib = method.GetCustomAttribute<DefaultParserAttribute>() where attrib != null || defaultAttrib != null select new { Method = method, Attrib = attrib, IsDefault = defaultAttrib != null }; #endif if (!methods.Any()) throw new RantInternalException($"{GetType().Name}.ctor: No parselet implementations found."); foreach (var method in methods) { if (method.Method.IsStatic) throw new RantInternalException($"{GetType().Name}.ctor: Parselet method '{method.Method.Name}' musn't be static."); if (method.Method.IsGenericMethod) throw new RantInternalException($"{GetType().Name}.ctor: Parselet method '{method.Method.Name}' musn't be generic:."); if (method.Method.GetParameters().Length != 1) throw new RantInternalException($"{GetType().Name}.ctor: Invalid amount of parameters for parselet method '{method.Method.Name}'."); if (method.Method.GetParameters().First().ParameterType != typeof(Token<R>)) throw new RantInternalException($"{GetType().Name}.ctor: Wrong parameter type for parselet method '{method.Method.Name}'."); if (method.IsDefault && method.Attrib != null) throw new RantInternalException($"{GetType().Name}.ctor: Parselet method '{method.Method.Name}' has both TokenParser and DefaultParser attributes."); var parseletName = method.Method.Name; // this could be a default method so it may not have the TokenParser attribute if (method.Attrib != null && !Util.IsNullOrWhiteSpace(method.Attrib.Name)) parseletName = method.Attrib.Name; if (method.IsDefault) { if (defaultParserMethod != null) throw new RantInternalException( $"{GetType().Name}.ctor: Default parser method already defined: '{defaultParserMethod.Method.Name}'. " + $"Cannot define '{method.Method.Name}' as default parser method."); // associate our default method with us parseletNameDict.Add(parseletName, this); defaultParserMethod = Delegate.CreateDelegate(typeof(TokenParserDelegate), this, method.Method) as TokenParserDelegate; continue; } Parselet existingParselet; if (parseletNameDict.TryGetValue(parseletName, out existingParselet)) throw new RantInternalException($"{GetType().Name}.ctor: '{existingParselet.GetType().Name}' already has an implementation called '{parseletName}'."); // associate our method with us parseletNameDict.Add(parseletName, this); tokenParserMethods.Add(parseletName, Delegate.CreateDelegate(typeof(TokenParserDelegate), this, method.Method) as TokenParserDelegate); if (method.Attrib.TokenType != null) { var type = method.Attrib.TokenType.Value; var existingName = ""; if (tokenTypeParseletNameDict.TryGetValue(type, out existingName)) throw new RantInternalException($"{GetType().Name}.ctor: '{existingName}' is already associated with R.{type}."); // associate the method's name with the token type tokenTypeParseletNameDict.Add(type, parseletName); } } }