2842 lines
		
	
	
		
			115 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			2842 lines
		
	
	
		
			115 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using Google.Protobuf.Reflection;
							 | 
						|||
| 
								 | 
							
								using ProtoBuf;
							 | 
						|||
| 
								 | 
							
								using ProtoBuf.Reflection;
							 | 
						|||
| 
								 | 
							
								using System;
							 | 
						|||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						|||
| 
								 | 
							
								using System.Globalization;
							 | 
						|||
| 
								 | 
							
								using System.IO;
							 | 
						|||
| 
								 | 
							
								using System.Linq;
							 | 
						|||
| 
								 | 
							
								using System.Text;
							 | 
						|||
| 
								 | 
							
								using System.Text.RegularExpressions;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace Google.Protobuf.Reflection
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								#pragma warning disable CS1591
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    interface IType
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        IType Parent { get; }
							 | 
						|||
| 
								 | 
							
								        string FullyQualifiedName { get; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        IType Find(string name);
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class FileDescriptorSet
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal const string Namespace = ".google.protobuf.";
							 | 
						|||
| 
								 | 
							
								        public Func<string, bool> ImportValidator { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal List<string> importPaths = new List<string>();
							 | 
						|||
| 
								 | 
							
								        public void AddImportPath(string path)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            importPaths.Add(path);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public Error[] GetErrors() => Error.GetArray(Errors);
							 | 
						|||
| 
								 | 
							
								        internal List<Error> Errors { get; } = new List<Error>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public bool Add(string name, bool includeInOutput, TextReader source = null)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (string.IsNullOrWhiteSpace(name))
							 | 
						|||
| 
								 | 
							
								                throw new ArgumentNullException(nameof(name));
							 | 
						|||
| 
								 | 
							
								            if (Path.IsPathRooted(name) || name.Contains(".."))
							 | 
						|||
| 
								 | 
							
								                throw new ArgumentException("Paths should be relative to the import paths, not rooted", nameof(name));
							 | 
						|||
| 
								 | 
							
								            FileDescriptorProto descriptor;
							 | 
						|||
| 
								 | 
							
								            if (TryResolve(name, out descriptor))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (includeInOutput) descriptor.IncludeInOutput = true;
							 | 
						|||
| 
								 | 
							
								                return true; // already exists, that counts as success
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            using (var reader = source ?? Open(name))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (reader == null) return false; // not found
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                descriptor = new FileDescriptorProto
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    Name = name,
							 | 
						|||
| 
								 | 
							
								                    IncludeInOutput = includeInOutput
							 | 
						|||
| 
								 | 
							
								                };
							 | 
						|||
| 
								 | 
							
								                Files.Add(descriptor);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                descriptor.Parse(reader, Errors, name);
							 | 
						|||
| 
								 | 
							
								                return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private TextReader Open(string name)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var found = FindFile(name);
							 | 
						|||
| 
								 | 
							
								            if (found == null) return null;
							 | 
						|||
| 
								 | 
							
								            return File.OpenText(found);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        string FindFile(string file)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            foreach (var path in importPaths)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var rel = Path.Combine(path, file);
							 | 
						|||
| 
								 | 
							
								                if (File.Exists(rel)) return rel;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return null;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        bool TryResolve(string name, out FileDescriptorProto descriptor)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            descriptor = Files.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
							 | 
						|||
| 
								 | 
							
								            return descriptor != null;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ApplyImports()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            bool didSomething;
							 | 
						|||
| 
								 | 
							
								            do
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                didSomething = false;
							 | 
						|||
| 
								 | 
							
								                var file = Files.FirstOrDefault(x => x.HasPendingImports);
							 | 
						|||
| 
								 | 
							
								                if (file != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    // note that GetImports clears the flag
							 | 
						|||
| 
								 | 
							
								                    foreach (var import in file.GetImports(true))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        if (!(ImportValidator?.Invoke(import.Path) ?? true))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(import.Token, $"import of {import.Path} is disallowed");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else if (Add(import.Path, false))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            didSomething = true;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(import.Token, $"unable to find: '{import.Path}'");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            } while (didSomething);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void Process()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ApplyImports();
							 | 
						|||
| 
								 | 
							
								            foreach (var file in Files)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                using (var ctx = new ParserContext(file, null, Errors))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    file.BuildTypeHierarchy(this, ctx);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var file in Files)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                using (var ctx = new ParserContext(file, null, Errors))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    file.ResolveTypes(ctx, false);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var file in Files)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                using (var ctx = new ParserContext(file, null, Errors))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    file.ResolveTypes(ctx, true);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public T Serialize<T>(Func<FileDescriptorSet,object,T> customSerializer, bool includeImports, object state = null)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            T result;
							 | 
						|||
| 
								 | 
							
								            if (includeImports || Files.All(x => x.IncludeInOutput))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                result = customSerializer(this, state);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var snapshort = Files.ToArray();
							 | 
						|||
| 
								 | 
							
								                Files.RemoveAll(x => !x.IncludeInOutput);
							 | 
						|||
| 
								 | 
							
								                result = customSerializer(this, state);
							 | 
						|||
| 
								 | 
							
								                Files.Clear();
							 | 
						|||
| 
								 | 
							
								                Files.AddRange(snapshort);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return result;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void Serialize(Stream destination, bool includeImports)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            Serialize((s,o) => { Serializer.Serialize((Stream)o, s); return true; }, includeImports, destination);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal FileDescriptorProto GetFile(string path)
							 | 
						|||
| 
								 | 
							
								            // try full match first, then name-only match
							 | 
						|||
| 
								 | 
							
								            => Files.FirstOrDefault(x => string.Equals(x.Name, path, StringComparison.OrdinalIgnoreCase));
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class DescriptorProto : ISchemaObject, IType, IMessage
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public static byte[] GetExtensionData(IExtensible obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var ext = obj?.GetExtensionObject(false);
							 | 
						|||
| 
								 | 
							
								            int len;
							 | 
						|||
| 
								 | 
							
								            if (ext == null || (len = ext.GetLength()) == 0) return null;
							 | 
						|||
| 
								 | 
							
								            var s = ext.BeginQuery();
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (s is MemoryStream) return ((MemoryStream)s).ToArray();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                byte[] buffer = new byte[len];
							 | 
						|||
| 
								 | 
							
								                int offset = 0, read;
							 | 
						|||
| 
								 | 
							
								                while ((read = s.Read(buffer, offset, len)) > 0)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    offset += read;
							 | 
						|||
| 
								 | 
							
								                    len -= read;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                if (len != 0) throw new EndOfStreamException();
							 | 
						|||
| 
								 | 
							
								                return buffer;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            finally
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ext.EndQuery(s);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public static void SetExtensionData(IExtensible obj, byte[] data)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (obj == null || data == null || data.Length == 0) return;
							 | 
						|||
| 
								 | 
							
								            var ext = obj.GetExtensionObject(true);
							 | 
						|||
| 
								 | 
							
								            (ext as IExtensionResettable)?.Reset();
							 | 
						|||
| 
								 | 
							
								            var s = ext.BeginAppend();
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                s.Write(data, 0, data.Length);
							 | 
						|||
| 
								 | 
							
								                ext.EndAppend(s, true);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ext.EndAppend(s, false);
							 | 
						|||
| 
								 | 
							
								                throw;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Name;
							 | 
						|||
| 
								 | 
							
								        internal IType Parent { get; set; }
							 | 
						|||
| 
								 | 
							
								        IType IType.Parent => Parent;
							 | 
						|||
| 
								 | 
							
								        string IType.FullyQualifiedName => FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								        IType IType.Find(string name)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            return (IType)NestedTypes.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase))
							 | 
						|||
| 
								 | 
							
								                ?? (IType)EnumTypes.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal string FullyQualifiedName { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        List<DescriptorProto> IMessage.Types => NestedTypes;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal int MaxField => (Options?.MessageSetWireFormat == true) ? int.MaxValue : FieldDescriptorProto.DefaultMaxField;
							 | 
						|||
| 
								 | 
							
								        int IMessage.MaxField => MaxField;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static bool TryParse(ParserContext ctx, IHazNames parent, out DescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = ctx.Tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            ctx.CheckNames(parent, name, ctx.Tokens.Previous);
							 | 
						|||
| 
								 | 
							
								            if (ctx.TryReadObject(out obj))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                obj.Name = name;
							 | 
						|||
| 
								 | 
							
								                return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.AlphaNumeric, "message"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                DescriptorProto obj;
							 | 
						|||
| 
								 | 
							
								                if (DescriptorProto.TryParse(ctx, this, out obj))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    NestedTypes.Add(obj);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "enum"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                EnumDescriptorProto obj;
							 | 
						|||
| 
								 | 
							
								                if (EnumDescriptorProto.TryParse(ctx, this, out obj))
							 | 
						|||
| 
								 | 
							
								                    EnumTypes.Add(obj);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "option"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Options = ctx.ParseOptionStatement(Options, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "reserved"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ParseReservedRanges(ctx);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "extensions"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ParseExtensionRange(ctx);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "extend"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                FieldDescriptorProto.ParseExtensions(ctx, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "oneof"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                OneofDescriptorProto.Parse(ctx, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "map"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ParseMap(ctx);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                FieldDescriptorProto obj;
							 | 
						|||
| 
								 | 
							
								                if (FieldDescriptorProto.TryParse(ctx, this, false, out obj))
							 | 
						|||
| 
								 | 
							
								                    Fields.Add(obj);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ParseMap(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, "<");
							 | 
						|||
| 
								 | 
							
								            var keyName = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            var keyToken = tokens.Previous;
							 | 
						|||
| 
								 | 
							
								            FieldDescriptorProto.Type keyType;
							 | 
						|||
| 
								 | 
							
								            if (FieldDescriptorProto.TryIdentifyType(keyName, out keyType))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                keyName = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            switch (keyType)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case 0:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeBytes:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeMessage:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeGroup:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeFloat:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeDouble:
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(tokens.Previous, "invalid map key type (only integral and string types are allowed)");
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, ",");
							 | 
						|||
| 
								 | 
							
								            var valueName = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            var valueToken = tokens.Previous;
							 | 
						|||
| 
								 | 
							
								            FieldDescriptorProto.Type valueType;
							 | 
						|||
| 
								 | 
							
								            if (FieldDescriptorProto.TryIdentifyType(valueName, out valueType))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                valueName = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, ">");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var name = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            var nameToken = tokens.Previous;
							 | 
						|||
| 
								 | 
							
								            ctx.CheckNames(this, name, nameToken);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, "=");
							 | 
						|||
| 
								 | 
							
								            int number = tokens.ConsumeInt32();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var jsonName = FieldDescriptorProto.GetJsonName(name);
							 | 
						|||
| 
								 | 
							
								            var typeName = jsonName.Substring(0, 1).ToUpperInvariant() + jsonName.Substring(1) + "Entry";
							 | 
						|||
| 
								 | 
							
								            ctx.CheckNames(this, typeName, nameToken);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var field = new FieldDescriptorProto
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                type = FieldDescriptorProto.Type.TypeMessage,
							 | 
						|||
| 
								 | 
							
								                TypeName = typeName,
							 | 
						|||
| 
								 | 
							
								                Name = name,
							 | 
						|||
| 
								 | 
							
								                JsonName = jsonName,
							 | 
						|||
| 
								 | 
							
								                Number = number,
							 | 
						|||
| 
								 | 
							
								                label = FieldDescriptorProto.Label.LabelRepeated,
							 | 
						|||
| 
								 | 
							
								                TypeToken = nameToken
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.Symbol, "["))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                field.Options = ctx.ParseOptionBlock(field.Options, field);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            Fields.Add(field);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var msgType = new DescriptorProto
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Name = typeName,
							 | 
						|||
| 
								 | 
							
								                Fields =
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    new FieldDescriptorProto
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        label = FieldDescriptorProto.Label.LabelOptional,
							 | 
						|||
| 
								 | 
							
								                        Name = "key",
							 | 
						|||
| 
								 | 
							
								                        JsonName = "key",
							 | 
						|||
| 
								 | 
							
								                        Number = 1,
							 | 
						|||
| 
								 | 
							
								                        type = keyType,
							 | 
						|||
| 
								 | 
							
								                        TypeName = keyName,
							 | 
						|||
| 
								 | 
							
								                        TypeToken = keyToken,
							 | 
						|||
| 
								 | 
							
								                    },
							 | 
						|||
| 
								 | 
							
								                    new FieldDescriptorProto
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        label = FieldDescriptorProto.Label.LabelOptional,
							 | 
						|||
| 
								 | 
							
								                        Name = "value",
							 | 
						|||
| 
								 | 
							
								                        JsonName = "value",
							 | 
						|||
| 
								 | 
							
								                        Number = 2,
							 | 
						|||
| 
								 | 
							
								                        type = valueType,
							 | 
						|||
| 
								 | 
							
								                        TypeName = valueName,
							 | 
						|||
| 
								 | 
							
								                        TypeToken = valueToken,
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								            if (msgType.Options == null) msgType.Options = new MessageOptions();
							 | 
						|||
| 
								 | 
							
								            msgType.Options.MapEntry = true;
							 | 
						|||
| 
								 | 
							
								            NestedTypes.Add(msgType);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ParseExtensionRange(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            tokens.Previous.RequireProto2(ctx);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            while (true)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                int from = tokens.ConsumeInt32(MaxField), to = from;
							 | 
						|||
| 
								 | 
							
								                if (tokens.Read().Is(TokenType.AlphaNumeric, "to"))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    tokens.Consume();
							 | 
						|||
| 
								 | 
							
								                    to = tokens.ConsumeInt32(MaxField);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                // the end is off by one
							 | 
						|||
| 
								 | 
							
								                if (to != int.MaxValue) to++;
							 | 
						|||
| 
								 | 
							
								                ExtensionRanges.Add(new ExtensionRange { Start = from, End = to });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if (tokens.ConsumeIf(TokenType.Symbol, ","))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    tokens.Consume();
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else if (tokens.ConsumeIf(TokenType.Symbol, ";"))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    tokens.Read().Throw("unable to parse extension range");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ParseReservedRanges(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            var token = tokens.Read(); // test the first one to determine what we're doing
							 | 
						|||
| 
								 | 
							
								            switch (token.Type)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case TokenType.StringLiteral:
							 | 
						|||
| 
								 | 
							
								                    while (true)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        var name = tokens.Consume(TokenType.StringLiteral);
							 | 
						|||
| 
								 | 
							
								                        var conflict = Fields.FirstOrDefault(x => x.Name == name);
							 | 
						|||
| 
								 | 
							
								                        if (conflict != null)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ctx.Errors.Error(tokens.Previous, $"'{conflict.Name}' is already in use by field {conflict.Number}");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        ReservedNames.Add(name);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (tokens.ConsumeIf(TokenType.Symbol, ","))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else if (tokens.ConsumeIf(TokenType.Symbol, ";"))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            tokens.Read().Throw("unable to parse reserved range");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case TokenType.AlphaNumeric:
							 | 
						|||
| 
								 | 
							
								                    while (true)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        int from = tokens.ConsumeInt32(), to = from;
							 | 
						|||
| 
								 | 
							
								                        if (tokens.Read().Is(TokenType.AlphaNumeric, "to"))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            tokens.Consume();
							 | 
						|||
| 
								 | 
							
								                            to = tokens.ConsumeInt32();
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        var conflict = Fields.FirstOrDefault(x => x.Number >= from && x.Number <= to);
							 | 
						|||
| 
								 | 
							
								                        if (conflict != null)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ctx.Errors.Error(tokens.Previous, $"field {conflict.Number} is already in use by '{conflict.Name}'");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        ReservedRanges.Add(new ReservedRange { Start = from, End = to + 1 });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        token = tokens.Read();
							 | 
						|||
| 
								 | 
							
								                        if (token.Is(TokenType.Symbol, ","))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            tokens.Consume();
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else if (token.Is(TokenType.Symbol, ";"))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            tokens.Consume();
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            token.Throw();
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    throw token.Throw();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        IEnumerable<string> IHazNames.GetNames()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            foreach (var field in Fields) yield return field.Name;
							 | 
						|||
| 
								 | 
							
								            foreach (var type in NestedTypes) yield return type.Name;
							 | 
						|||
| 
								 | 
							
								            foreach (var type in EnumTypes) yield return type.Name;
							 | 
						|||
| 
								 | 
							
								            foreach (var name in ReservedNames) yield return name;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    partial class OneofDescriptorProto : ISchemaObject
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal DescriptorProto Parent { get; set; }
							 | 
						|||
| 
								 | 
							
								        internal static void Parse(ParserContext ctx, DescriptorProto parent)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Object;
							 | 
						|||
| 
								 | 
							
								            var oneOf = new OneofDescriptorProto
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Name = ctx.Tokens.Consume(TokenType.AlphaNumeric)
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								            parent.OneofDecls.Add(oneOf);
							 | 
						|||
| 
								 | 
							
								            oneOf.Parent = parent;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (ctx.TryReadObjectImpl(oneOf))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.AlphaNumeric, "option"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Options = ctx.ParseOptionStatement(Options, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                FieldDescriptorProto field;
							 | 
						|||
| 
								 | 
							
								                if (FieldDescriptorProto.TryParse(ctx, Parent, true, out field))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    field.OneofIndex = Parent.OneofDecls.Count() - 1;
							 | 
						|||
| 
								 | 
							
								                    Parent.Fields.Add(field);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class OneofOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(OneofOptions);
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.Deprecated { get { return false; } set { } }
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key) => false;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class FileDescriptorProto : ISchemaObject, IMessage, IType
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal static FileDescriptorProto GetFile(IType type)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            while (type != null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (type is FileDescriptorProto) return (FileDescriptorProto)type;
							 | 
						|||
| 
								 | 
							
								                type = type.Parent;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return null;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        int IMessage.MaxField => FieldDescriptorProto.DefaultMaxField;
							 | 
						|||
| 
								 | 
							
								        List<FieldDescriptorProto> IMessage.Fields => null;
							 | 
						|||
| 
								 | 
							
								        List<FieldDescriptorProto> IMessage.Extensions => Extensions;
							 | 
						|||
| 
								 | 
							
								        List<DescriptorProto> IMessage.Types => MessageTypes;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Name;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        string IType.FullyQualifiedName => null;
							 | 
						|||
| 
								 | 
							
								        IType IType.Parent => null;
							 | 
						|||
| 
								 | 
							
								        IType IType.Find(string name)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            return (IType)MessageTypes.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase))
							 | 
						|||
| 
								 | 
							
								                ?? (IType)EnumTypes.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal bool HasPendingImports { get; private set; }
							 | 
						|||
| 
								 | 
							
								        internal FileDescriptorSet Parent { get; private set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal bool IncludeInOutput { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public bool HasImports() => _imports.Count != 0;
							 | 
						|||
| 
								 | 
							
								        internal IEnumerable<Import> GetImports(bool resetPendingFlag = false)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (resetPendingFlag)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                HasPendingImports = false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return _imports;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        readonly List<Import> _imports = new List<Import>();
							 | 
						|||
| 
								 | 
							
								        internal bool AddImport(string path, bool isPublic, Token token)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var existing = _imports.FirstOrDefault(x => string.Equals(x.Path, path, StringComparison.OrdinalIgnoreCase));
							 | 
						|||
| 
								 | 
							
								            if (existing != null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // we'll allow this to upgrade
							 | 
						|||
| 
								 | 
							
								                if (isPublic) existing.IsPublic = true;
							 | 
						|||
| 
								 | 
							
								                return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            HasPendingImports = true;
							 | 
						|||
| 
								 | 
							
								            _imports.Add(new Import { Path = path, IsPublic = isPublic, Token = token });
							 | 
						|||
| 
								 | 
							
								            return true;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal const string SyntaxProto2 = "proto2", SyntaxProto3 = "proto3";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            Token token;
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.AlphaNumeric, "message"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                DescriptorProto obj;
							 | 
						|||
| 
								 | 
							
								                if (DescriptorProto.TryParse(ctx, this, out obj))
							 | 
						|||
| 
								 | 
							
								                    MessageTypes.Add(obj);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "enum"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                EnumDescriptorProto obj;
							 | 
						|||
| 
								 | 
							
								                if (EnumDescriptorProto.TryParse(ctx, this, out obj))
							 | 
						|||
| 
								 | 
							
								                    EnumTypes.Add(obj);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "extend"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                FieldDescriptorProto.ParseExtensions(ctx, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "service"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ServiceDescriptorProto obj;
							 | 
						|||
| 
								 | 
							
								                if (ServiceDescriptorProto.TryParse(ctx, out obj))
							 | 
						|||
| 
								 | 
							
								                    Services.Add(obj);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "import"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								                bool isPublic = tokens.ConsumeIf(TokenType.AlphaNumeric, "public");
							 | 
						|||
| 
								 | 
							
								                string path = tokens.Consume(TokenType.StringLiteral);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if (!AddImport(path, isPublic, tokens.Previous))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Warn(tokens.Previous, $"duplicate import: '{path}'");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, ";");
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "syntax"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								                if (MessageTypes.Any() || EnumTypes.Any() || Extensions.Any())
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(tokens.Previous, "syntax must be set before types are defined");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, "=");
							 | 
						|||
| 
								 | 
							
								                Syntax = tokens.Consume(TokenType.StringLiteral);
							 | 
						|||
| 
								 | 
							
								                switch (Syntax)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case SyntaxProto2:
							 | 
						|||
| 
								 | 
							
								                    case SyntaxProto3:
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    default:
							 | 
						|||
| 
								 | 
							
								                        ctx.Errors.Error(tokens.Previous, $"unknown syntax '{Syntax}'");
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, ";");
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "package"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								                Package = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "option"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Options = ctx.ParseOptionStatement(Options, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.Peek(out token))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                token.Throw();
							 | 
						|||
| 
								 | 
							
								            } // else EOF
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void Parse(TextReader schema, List<Error> errors, string file)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            Syntax = "";
							 | 
						|||
| 
								 | 
							
								            using (var ctx = new ParserContext(this, new Peekable<Token>(schema.Tokenize(file).RemoveCommentsAndWhitespace()), errors))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								                Token startOfFile;
							 | 
						|||
| 
								 | 
							
								                tokens.Peek(out startOfFile); // want this for "stuff you didn't do" warnings
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                // read the file into the object
							 | 
						|||
| 
								 | 
							
								                ctx.Fill(this);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                // finish up
							 | 
						|||
| 
								 | 
							
								                if (string.IsNullOrWhiteSpace(Syntax))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Warn(startOfFile, "no syntax specified; it is strongly recommended to specify 'syntax=\"proto2\";' or 'syntax=\"proto3\";'");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                if (Syntax == "" || Syntax == SyntaxProto2)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    Syntax = null; // for output compatibility; is blank even if set to proto2 explicitly
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal bool TryResolveEnum(string typeName, IType parent, out EnumDescriptorProto @enum, bool allowImports, bool treatAllAsPublic = false)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            IType type;
							 | 
						|||
| 
								 | 
							
								            if (TryResolveType(typeName, parent, out type, allowImports, true, treatAllAsPublic))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                @enum = type as EnumDescriptorProto;
							 | 
						|||
| 
								 | 
							
								                return @enum != null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            @enum = null;
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal bool TryResolveMessage(string typeName, IType parent, out DescriptorProto message, bool allowImports, bool treatAllAsPublic = false)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            IType type;
							 | 
						|||
| 
								 | 
							
								            if (TryResolveType(typeName, parent, out type, allowImports, true, treatAllAsPublic))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                message = type as DescriptorProto;
							 | 
						|||
| 
								 | 
							
								                return message != null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            message = null;
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal static bool TrySplit(string input, out string left, out string right)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var split = input.IndexOf('.');
							 | 
						|||
| 
								 | 
							
								            if (split < 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                left = right = null;
							 | 
						|||
| 
								 | 
							
								                return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            left = input.Substring(0, split).Trim();
							 | 
						|||
| 
								 | 
							
								            right = input.Substring(split + 1).Trim();
							 | 
						|||
| 
								 | 
							
								            return true;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal static bool TrySplitLast(string input, out string left, out string right)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var split = input.LastIndexOf('.');
							 | 
						|||
| 
								 | 
							
								            if (split < 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                left = right = null;
							 | 
						|||
| 
								 | 
							
								                return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            left = input.Substring(0, split).Trim();
							 | 
						|||
| 
								 | 
							
								            right = input.Substring(split + 1).Trim();
							 | 
						|||
| 
								 | 
							
								            return true;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
										bool TryResolveFromFile(FileDescriptorProto file, string ee, string ion, out FieldDescriptorProto fld, bool withPackageName, bool ai)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											fld = null;
							 | 
						|||
| 
								 | 
							
											if (file == null) return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if (withPackageName)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												var pkg = file.Package;
							 | 
						|||
| 
								 | 
							
												if (string.IsNullOrWhiteSpace(pkg)) return false; // we're only looking *with* packages right now
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												if (!ion.StartsWith(pkg + ".")) return false; // wrong file
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												ion = ion.Substring(pkg.Length + 1); // and fully qualified (non-qualified is a second pass)
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return file.TryResolveExtension(ee, ion, out fld, ai, false);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								        private bool TryResolveExtension(string extendee, string extension, out FieldDescriptorProto field, bool allowImports = true, bool checkOwnPackage = true)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            bool isRooted = extension.StartsWith(".");
							 | 
						|||
| 
								 | 
							
								            if (isRooted)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // rooted
							 | 
						|||
| 
								 | 
							
								                extension = extension.Substring(1); // remove the root
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            string left;
							 | 
						|||
| 
								 | 
							
								            string right;
							 | 
						|||
| 
								 | 
							
								            if (TrySplitLast(extension, out left, out right))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                IType type;
							 | 
						|||
| 
								 | 
							
								                if (TryResolveType(left, null, out type, true, true))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    field = (type as DescriptorProto)?.Extensions?.FirstOrDefault(x => x.Extendee == extendee
							 | 
						|||
| 
								 | 
							
								                        && x.Name == right);
							 | 
						|||
| 
								 | 
							
								                    if (field != null) return true;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                field = Extensions?.FirstOrDefault(x => x.Extendee == extendee && x.Name == extension);
							 | 
						|||
| 
								 | 
							
								                if (field != null) return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (checkOwnPackage)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (TryResolveFromFile(this, extendee, extension, out field, true, false)) return true;
							 | 
						|||
| 
								 | 
							
								                if (TryResolveFromFile(this, extendee, extension, out field, false, false)) return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            if (allowImports)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                foreach (var import in _imports)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var file = Parent?.GetFile(import.Path);
							 | 
						|||
| 
								 | 
							
								                    if (file != null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        if (TryResolveFromFile(file, extendee, extension, out field, true, import.IsPublic))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            import.Used = true;
							 | 
						|||
| 
								 | 
							
								                            return true;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                foreach (var import in _imports)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var file = Parent?.GetFile(import.Path);
							 | 
						|||
| 
								 | 
							
								                    if (file != null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        if (TryResolveFromFile(file, extendee, extension, out field, false, import.IsPublic))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            import.Used = true;
							 | 
						|||
| 
								 | 
							
								                            return true;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            field = null;
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
										bool TryResolveFromFile(FileDescriptorProto file, string tn, bool ai, out IType tp, bool withPackageName, bool treatAllAsPublic)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											tp = null;
							 | 
						|||
| 
								 | 
							
											if (file == null) return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if (withPackageName)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												var pkg = file.Package;
							 | 
						|||
| 
								 | 
							
												if (string.IsNullOrWhiteSpace(pkg)) return false; // we're only looking *with* packages right now
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												if (!tn.StartsWith(pkg + ".")) return false; // wrong file
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												tn = tn.Substring(pkg.Length + 1); // and fully qualified (non-qualified is a second pass)
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return file.TryResolveType(tn, file, out tp, ai, false, treatAllAsPublic);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								        internal bool TryResolveType(string typeName, IType parent, out IType type, bool allowImports, bool checkOwnPackage = true, bool treatAllAsPublic = false)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            bool isRooted = typeName.StartsWith(".");
							 | 
						|||
| 
								 | 
							
								            string left;
							 | 
						|||
| 
								 | 
							
								            string right;
							 | 
						|||
| 
								 | 
							
								            if (isRooted)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // rooted
							 | 
						|||
| 
								 | 
							
								                typeName = typeName.Substring(1); // remove the root
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (TrySplit(typeName, out left, out right))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                while (parent != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var next = parent?.Find(left);
							 | 
						|||
| 
								 | 
							
								                    if (next != null && TryResolveType(right, next, out type, false, treatAllAsPublic)) return true;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    parent = parent.Parent;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // simple name
							 | 
						|||
| 
								 | 
							
								                while (parent != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    type = parent.Find(typeName);
							 | 
						|||
| 
								 | 
							
								                    if (type != null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        return true;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    parent = parent.Parent;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (checkOwnPackage && TryResolveFromFile(this, typeName, false, out type, true, treatAllAsPublic)) return true;
							 | 
						|||
| 
								 | 
							
								            if (checkOwnPackage && TryResolveFromFile(this, typeName, false, out type, false, treatAllAsPublic)) return true;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            // look at imports
							 | 
						|||
| 
								 | 
							
								            // check for the name including the package prefix
							 | 
						|||
| 
								 | 
							
								            foreach (var import in _imports)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (allowImports || import.IsPublic || treatAllAsPublic)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var file = Parent?.GetFile(import.Path);
							 | 
						|||
| 
								 | 
							
								                    if (TryResolveFromFile(file, typeName, false, out type, true, treatAllAsPublic))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        import.Used = true;
							 | 
						|||
| 
								 | 
							
								                        return true;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            // now look without package prefix
							 | 
						|||
| 
								 | 
							
								            foreach (var import in _imports)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (allowImports || import.IsPublic || treatAllAsPublic)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var file = Parent?.GetFile(import.Path);
							 | 
						|||
| 
								 | 
							
								                    if (TryResolveFromFile(file, typeName, false, out type, false, treatAllAsPublic))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        import.Used = true;
							 | 
						|||
| 
								 | 
							
								                        return true;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            type = null;
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static void SetParents(string prefix, EnumDescriptorProto parent)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            parent.FullyQualifiedName = prefix + "." + parent.Name;
							 | 
						|||
| 
								 | 
							
								            foreach (var val in parent.Values)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                val.Parent = parent;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        static void SetParents(string prefix, DescriptorProto parent)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var fqn = parent.FullyQualifiedName = prefix + "." + parent.Name;
							 | 
						|||
| 
								 | 
							
								            foreach (var field in parent.Fields)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                field.Parent = parent;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var @enum in parent.EnumTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                @enum.Parent = parent;
							 | 
						|||
| 
								 | 
							
								                SetParents(fqn, @enum);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var child in parent.NestedTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                child.Parent = parent;
							 | 
						|||
| 
								 | 
							
								                SetParents(fqn, child);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var ext in parent.Extensions)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ext.Parent = parent;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal void BuildTypeHierarchy(FileDescriptorSet set, ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            // build the tree starting at the root
							 | 
						|||
| 
								 | 
							
								            Parent = set;
							 | 
						|||
| 
								 | 
							
								            var prefix = string.IsNullOrWhiteSpace(Package) ? "" : ("." + Package);
							 | 
						|||
| 
								 | 
							
								            foreach (var type in EnumTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                type.Parent = this;
							 | 
						|||
| 
								 | 
							
								                SetParents(prefix, type);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var type in MessageTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                type.Parent = this;
							 | 
						|||
| 
								 | 
							
								                SetParents(prefix, type);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var type in Extensions)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                type.Parent = this;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static bool ShouldResolveType(FieldDescriptorProto.Type type)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (type)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case 0:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeMessage:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeEnum:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeGroup:
							 | 
						|||
| 
								 | 
							
								                    return true;
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        private void ResolveTypes(ParserContext ctx, List<FieldDescriptorProto> fields, IType parent, bool options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            foreach (var field in fields)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (options) ResolveOptions(ctx, field.Options);
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (!string.IsNullOrEmpty(field.TypeName) && ShouldResolveType(field.type))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        // TODO: use TryResolveType once rather than twice
							 | 
						|||
| 
								 | 
							
								                        string fqn;
							 | 
						|||
| 
								 | 
							
								                        DescriptorProto msg;
							 | 
						|||
| 
								 | 
							
								                        EnumDescriptorProto @enum;
							 | 
						|||
| 
								 | 
							
								                        if (TryResolveMessage(field.TypeName, parent, out msg, true))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            if (field.type != FieldDescriptorProto.Type.TypeGroup)
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                field.type = FieldDescriptorProto.Type.TypeMessage;
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            fqn = msg?.FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else if (TryResolveEnum(field.TypeName, parent, out @enum, true))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            field.type = FieldDescriptorProto.Type.TypeEnum;
							 | 
						|||
| 
								 | 
							
								                            if (!string.IsNullOrWhiteSpace(field.DefaultValue)
							 | 
						|||
| 
								 | 
							
								                                & !@enum.Values.Any(x => x.Name == field.DefaultValue))
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                ctx.Errors.Error(field.TypeToken, $"enum {@enum.Name} does not contain value '{field.DefaultValue}'");
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            fqn = @enum?.FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ctx.Errors.Add(field.TypeToken.TypeNotFound(field.TypeName));
							 | 
						|||
| 
								 | 
							
								                            fqn = field.TypeName;
							 | 
						|||
| 
								 | 
							
								                            field.type = FieldDescriptorProto.Type.TypeMessage; // just an assumption
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        field.TypeName = fqn;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    if (!string.IsNullOrEmpty(field.Extendee))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        string fqn;
							 | 
						|||
| 
								 | 
							
								                        DescriptorProto msg;
							 | 
						|||
| 
								 | 
							
								                        if (TryResolveMessage(field.Extendee, parent, out msg, true))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            fqn = msg?.FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ctx.Errors.Add(field.TypeToken.TypeNotFound(field.Extendee));
							 | 
						|||
| 
								 | 
							
								                            fqn = field.Extendee;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        field.Extendee = fqn;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    if (field.Options?.Packed ?? false)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        bool canPack = FieldDescriptorProto.CanPack(field.type);
							 | 
						|||
| 
								 | 
							
								                        if (!canPack)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ctx.Errors.Error(field.TypeToken, $"field of type {field.type} cannot be packed");
							 | 
						|||
| 
								 | 
							
								                            field.Options.Packed = false;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ResolveTypes(ParserContext ctx, ServiceDescriptorProto service, bool options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (options) ResolveOptions(ctx, service.Options);
							 | 
						|||
| 
								 | 
							
								            foreach (var method in service.Methods)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (options) ResolveOptions(ctx, method.Options);
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    DescriptorProto msg;
							 | 
						|||
| 
								 | 
							
								                    if (!TryResolveMessage(method.InputType, this, out msg, true))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ctx.Errors.Add(method.InputTypeToken.TypeNotFound(method.InputType));
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    method.InputType = msg?.FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								                    if (!TryResolveMessage(method.OutputType, this, out msg, true))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ctx.Errors.Add(method.OutputTypeToken.TypeNotFound(method.OutputType));
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    method.OutputType = msg?.FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ResolveTypes(ParserContext ctx, DescriptorProto type, bool options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (options)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveOptions(ctx, type.Options);
							 | 
						|||
| 
								 | 
							
								                foreach (var decl in type.OneofDecls)
							 | 
						|||
| 
								 | 
							
								                    ResolveOptions(ctx, decl.Options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            ResolveTypes(ctx, type.Fields, type, options);
							 | 
						|||
| 
								 | 
							
								            ResolveTypes(ctx, type.Extensions, type, options);
							 | 
						|||
| 
								 | 
							
								            foreach (var nested in type.NestedTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveTypes(ctx, nested, options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var nested in type.EnumTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveTypes(ctx, nested, options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        IEnumerable<string> IHazNames.GetNames()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            foreach (var type in MessageTypes) yield return type.Name;
							 | 
						|||
| 
								 | 
							
								            foreach (var type in EnumTypes) yield return type.Name;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal void ResolveTypes(ParserContext ctx, bool options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (options) ResolveOptions(ctx, Options);
							 | 
						|||
| 
								 | 
							
								            foreach (var type in MessageTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveTypes(ctx, type, options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var type in EnumTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveTypes(ctx, type, options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var service in Services)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveTypes(ctx, service, options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ResolveTypes(ctx, Extensions, this, options);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (options) // can only process deps on the second pass, once options have been resolved
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                HashSet<string> publicDependencies = null;
							 | 
						|||
| 
								 | 
							
								                foreach (var import in _imports)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (!Dependencies.Contains(import.Path))
							 | 
						|||
| 
								 | 
							
								                        Dependencies.Add(import.Path);
							 | 
						|||
| 
								 | 
							
								                    if (import.IsPublic)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        (publicDependencies ?? (publicDependencies = new HashSet<string>())).Add(import.Path);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    if (IncludeInOutput && !import.Used)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ctx.Errors.Warn(import.Token, $"import not used: '{import.Path}'");
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                // note that Dependencies should stay in declaration order to be consistent with protoc
							 | 
						|||
| 
								 | 
							
								                if (publicDependencies != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var arr = publicDependencies.Select(path => Dependencies.IndexOf(path)).ToArray();
							 | 
						|||
| 
								 | 
							
								                    Array.Sort(arr);
							 | 
						|||
| 
								 | 
							
								                    PublicDependencies = arr;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ResolveTypes(ParserContext ctx, EnumDescriptorProto type, bool options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (options)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ResolveOptions(ctx, type.Options);
							 | 
						|||
| 
								 | 
							
								                foreach (var val in type.Values)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ResolveOptions(ctx, val.Options);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ResolveOptions(ParserContext ctx, ISchemaOptions options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (options == null || options.UninterpretedOptions.Count == 0) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var extension = ((IExtensible)options).GetExtensionObject(true);
							 | 
						|||
| 
								 | 
							
								            var target = extension.BeginAppend();
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                using (var writer = new ProtoWriter(target, null, null))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var hive = OptionHive.Build(options.UninterpretedOptions);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    // first pass is used to sort the fields so we write them in the right order
							 | 
						|||
| 
								 | 
							
								                    AppendOptions(this, writer, ctx, options.Extendee, hive.Children, true, 0, false);
							 | 
						|||
| 
								 | 
							
								                    // second pass applies the data
							 | 
						|||
| 
								 | 
							
								                    AppendOptions(this, writer, ctx, options.Extendee, hive.Children, false, 0, false);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                options.UninterpretedOptions.RemoveAll(x => x.Applied);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            finally
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                extension.EndAppend(target, true);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        class OptionHive
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            public OptionHive(string name, bool isExtension, Token token)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Name = name;
							 | 
						|||
| 
								 | 
							
								                IsExtension = isExtension;
							 | 
						|||
| 
								 | 
							
								                Token = token;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            public override string ToString()
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var sb = new StringBuilder();
							 | 
						|||
| 
								 | 
							
								                Concat(sb);
							 | 
						|||
| 
								 | 
							
								                return sb.ToString();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            private void Concat(StringBuilder sb)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                bool isFirst = true;
							 | 
						|||
| 
								 | 
							
								                foreach (var value in Options)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (!isFirst) sb.Append(", ");
							 | 
						|||
| 
								 | 
							
								                    isFirst = false;
							 | 
						|||
| 
								 | 
							
								                    sb.Append(value);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                foreach (var child in Children)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (!isFirst) sb.Append(", ");
							 | 
						|||
| 
								 | 
							
								                    sb.Append(child.Name).Append("={");
							 | 
						|||
| 
								 | 
							
								                    child.Concat(sb);
							 | 
						|||
| 
								 | 
							
								                    sb.Append("}");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            public bool IsExtension { get; }
							 | 
						|||
| 
								 | 
							
								            public string Name { get; }
							 | 
						|||
| 
								 | 
							
								            public Token Token { get; }
							 | 
						|||
| 
								 | 
							
								            public List<UninterpretedOption> Options { get; } = new List<UninterpretedOption>();
							 | 
						|||
| 
								 | 
							
								            public List<OptionHive> Children { get; } = new List<OptionHive>();
							 | 
						|||
| 
								 | 
							
								            public FieldDescriptorProto Field { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            public static OptionHive Build(List<UninterpretedOption> options)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (options == null || options.Count == 0) return null;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                var root = new OptionHive(null, false, default(Token));
							 | 
						|||
| 
								 | 
							
								                foreach (var option in options)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var level = root;
							 | 
						|||
| 
								 | 
							
								                    OptionHive nextLevel = null;
							 | 
						|||
| 
								 | 
							
								                    foreach (var name in option.Names)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        nextLevel = level.Children.FirstOrDefault(x => x.Name == name.name_part && x.IsExtension == name.IsExtension);
							 | 
						|||
| 
								 | 
							
								                        if (nextLevel == null)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            nextLevel = new OptionHive(name.name_part, name.IsExtension, name.Token);
							 | 
						|||
| 
								 | 
							
								                            level.Children.Add(nextLevel);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        level = nextLevel;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    level.Options.Add(option);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                return root;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        private static void AppendOptions(FileDescriptorProto file, ProtoWriter writer, ParserContext ctx, string extendee, List<OptionHive> options, bool resolveOnly, int depth, bool messageSet)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            foreach (var option in options)
							 | 
						|||
| 
								 | 
							
								                AppendOption(file, writer, ctx, extendee, option, resolveOnly, depth, messageSet);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (resolveOnly && depth != 0) // fun fact: proto writes root fields in *file* order, but sub-fields in *field* order
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // ascending field order
							 | 
						|||
| 
								 | 
							
								                options.Sort((x, y) => (x.Field?.Number ?? 0).CompareTo(y.Field?.Number ?? 0));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        private static bool ShouldWrite(FieldDescriptorProto f, string v, string d){
							 | 
						|||
| 
								 | 
							
								            return f.label != FieldDescriptorProto.Label.LabelOptional || v != (f.DefaultValue ?? d);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        private static void AppendOption(FileDescriptorProto file, ProtoWriter writer, ParserContext ctx, string extendee, OptionHive option, bool resolveOnly, int depth, bool messageSet)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            // resolve the field for this level
							 | 
						|||
| 
								 | 
							
								            FieldDescriptorProto field = option.Field;
							 | 
						|||
| 
								 | 
							
								            DescriptorProto msg;
							 | 
						|||
| 
								 | 
							
								            if (field != null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // already resolved
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (option.IsExtension)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (!file.TryResolveExtension(extendee, option.Name, out field)) field = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (file.TryResolveMessage(extendee, null, out msg, true))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                field = msg.Fields.FirstOrDefault(x => x.Name == option.Name);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                field = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (field == null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (!resolveOnly)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(option.Token, $"unable to resolve custom option '{option.Name}' for '{extendee}'");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                return;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            option.Field = field;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            switch (field.type)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeMessage:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeGroup:
							 | 
						|||
| 
								 | 
							
								                    var nextFile = GetFile(field.Parent as IType);
							 | 
						|||
| 
								 | 
							
								                    DescriptorProto fieldType;
							 | 
						|||
| 
								 | 
							
								                    var nextMessageSet = !resolveOnly && nextFile.TryResolveMessage(field.TypeName, null, out fieldType, true)
							 | 
						|||
| 
								 | 
							
								                        && (fieldType.Options?.MessageSetWireFormat ?? false);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    if (option.Children.Count != 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        if (resolveOnly)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            AppendOptions(nextFile, writer, ctx, field.TypeName, option.Children, resolveOnly, depth + 1, nextMessageSet);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else if (messageSet)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(1, WireType.StartGroup, writer);
							 | 
						|||
| 
								 | 
							
								                            var grp = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(2, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteInt32(field.Number, writer);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(3, WireType.String, writer);
							 | 
						|||
| 
								 | 
							
								                            var payload = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            AppendOptions(nextFile, writer, ctx, field.TypeName, option.Children, resolveOnly, depth + 1, nextMessageSet);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.EndSubItem(payload, writer);
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.EndSubItem(grp, writer);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(field.Number,
							 | 
						|||
| 
								 | 
							
								                                field.type == FieldDescriptorProto.Type.TypeGroup ? WireType.StartGroup : WireType.String, writer);
							 | 
						|||
| 
								 | 
							
								                            var tok = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            AppendOptions(nextFile, writer, ctx, field.TypeName, option.Children, resolveOnly, depth + 1, nextMessageSet);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.EndSubItem(tok, writer);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    if (resolveOnly) return; // nothing more to do
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    if (option.Options.Count == 1 && !option.Options.Single().ShouldSerializeAggregateValue())
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        // need to write an empty object to match protoc
							 | 
						|||
| 
								 | 
							
								                        if (messageSet)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(1, WireType.StartGroup, writer);
							 | 
						|||
| 
								 | 
							
								                            var grp = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(2, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteInt32(field.Number, writer);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(3, WireType.String, writer);
							 | 
						|||
| 
								 | 
							
								                            var payload = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.EndSubItem(payload, writer);
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.EndSubItem(grp, writer);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.WriteFieldHeader(field.Number,
							 | 
						|||
| 
								 | 
							
								                                   field.type == FieldDescriptorProto.Type.TypeGroup ? WireType.StartGroup : WireType.String, writer);
							 | 
						|||
| 
								 | 
							
								                            var payload = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								                            ProtoWriter.EndSubItem(payload, writer);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        option.Options.Single().Applied = true;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        foreach (var values in option.Options)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            ctx.Errors.Error(option.Token, $"unable to assign custom option '{option.Name}' for '{extendee}'");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    if (resolveOnly) return; // nothing more to do
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    foreach (var child in option.Children)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ctx.Errors.Error(option.Token, $"unable to assign custom option '{child.Name}' for '{extendee}'");
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    foreach (var value in option.Options)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        int i32;
							 | 
						|||
| 
								 | 
							
								                        switch (field.type)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeFloat:
							 | 
						|||
| 
								 | 
							
								                                float f32;
							 | 
						|||
| 
								 | 
							
								                                if (!TokenExtensions.TryParseSingle(value.AggregateValue, out f32))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ctx.Errors.Error(option.Token, $"invalid value for floating point '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                    continue;
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                if (ShouldWrite(field, value.AggregateValue, "0"))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteFieldHeader(field.Number, WireType.Fixed32, writer);
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteSingle(f32, writer);
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeDouble:
							 | 
						|||
| 
								 | 
							
								                                double f64;
							 | 
						|||
| 
								 | 
							
								                                if (!TokenExtensions.TryParseDouble(value.AggregateValue, out f64))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ctx.Errors.Error(option.Token, $"invalid value for floating point '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                    continue;
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                if (ShouldWrite(field, value.AggregateValue, "0"))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteFieldHeader(field.Number, WireType.Fixed64, writer);
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteDouble(f64, writer);
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeBool:
							 | 
						|||
| 
								 | 
							
								                                switch (value.AggregateValue)
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    case "true":
							 | 
						|||
| 
								 | 
							
								                                        i32 = 1;
							 | 
						|||
| 
								 | 
							
								                                        break;
							 | 
						|||
| 
								 | 
							
								                                    case "false":
							 | 
						|||
| 
								 | 
							
								                                        i32 = 0;
							 | 
						|||
| 
								 | 
							
								                                        break;
							 | 
						|||
| 
								 | 
							
								                                    default:
							 | 
						|||
| 
								 | 
							
								                                        ctx.Errors.Error(option.Token, $"invalid value for boolean '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                        continue;
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                if (ShouldWrite(field, value.AggregateValue, "false"))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteFieldHeader(field.Number, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteInt32(i32, writer);
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeUint32:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeFixed32:
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    uint ui32;
							 | 
						|||
| 
								 | 
							
								                                    if (!TokenExtensions.TryParseUInt32(value.AggregateValue, out ui32))
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        ctx.Errors.Error(option.Token, $"invalid value for unsigned integer '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                        continue;
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                    if (ShouldWrite(field, value.AggregateValue, "0"))
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        switch (field.type)
							 | 
						|||
| 
								 | 
							
								                                        {
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeUint32:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeFixed32:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.Fixed32, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                        }
							 | 
						|||
| 
								 | 
							
								                                        ProtoWriter.WriteUInt32(ui32, writer);
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeUint64:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeFixed64:
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ulong ui64;
							 | 
						|||
| 
								 | 
							
								                                    if (!TokenExtensions.TryParseUInt64(value.AggregateValue, out ui64))
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        ctx.Errors.Error(option.Token, $"invalid value for unsigned integer '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                        continue;
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                    if (ShouldWrite(field, value.AggregateValue, "0"))
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        switch (field.type)
							 | 
						|||
| 
								 | 
							
								                                        {
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeUint64:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeFixed64:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.Fixed64, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                        }
							 | 
						|||
| 
								 | 
							
								                                        ProtoWriter.WriteUInt64(ui64, writer);
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeInt32:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeSint32:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeSfixed32:
							 | 
						|||
| 
								 | 
							
								                                if (!TokenExtensions.TryParseInt32(value.AggregateValue, out i32))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ctx.Errors.Error(option.Token, $"invalid value for integer '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                    continue;
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                if (ShouldWrite(field, value.AggregateValue, "0"))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    switch (field.type)
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        case FieldDescriptorProto.Type.TypeInt32:
							 | 
						|||
| 
								 | 
							
								                                            ProtoWriter.WriteFieldHeader(field.Number, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                                            break;
							 | 
						|||
| 
								 | 
							
								                                        case FieldDescriptorProto.Type.TypeSint32:
							 | 
						|||
| 
								 | 
							
								                                            ProtoWriter.WriteFieldHeader(field.Number, WireType.SignedVariant, writer);
							 | 
						|||
| 
								 | 
							
								                                            break;
							 | 
						|||
| 
								 | 
							
								                                        case FieldDescriptorProto.Type.TypeSfixed32:
							 | 
						|||
| 
								 | 
							
								                                            ProtoWriter.WriteFieldHeader(field.Number, WireType.Fixed32, writer);
							 | 
						|||
| 
								 | 
							
								                                            break;
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteInt32(i32, writer);
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeInt64:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeSint64:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeSfixed64:
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    long i64;
							 | 
						|||
| 
								 | 
							
								                                    if (!TokenExtensions.TryParseInt64(value.AggregateValue, out i64))
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        ctx.Errors.Error(option.Token, $"invalid value for integer '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                        continue;
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                    if (ShouldWrite(field, value.AggregateValue, "0"))
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        switch (field.type)
							 | 
						|||
| 
								 | 
							
								                                        {
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeInt64:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeSint64:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.SignedVariant, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                            case FieldDescriptorProto.Type.TypeSfixed64:
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteFieldHeader(field.Number, WireType.Fixed64, writer);
							 | 
						|||
| 
								 | 
							
								                                                break;
							 | 
						|||
| 
								 | 
							
								                                        }
							 | 
						|||
| 
								 | 
							
								                                        ProtoWriter.WriteInt64(i64, writer);
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeEnum:
							 | 
						|||
| 
								 | 
							
								                                EnumDescriptorProto @enum;
							 | 
						|||
| 
								 | 
							
								                                if (file.TryResolveEnum(field.TypeName, null, out @enum, true, true))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    var found = @enum.Values.FirstOrDefault(x => x.Name == value.AggregateValue);
							 | 
						|||
| 
								 | 
							
								                                    if (found == null)
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        ctx.Errors.Error(option.Token, $"invalid value for enum '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                        continue;
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                    else
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        if (ShouldWrite(field, value.AggregateValue, @enum.Values.FirstOrDefault()?.Name))
							 | 
						|||
| 
								 | 
							
								                                        {
							 | 
						|||
| 
								 | 
							
								                                            ProtoWriter.WriteFieldHeader(field.Number, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                                            ProtoWriter.WriteInt32(found.Number, writer);
							 | 
						|||
| 
								 | 
							
								                                        }
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                else
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ctx.Errors.Error(option.Token, $"unable to resolve enum '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                    continue;
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeString:
							 | 
						|||
| 
								 | 
							
								                            case FieldDescriptorProto.Type.TypeBytes:
							 | 
						|||
| 
								 | 
							
								                                if (ShouldWrite(field, value.AggregateValue, ""))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    ProtoWriter.WriteFieldHeader(field.Number, WireType.String, writer);
							 | 
						|||
| 
								 | 
							
								                                    if (value.AggregateValue == null || value.AggregateValue.IndexOf('\\') < 0)
							 | 
						|||
| 
								 | 
							
								                                        ProtoWriter.WriteString(value.AggregateValue ?? "", writer);
							 | 
						|||
| 
								 | 
							
								                                    else
							 | 
						|||
| 
								 | 
							
								                                    {
							 | 
						|||
| 
								 | 
							
								                                        using (var ms = new MemoryStream(value.AggregateValue.Length))
							 | 
						|||
| 
								 | 
							
								                                        {
							 | 
						|||
| 
								 | 
							
								                                            if (!LoadBytes(ms, value.AggregateValue))
							 | 
						|||
| 
								 | 
							
								                                            {
							 | 
						|||
| 
								 | 
							
								                                                ctx.Errors.Error(option.Token, $"invalid escape sequence '{field.TypeName}': '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                                continue;
							 | 
						|||
| 
								 | 
							
								                                            }
							 | 
						|||
| 
								 | 
							
								#if NETSTANDARD1_3
							 | 
						|||
| 
								 | 
							
								                                            if (ms.TryGetBuffer(out var seg))
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteBytes(seg.Array, seg.Offset, seg.Count, writer);
							 | 
						|||
| 
								 | 
							
								                                            else
							 | 
						|||
| 
								 | 
							
								                                                ProtoWriter.WriteBytes(ms.ToArray(), writer);
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								                                            ProtoWriter.WriteBytes(ms.GetBuffer(), 0, (int)ms.Length, writer);
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								                                        }
							 | 
						|||
| 
								 | 
							
								                                    }
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            default:
							 | 
						|||
| 
								 | 
							
								                                ctx.Errors.Error(option.Token, $"{field.type} options not yet implemented: '{option.Name}' = '{value.AggregateValue}'");
							 | 
						|||
| 
								 | 
							
								                                continue;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        value.Applied = true;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private static unsafe bool LoadBytes(Stream ms, string value)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            bool isEscaped = false;
							 | 
						|||
| 
								 | 
							
								            byte* b = stackalloc byte[10];
							 | 
						|||
| 
								 | 
							
								            foreach (char c in value)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (isEscaped)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    isEscaped = false;
							 | 
						|||
| 
								 | 
							
								                    // only a few things remain escaped after ConsumeString:
							 | 
						|||
| 
								 | 
							
								                    switch (c)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        case '\\': ms.WriteByte((byte)'\\'); break;
							 | 
						|||
| 
								 | 
							
								                        case '\'': ms.WriteByte((byte)'\''); break;
							 | 
						|||
| 
								 | 
							
								                        case '"': ms.WriteByte((byte)'"'); break;
							 | 
						|||
| 
								 | 
							
								                        case 'r': ms.WriteByte((byte)'\r'); break;
							 | 
						|||
| 
								 | 
							
								                        case 'n': ms.WriteByte((byte)'\n'); break;
							 | 
						|||
| 
								 | 
							
								                        case 't': ms.WriteByte((byte)'\t'); break;
							 | 
						|||
| 
								 | 
							
								                        default: return false;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else if (c == '\\')
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    isEscaped = true;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var x = c; // can't take address of readonly local
							 | 
						|||
| 
								 | 
							
								                    int bytes = Encoding.UTF8.GetBytes(&x, 1, b, 10);
							 | 
						|||
| 
								 | 
							
								                    for (int i = 0; i < bytes; i++)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ms.WriteByte(b[i]);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return !isEscaped;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    partial class EnumDescriptorProto : ISchemaObject, IType
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Name;
							 | 
						|||
| 
								 | 
							
								        internal IType Parent { get; set; }
							 | 
						|||
| 
								 | 
							
								        string IType.FullyQualifiedName => FullyQualifiedName;
							 | 
						|||
| 
								 | 
							
								        IType IType.Parent => Parent;
							 | 
						|||
| 
								 | 
							
								        IType IType.Find(string name) => null;
							 | 
						|||
| 
								 | 
							
								        internal string FullyQualifiedName { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static bool TryParse(ParserContext ctx, IHazNames parent, out EnumDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = ctx.Tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            ctx.CheckNames(parent, name, ctx.Tokens.Previous);
							 | 
						|||
| 
								 | 
							
								            if (ctx.TryReadObject(out obj))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                obj.Name = name;
							 | 
						|||
| 
								 | 
							
								                return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.AlphaNumeric, "option"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Options = ctx.ParseOptionStatement(Options, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Values.Add(EnumValueDescriptorProto.Parse(ctx));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class FieldDescriptorProto : ISchemaObject
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public bool IsPacked(string syntax)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (label != Label.LabelRepeated) return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var exp = Options?.Packed;
							 | 
						|||
| 
								 | 
							
								            if (exp.HasValue) return exp.GetValueOrDefault();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (syntax != FileDescriptorProto.SyntaxProto2 && FieldDescriptorProto.CanPack(type))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Name;
							 | 
						|||
| 
								 | 
							
								        internal const int DefaultMaxField = 536870911;
							 | 
						|||
| 
								 | 
							
								        internal const int FirstReservedField = 19000;
							 | 
						|||
| 
								 | 
							
								        internal const int LastReservedField = 19999;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal IMessage Parent { get; set; }
							 | 
						|||
| 
								 | 
							
								        internal Token TypeToken { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal int MaxField => Parent?.MaxField ?? DefaultMaxField;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										internal static void NotAllowedOneOf(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											var token = ctx.Tokens.Previous;
							 | 
						|||
| 
								 | 
							
											ctx.Errors.Error(token, $"'{token.Value}' not allowed with 'oneof'");
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static bool TryParse(ParserContext ctx, IMessage parent, bool isOneOf, out FieldDescriptorProto field)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								            Label label = Label.LabelOptional; // default
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.AlphaNumeric, "repeated"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (isOneOf) NotAllowedOneOf(ctx);
							 | 
						|||
| 
								 | 
							
								                label = Label.LabelRepeated;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "required"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (isOneOf) NotAllowedOneOf(ctx);
							 | 
						|||
| 
								 | 
							
								                else tokens.Previous.RequireProto2(ctx);
							 | 
						|||
| 
								 | 
							
								                label = Label.LabelRequired;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (tokens.ConsumeIf(TokenType.AlphaNumeric, "optional"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (isOneOf) NotAllowedOneOf(ctx);
							 | 
						|||
| 
								 | 
							
								                else tokens.Previous.RequireProto2(ctx);
							 | 
						|||
| 
								 | 
							
								                label = Label.LabelOptional;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (ctx.Syntax == FileDescriptorProto.SyntaxProto2 && !isOneOf)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // required in proto2
							 | 
						|||
| 
								 | 
							
								                throw tokens.Read().Throw("expected 'repeated' / 'required' / 'optional'");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var typeToken = tokens.Read();
							 | 
						|||
| 
								 | 
							
								            if (typeToken.Is(TokenType.AlphaNumeric, "map"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                tokens.Previous.Throw($"'{tokens.Previous.Value}' can not be used with 'map'");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            string typeName = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var isGroup = typeName == "group";
							 | 
						|||
| 
								 | 
							
								            if (isGroup)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                //if (isOneOf) NotAllowedOneOf(ctx);
							 | 
						|||
| 
								 | 
							
								                //else if (parentTyped == null)
							 | 
						|||
| 
								 | 
							
								                //{
							 | 
						|||
| 
								 | 
							
								                //    ctx.Errors.Error(tokens.Previous, "group not allowed in this context");
							 | 
						|||
| 
								 | 
							
								                //}
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.Object;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            string name = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            var nameToken = tokens.Previous;
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, "=");
							 | 
						|||
| 
								 | 
							
								            var number = tokens.ConsumeInt32();
							 | 
						|||
| 
								 | 
							
								            var numberToken = tokens.Previous;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (number < 1 || number > parent.MaxField)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.Errors.Error(numberToken, $"field numbers must be in the range 1-{parent.MaxField}");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (number >= FirstReservedField && number <= LastReservedField)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.Errors.Warn(numberToken, $"field numbers in the range {FirstReservedField}-{LastReservedField} are reserved; this may cause problems on many implementations");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ctx.CheckNames(parent, name, nameToken);
							 | 
						|||
| 
								 | 
							
								            if (parent is DescriptorProto)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var parentTyped = parent as DescriptorProto;
							 | 
						|||
| 
								 | 
							
								                var conflict = parentTyped.Fields.FirstOrDefault(x => x.Number == number);
							 | 
						|||
| 
								 | 
							
								                if (conflict != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(numberToken, $"field {number} is already in use by '{conflict.Name}'");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                if (parentTyped.ReservedNames.Contains(name))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(nameToken, $"field '{name}' is reserved");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                if (parentTyped.ReservedRanges.Any(x => x.Start <= number && x.End > number))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(numberToken, $"field {number} is reserved");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            Type type;
							 | 
						|||
| 
								 | 
							
								            if (isGroup)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                type = Type.TypeGroup;
							 | 
						|||
| 
								 | 
							
								                typeName = name;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                typeToken.RequireProto2(ctx);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                var firstChar = typeName[0].ToString();
							 | 
						|||
| 
								 | 
							
								                if (firstChar.ToLowerInvariant() == firstChar)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(nameToken, "group names must start with an upper-case letter");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                name = typeName.ToLowerInvariant();
							 | 
						|||
| 
								 | 
							
								                DescriptorProto grpType;
							 | 
						|||
| 
								 | 
							
								                if (ctx.TryReadObject<DescriptorProto>(out grpType))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    grpType.Name = typeName;
							 | 
						|||
| 
								 | 
							
								                    ctx.CheckNames(parent, typeName, nameToken);
							 | 
						|||
| 
								 | 
							
								                    parent?.Types?.Add(grpType);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (TryIdentifyType(typeName, out type))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                typeName = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            field = new FieldDescriptorProto
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                type = type,
							 | 
						|||
| 
								 | 
							
								                TypeName = typeName,
							 | 
						|||
| 
								 | 
							
								                Name = name,
							 | 
						|||
| 
								 | 
							
								                JsonName = GetJsonName(name),
							 | 
						|||
| 
								 | 
							
								                Number = number,
							 | 
						|||
| 
								 | 
							
								                label = label,
							 | 
						|||
| 
								 | 
							
								                TypeToken = typeToken // internal property that helps give useful error messages
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (!isGroup)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (tokens.ConsumeIf(TokenType.Symbol, "["))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    field.Options = ctx.ParseOptionBlock(field.Options, field);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, ";");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								            return true;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        static readonly char[] Underscores = { '_' };
							 | 
						|||
| 
								 | 
							
								        internal static string GetJsonName(string name)
							 | 
						|||
| 
								 | 
							
								            => Regex.Replace(name, "_+([0-9a-zA-Z])", match => match.Groups[1].Value.ToUpperInvariant()).TrimEnd(Underscores);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static bool CanPack(Type type)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (type)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case Type.TypeBool:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeDouble:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeEnum:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeFixed32:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeFixed64:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeFloat:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeInt32:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeInt64:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeSfixed32:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeSfixed64:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeSint32:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeSint64:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeUint32:
							 | 
						|||
| 
								 | 
							
								                case Type.TypeUint64:
							 | 
						|||
| 
								 | 
							
								                    return true;
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
										internal static bool Assign(Type @in, out Type @out)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											@out = @in;
							 | 
						|||
| 
								 | 
							
											return true;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								        internal static bool TryIdentifyType(string typeName, out Type type)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (typeName)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case "bool": return Assign(Type.TypeBool, out @type);
							 | 
						|||
| 
								 | 
							
								                case "bytes": return Assign(Type.TypeBytes, out @type);
							 | 
						|||
| 
								 | 
							
								                case "double": return Assign(Type.TypeDouble, out @type);
							 | 
						|||
| 
								 | 
							
								                case "fixed32": return Assign(Type.TypeFixed32, out @type);
							 | 
						|||
| 
								 | 
							
								                case "fixed64": return Assign(Type.TypeFixed64, out @type);
							 | 
						|||
| 
								 | 
							
								                case "float": return Assign(Type.TypeFloat, out @type);
							 | 
						|||
| 
								 | 
							
								                case "int32": return Assign(Type.TypeInt32, out @type);
							 | 
						|||
| 
								 | 
							
								                case "int64": return Assign(Type.TypeInt64, out @type);
							 | 
						|||
| 
								 | 
							
								                case "sfixed32": return Assign(Type.TypeSfixed32, out @type);
							 | 
						|||
| 
								 | 
							
								                case "sfixed64": return Assign(Type.TypeSfixed64, out @type);
							 | 
						|||
| 
								 | 
							
								                case "sint32": return Assign(Type.TypeSint32, out @type);
							 | 
						|||
| 
								 | 
							
								                case "sint64": return Assign(Type.TypeSint64, out @type);
							 | 
						|||
| 
								 | 
							
								                case "string": return Assign(Type.TypeString, out @type);
							 | 
						|||
| 
								 | 
							
								                case "uint32": return Assign(Type.TypeUint32, out @type);
							 | 
						|||
| 
								 | 
							
								                case "uint64": return Assign(Type.TypeUint64, out @type);
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    type = default(Type);
							 | 
						|||
| 
								 | 
							
								                    return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static void ParseExtensions(ParserContext ctx, IMessage message)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var extendee = ctx.Tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            var dummy = new DummyExtensions(extendee, message);
							 | 
						|||
| 
								 | 
							
								            ctx.TryReadObjectImpl(dummy);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            throw new InvalidOperationException();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        class DummyExtensions : ISchemaObject, IHazNames, IMessage
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            int IMessage.MaxField => message.MaxField;
							 | 
						|||
| 
								 | 
							
								            List<DescriptorProto> IMessage.Types => message.Types;
							 | 
						|||
| 
								 | 
							
								            List<FieldDescriptorProto> IMessage.Extensions => message.Extensions;
							 | 
						|||
| 
								 | 
							
								            List<FieldDescriptorProto> IMessage.Fields => message.Fields;
							 | 
						|||
| 
								 | 
							
								            public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                get { return null; }
							 | 
						|||
| 
								 | 
							
								                set { }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            IEnumerable<string> IHazNames.GetNames()
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var fields = message.Fields;
							 | 
						|||
| 
								 | 
							
								                if (fields != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    foreach (var field in fields) yield return field.Name;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                foreach (var field in message.Extensions) yield return field.Name;
							 | 
						|||
| 
								 | 
							
								                foreach (var type in message.Types) yield return type.Name;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								                FieldDescriptorProto field;
							 | 
						|||
| 
								 | 
							
								                if (TryParse(ctx, this, false, out field))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    field.Extendee = extendee;
							 | 
						|||
| 
								 | 
							
								                    message.Extensions.Add(field);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            private IMessage message;
							 | 
						|||
| 
								 | 
							
								            private string extendee;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            public DummyExtensions(string extendee, IMessage message)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                this.extendee = extendee;
							 | 
						|||
| 
								 | 
							
								                this.message = message;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    internal interface IMessage : IHazNames
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        int MaxField { get; }
							 | 
						|||
| 
								 | 
							
								        List<DescriptorProto> Types { get; }
							 | 
						|||
| 
								 | 
							
								        List<FieldDescriptorProto> Extensions { get; }
							 | 
						|||
| 
								 | 
							
								        List<FieldDescriptorProto> Fields { get; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    partial class ServiceDescriptorProto : ISchemaObject
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal static bool TryParse(ParserContext ctx, out ServiceDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = ctx.Tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            if (ctx.TryReadObject(out obj))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                obj.Name = name;
							 | 
						|||
| 
								 | 
							
								                return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.AlphaNumeric, "option"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Options = ctx.ParseOptionStatement(Options, this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                // is a method
							 | 
						|||
| 
								 | 
							
								                Methods.Add(MethodDescriptorProto.Parse(ctx));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ctx.AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    partial class MethodDescriptorProto : ISchemaObject
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal Token InputTypeToken { get; set; }
							 | 
						|||
| 
								 | 
							
								        internal Token OutputTypeToken { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static MethodDescriptorProto Parse(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.AlphaNumeric, "rpc");
							 | 
						|||
| 
								 | 
							
								            var name = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, "(");
							 | 
						|||
| 
								 | 
							
								            bool isInputStream = tokens.ConsumeIf(TokenType.AlphaNumeric, "stream");
							 | 
						|||
| 
								 | 
							
								            var inputTypeToken = tokens.Read();
							 | 
						|||
| 
								 | 
							
								            var inputType = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, ")");
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.AlphaNumeric, "returns");
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, "(");
							 | 
						|||
| 
								 | 
							
								            bool isOutputStream = tokens.ConsumeIf(TokenType.AlphaNumeric, "stream");
							 | 
						|||
| 
								 | 
							
								            var outputTypeToken = tokens.Read();
							 | 
						|||
| 
								 | 
							
								            var outputType = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, ")");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var method = new MethodDescriptorProto
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Name = name,
							 | 
						|||
| 
								 | 
							
								                InputType = inputType,
							 | 
						|||
| 
								 | 
							
								                OutputType = outputType,
							 | 
						|||
| 
								 | 
							
								                InputTypeToken = inputTypeToken,
							 | 
						|||
| 
								 | 
							
								                OutputTypeToken = outputTypeToken
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								            if (isInputStream) method.ClientStreaming = true;
							 | 
						|||
| 
								 | 
							
								            if (isOutputStream) method.ServerStreaming = true;
							 | 
						|||
| 
								 | 
							
								            Token token;
							 | 
						|||
| 
								 | 
							
								            if (tokens.Peek(out token) && token.Is(TokenType.Symbol, "{"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ctx.AbortState = AbortState.Object;
							 | 
						|||
| 
								 | 
							
								                ctx.TryReadObjectImpl(method);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, ";");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return method;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void ISchemaObject.ReadOne(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ctx.Tokens.Consume(TokenType.AlphaNumeric, "option");
							 | 
						|||
| 
								 | 
							
								            Options = ctx.ParseOptionStatement(Options, this);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    partial class EnumValueDescriptorProto
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal static EnumValueDescriptorProto Parse(ParserContext ctx)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = ctx.Tokens;
							 | 
						|||
| 
								 | 
							
								            string name = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, "=");
							 | 
						|||
| 
								 | 
							
								            var value = tokens.ConsumeInt32();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var obj = new EnumValueDescriptorProto { Name = name, Number = value };
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.Symbol, "["))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                obj.Options = ctx.ParseOptionBlock(obj.Options);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            tokens.Consume(TokenType.Symbol, ";");
							 | 
						|||
| 
								 | 
							
								            return obj;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal EnumDescriptorProto Parent { get; set; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class MessageOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(MessageOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (key)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case "map_entry":
							 | 
						|||
| 
								 | 
							
								                    MapEntry = ctx.Tokens.ConsumeBoolean();
							 | 
						|||
| 
								 | 
							
								                    ctx.Errors.Error(ctx.Tokens.Previous, "'map_entry' should not be set explicitly; use 'map<TKey,TValue>' instead");
							 | 
						|||
| 
								 | 
							
								                    return true;
							 | 
						|||
| 
								 | 
							
								                case "message_set_wire_format": MessageSetWireFormat = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "no_standard_descriptor_accessor": NoStandardDescriptorAccessor = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                default: return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class MethodOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(MethodOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (key)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case "idempotency_level": idempotency_level = ctx.Tokens.ConsumeEnum<IdempotencyLevel>(); return true;
							 | 
						|||
| 
								 | 
							
								                default: return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class ServiceOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(ServiceOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key) => false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    partial class UninterpretedOption
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        partial class NamePart
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            public override string ToString() => IsExtension ? ("(" + name_part + ")") : name_part;
							 | 
						|||
| 
								 | 
							
								            internal Token Token { get; set; }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal bool Applied { get; set; }
							 | 
						|||
| 
								 | 
							
								        internal Token Token { get; set; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class EnumOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(EnumOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (key)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case "allow_alias": AllowAlias = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                default: return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class EnumValueOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(EnumValueOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key) => false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class FieldOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(FieldOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (key)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case "jstype": Jstype = ctx.Tokens.ConsumeEnum<JSType>(); return true;
							 | 
						|||
| 
								 | 
							
								                case "ctype": Ctype = ctx.Tokens.ConsumeEnum<CType>(); return true;
							 | 
						|||
| 
								 | 
							
								                case "lazy": Lazy = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "packed": Packed = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "weak": Weak = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                default: return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    partial class FileOptions : ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        string ISchemaOptions.Extendee => FileDescriptorSet.Namespace + nameof(FileOptions);
							 | 
						|||
| 
								 | 
							
								        bool ISchemaOptions.ReadOne(ParserContext ctx, string key)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (key)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case "optimize_for": OptimizeFor = ctx.Tokens.ConsumeEnum<OptimizeMode>(); return true;
							 | 
						|||
| 
								 | 
							
								                case "cc_enable_arenas": CcEnableArenas = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "cc_generic_services": CcGenericServices = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								#pragma warning disable 0612
							 | 
						|||
| 
								 | 
							
								                case "java_generate_equals_and_hash": JavaGenerateEqualsAndHash = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								#pragma warning restore 0612
							 | 
						|||
| 
								 | 
							
								                case "java_generic_services": JavaGenericServices = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "java_multiple_files": JavaMultipleFiles = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "java_string_check_utf8": JavaStringCheckUtf8 = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								                case "py_generic_services": PyGenericServices = ctx.Tokens.ConsumeBoolean(); return true;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                case "csharp_namespace": CsharpNamespace = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								                case "go_package": GoPackage = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								                case "java_outer_classname": JavaOuterClassname = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								                case "java_package": JavaPackage = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								                case "objc_class_prefix": ObjcClassPrefix = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								                case "php_class_prefix": PhpClassPrefix = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								                case "swift_prefix": SwiftPrefix = ctx.Tokens.ConsumeString(); return true;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                default: return false;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public byte[] ExtensionData
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get { return DescriptorProto.GetExtensionData(this); }
							 | 
						|||
| 
								 | 
							
								            set { DescriptorProto.SetExtensionData(this, value); }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#pragma warning restore CS1591
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								namespace ProtoBuf.Reflection
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    internal static class ErrorExtensions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public static void Warn(this List<Error> errors, Token token, string message)
							 | 
						|||
| 
								 | 
							
								            => errors.Add(new Error(token, message, false));
							 | 
						|||
| 
								 | 
							
								        public static void Error(this List<Error> errors, Token token, string message)
							 | 
						|||
| 
								 | 
							
								            => errors.Add(new Error(token, message, true));
							 | 
						|||
| 
								 | 
							
								        public static void Error(this List<Error> errors, ParserException ex)
							 | 
						|||
| 
								 | 
							
								            => errors.Add(new Error(ex));
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Describes a generated file
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public class CodeFile
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Get a string representation of this instance
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        /// <returns></returns>
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Name;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Create a new CodeFile instance
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public CodeFile(string name, string text)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            Name = name;
							 | 
						|||
| 
								 | 
							
								            Text = text;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The name (including path if necessary) of this file
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public string Name { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The contents of this file
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public string Text { get; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Represents the overall result of a compilation process
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public class CompilerResult
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal CompilerResult(Error[] errors, CodeFile[] files)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            Errors = errors;
							 | 
						|||
| 
								 | 
							
								            Files = files;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The errors from this execution
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public Error[] Errors { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The output files from this execution
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public CodeFile[] Files { get; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    internal class Import
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Path;
							 | 
						|||
| 
								 | 
							
								        public string Path { get; set; }
							 | 
						|||
| 
								 | 
							
								        public bool IsPublic { get; set; }
							 | 
						|||
| 
								 | 
							
								        public Token Token { get; set; }
							 | 
						|||
| 
								 | 
							
								        public bool Used { get; set; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Describes an error that occurred during processing
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public class Error
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parse an error from a PROTOC error message
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static Error[] Parse(string stdout, string stderr)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (string.IsNullOrWhiteSpace(stdout) && string.IsNullOrWhiteSpace(stderr))
							 | 
						|||
| 
								 | 
							
								                return noErrors;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            List<Error> errors = new List<Error>();
							 | 
						|||
| 
								 | 
							
								            using (var reader = new StringReader(stdout))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Add(reader, errors);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            using (var reader = new StringReader(stderr))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Add(reader, errors);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return errors.ToArray();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        static void Add(TextReader lines, List<Error> errors)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            string line;
							 | 
						|||
| 
								 | 
							
								            while ((line = lines.ReadLine()) != null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var s = line;
							 | 
						|||
| 
								 | 
							
								                bool isError = true;
							 | 
						|||
| 
								 | 
							
								                int lineNumber = 1, columnNumber = 1;
							 | 
						|||
| 
								 | 
							
								                if (s[0] == '[')
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    int i = s.IndexOf(']');
							 | 
						|||
| 
								 | 
							
								                    if (i > 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        var prefix = line.Substring(1, i).Trim();
							 | 
						|||
| 
								 | 
							
								                        s = line.Substring(i + 1).Trim();
							 | 
						|||
| 
								 | 
							
								                        if (prefix.IndexOf("WARNING", StringComparison.OrdinalIgnoreCase) >= 0
							 | 
						|||
| 
								 | 
							
								                            && prefix.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) < 0)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            isError = false;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                var match = Regex.Match(s, @"^([^:]+):([0-9]+):([0-9]+):\s+");
							 | 
						|||
| 
								 | 
							
								                string file = "";
							 | 
						|||
| 
								 | 
							
								                if (match.Success)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    file = match.Groups[1].Value;
							 | 
						|||
| 
								 | 
							
								                    if (!int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out lineNumber))
							 | 
						|||
| 
								 | 
							
								                        lineNumber = 1;
							 | 
						|||
| 
								 | 
							
								                    if (!int.TryParse(match.Groups[3].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out columnNumber))
							 | 
						|||
| 
								 | 
							
								                        columnNumber = 1;
							 | 
						|||
| 
								 | 
							
								                    s = s.Substring(match.Length).Trim();
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                errors.Add(new Error(new Token(" ", lineNumber, columnNumber, TokenType.None, "", 0, file), s, isError));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal string ToString(bool includeType) => Text.Length == 0
							 | 
						|||
| 
								 | 
							
								                ? $"{File}({LineNumber},{ColumnNumber}): {(includeType ? (IsError ? "error: " : "warning: ") : "")}{Message}"
							 | 
						|||
| 
								 | 
							
								                : $"{File}({LineNumber},{ColumnNumber},{LineNumber},{ColumnNumber + Text.Length}): {(includeType ? (IsError ? "error: " : "warning: ") : "")}{Message}";
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Get a text representation of this instance
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        /// <returns></returns>
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => ToString(true);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static Error[] GetArray(List<Error> errors)
							 | 
						|||
| 
								 | 
							
								            => errors.Count == 0 ? noErrors : errors.ToArray();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private static readonly Error[] noErrors = new Error[0];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal Error(Token token, string message, bool isError)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ColumnNumber = token.ColumnNumber;
							 | 
						|||
| 
								 | 
							
								            LineNumber = token.LineNumber;
							 | 
						|||
| 
								 | 
							
								            File = token.File;
							 | 
						|||
| 
								 | 
							
								            LineContents = token.LineContents;
							 | 
						|||
| 
								 | 
							
								            Message = message;
							 | 
						|||
| 
								 | 
							
								            IsError = isError;
							 | 
						|||
| 
								 | 
							
								            Text = token.Value;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal Error(ParserException ex)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ColumnNumber = ex.ColumnNumber;
							 | 
						|||
| 
								 | 
							
								            LineNumber = ex.LineNumber;
							 | 
						|||
| 
								 | 
							
								            File = ex.File;
							 | 
						|||
| 
								 | 
							
								            LineContents = ex.LineContents;
							 | 
						|||
| 
								 | 
							
								            Message = ex.Message;
							 | 
						|||
| 
								 | 
							
								            IsError = ex.IsError;
							 | 
						|||
| 
								 | 
							
								            Text = ex.Text ?? "";
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// True if this instance represents a non-fatal warning
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public bool IsWarning => !IsError;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// True if this instance represents a fatal error
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public bool IsError { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The file in which this error was identified
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public string File { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The source text relating to this error
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public string Text { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The error message
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public string Message { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The entire line contents in the source in which this error was located
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public string LineContents { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The line number in which this error was located
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public int LineNumber { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The column number in which this error was located
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public int ColumnNumber { get; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    enum AbortState
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        None, Statement, Object
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    interface ISchemaOptions
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        List<UninterpretedOption> UninterpretedOptions { get; }
							 | 
						|||
| 
								 | 
							
								        bool Deprecated { get; set; }
							 | 
						|||
| 
								 | 
							
								        bool ReadOne(ParserContext ctx, string key);
							 | 
						|||
| 
								 | 
							
								        byte[] ExtensionData { get; set; }
							 | 
						|||
| 
								 | 
							
								        string Extendee { get; }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    interface IHazNames
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        IEnumerable<string> GetNames();
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    interface ISchemaObject
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        void ReadOne(ParserContext ctx);
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    internal class ParserContext : IDisposable
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public AbortState AbortState { get; set; }
							 | 
						|||
| 
								 | 
							
								        private void ReadOne<T>(T obj) where T : class, ISchemaObject
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            AbortState oldState = AbortState;
							 | 
						|||
| 
								 | 
							
								            AbortState = AbortState.None;
							 | 
						|||
| 
								 | 
							
								            Token stateBefore;
							 | 
						|||
| 
								 | 
							
								            if (!Tokens.Peek(out stateBefore)) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                obj.ReadOne(this);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch (ParserException ex)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Errors.Error(ex);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            finally
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var state = AbortState;
							 | 
						|||
| 
								 | 
							
								                Token stateAfter;
							 | 
						|||
| 
								 | 
							
								                if (Tokens.Peek(out stateAfter) && stateBefore == stateAfter)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    // we didn't move! avoid looping forever failing to do the same thing
							 | 
						|||
| 
								 | 
							
								                    Errors.Error(stateAfter, "unknown error");
							 | 
						|||
| 
								 | 
							
								                    state = stateAfter.Is(TokenType.Symbol, "}")
							 | 
						|||
| 
								 | 
							
								                        ? AbortState.Object : AbortState.Statement;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                AbortState = oldState;
							 | 
						|||
| 
								 | 
							
								                switch (state)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case AbortState.Object:
							 | 
						|||
| 
								 | 
							
								                        Tokens.SkipToEndObject();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case AbortState.Statement:
							 | 
						|||
| 
								 | 
							
								                        Tokens.SkipToEndStatement();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public void Fill<T>(T obj) where T : class, ISchemaObject
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = Tokens;
							 | 
						|||
| 
								 | 
							
								            Token token;
							 | 
						|||
| 
								 | 
							
								            while (tokens.Peek(out token))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (tokens.ConsumeIf(TokenType.Symbol, ";"))
							 | 
						|||
| 
								 | 
							
								                { }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ReadOne(obj);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        static readonly char[] Period = { '.' };
							 | 
						|||
| 
								 | 
							
								        private void ReadOption<T>(ref T obj, ISchemaObject parent, List<UninterpretedOption.NamePart> existingNameParts = null) where T : class, ISchemaOptions, new()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = Tokens;
							 | 
						|||
| 
								 | 
							
								            bool isBlock = existingNameParts != null;
							 | 
						|||
| 
								 | 
							
								            var nameParts = isBlock
							 | 
						|||
| 
								 | 
							
								                ? new List<UninterpretedOption.NamePart>(existingNameParts) // create a clone we can append to
							 | 
						|||
| 
								 | 
							
								                : new List<UninterpretedOption.NamePart>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            do
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (nameParts.Count != 0) tokens.ConsumeIf(TokenType.AlphaNumeric, ".");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                bool isExtension = tokens.ConsumeIf(TokenType.Symbol, isBlock ? "[" : "(");
							 | 
						|||
| 
								 | 
							
								                string key = tokens.Consume(TokenType.AlphaNumeric);
							 | 
						|||
| 
								 | 
							
								                var keyToken = tokens.Previous;
							 | 
						|||
| 
								 | 
							
								                if (isExtension) tokens.Consume(TokenType.Symbol, isBlock ? "]" : ")");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if (!isExtension && key.StartsWith("."))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    key = key.TrimStart(Period);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                key = key.Trim();
							 | 
						|||
| 
								 | 
							
								                if (isExtension || nameParts.Count == 0 || key.IndexOf('.') < 0)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var name = new UninterpretedOption.NamePart { IsExtension = isExtension, name_part = key, Token = keyToken };
							 | 
						|||
| 
								 | 
							
								                    nameParts.Add(name);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    foreach (var part in key.Split(Period, StringSplitOptions.RemoveEmptyEntries))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        var name = new UninterpretedOption.NamePart { IsExtension = false, name_part = part, Token = keyToken };
							 | 
						|||
| 
								 | 
							
								                        nameParts.Add(name);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            } while (!(
							 | 
						|||
| 
								 | 
							
								            (isBlock && tokens.Is(TokenType.Symbol, "{"))
							 | 
						|||
| 
								 | 
							
								            || tokens.ConsumeIf(TokenType.Symbol, isBlock ? ":" : "=")));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (tokens.ConsumeIf(TokenType.Symbol, "{"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (obj == null) obj = new T();
							 | 
						|||
| 
								 | 
							
								                bool any = false;
							 | 
						|||
| 
								 | 
							
								                while (!tokens.ConsumeIf(TokenType.Symbol, "}"))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ReadOption(ref obj, parent, nameParts);
							 | 
						|||
| 
								 | 
							
								                    any = true;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                if (!any)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var newOption = new UninterpretedOption();
							 | 
						|||
| 
								 | 
							
								                    newOption.Names.AddRange(nameParts);
							 | 
						|||
| 
								 | 
							
								                    obj.UninterpretedOptions.Add(newOption);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                var field = parent as FieldDescriptorProto;
							 | 
						|||
| 
								 | 
							
								                bool isField = typeof(T) == typeof(FieldOptions) && field != null;
							 | 
						|||
| 
								 | 
							
								                var singleKey = (nameParts.Count == 1 && !nameParts[0].IsExtension) ? nameParts[0].name_part : null;
							 | 
						|||
| 
								 | 
							
								                if (singleKey == "default" && isField)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    string defaultValue = tokens.ConsumeString(field.type == FieldDescriptorProto.Type.TypeBytes);
							 | 
						|||
| 
								 | 
							
								                    nameParts[0].Token.RequireProto2(this);
							 | 
						|||
| 
								 | 
							
								                    ParseDefault(tokens.Previous, field.type, ref defaultValue);
							 | 
						|||
| 
								 | 
							
								                    if (defaultValue != null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        field.DefaultValue = defaultValue;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else if (singleKey == "json_name" && isField)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    string jsonName = tokens.ConsumeString();
							 | 
						|||
| 
								 | 
							
								                    field.JsonName = jsonName;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (obj == null) obj = new T();
							 | 
						|||
| 
								 | 
							
								                    if (singleKey == "deprecated")
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        obj.Deprecated = tokens.ConsumeBoolean();
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (singleKey == null || !obj.ReadOne(this, singleKey))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        var newOption = new UninterpretedOption
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            AggregateValue = tokens.ConsumeString(),
							 | 
						|||
| 
								 | 
							
								                            Token = tokens.Previous
							 | 
						|||
| 
								 | 
							
								                        };
							 | 
						|||
| 
								 | 
							
								                        newOption.Names.AddRange(nameParts);
							 | 
						|||
| 
								 | 
							
								                        obj.UninterpretedOptions.Add(newOption);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ParseDefault(Token token, FieldDescriptorProto.Type type, ref string defaultValue)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            switch (type)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeBool:
							 | 
						|||
| 
								 | 
							
								                    switch (defaultValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        case "true":
							 | 
						|||
| 
								 | 
							
								                        case "false":
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                        default:
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(token, "expected 'true' or 'false'");
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeDouble:
							 | 
						|||
| 
								 | 
							
								                    switch (defaultValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        case "inf":
							 | 
						|||
| 
								 | 
							
								                        case "-inf":
							 | 
						|||
| 
								 | 
							
								                        case "nan":
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                        default:
							 | 
						|||
| 
								 | 
							
								                            double val;
							 | 
						|||
| 
								 | 
							
								                            if (TokenExtensions.TryParseDouble(defaultValue, out val))
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                defaultValue = Format(val);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            else
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                Errors.Error(token, "invalid floating-point number");
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeFloat:
							 | 
						|||
| 
								 | 
							
								                    switch (defaultValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        case "inf":
							 | 
						|||
| 
								 | 
							
								                        case "-inf":
							 | 
						|||
| 
								 | 
							
								                        case "nan":
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                        default:
							 | 
						|||
| 
								 | 
							
								                            float val;
							 | 
						|||
| 
								 | 
							
								                            if (TokenExtensions.TryParseSingle(defaultValue, out val))
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                defaultValue = Format(val);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            else
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                Errors.Error(token, "invalid floating-point number");
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeSfixed32:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeInt32:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeSint32:
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        int val;
							 | 
						|||
| 
								 | 
							
								                        if (TokenExtensions.TryParseInt32(defaultValue, out val))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            defaultValue = val.ToString(CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(token, "invalid integer");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeFixed32:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeUint32:
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        uint val;
							 | 
						|||
| 
								 | 
							
								                        if (TokenExtensions.TryParseUInt32(defaultValue, out val))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            defaultValue = val.ToString(CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(token, "invalid unsigned integer");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeSfixed64:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeInt64:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeSint64:
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        long val;
							 | 
						|||
| 
								 | 
							
								                        if (TokenExtensions.TryParseInt64(defaultValue, out val))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            defaultValue = val.ToString(CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(token, "invalid integer");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeFixed64:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeUint64:
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ulong val;
							 | 
						|||
| 
								 | 
							
								                        if (TokenExtensions.TryParseUInt64(defaultValue, out val))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            defaultValue = val.ToString(CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            Errors.Error(token, "invalid unsigned integer");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                case 0:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeBytes:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeString:
							 | 
						|||
| 
								 | 
							
								                case FieldDescriptorProto.Type.TypeEnum:
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    Errors.Error(token, $"default value not handled: {type}={defaultValue}");
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static readonly char[] ExponentChars = { 'e', 'E' };
							 | 
						|||
| 
								 | 
							
								        static readonly string[] ExponentFormats = { "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "e10" };
							 | 
						|||
| 
								 | 
							
								        static string Format(float val)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            string s = val.ToString(CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								            if (s.IndexOfAny(ExponentChars) < 0) return s;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            foreach (var format in ExponentFormats)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var tmp = val.ToString(format, CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								                float x;
							 | 
						|||
| 
								 | 
							
								                if (float.TryParse(tmp, NumberStyles.Any, CultureInfo.InvariantCulture, out x) && x == val) return tmp;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return val.ToString("e", CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static string Format(double val)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            string s = val.ToString(CultureInfo.InvariantCulture).ToUpperInvariant();
							 | 
						|||
| 
								 | 
							
								            if (s.IndexOfAny(ExponentChars) < 0) return s;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            foreach (var format in ExponentFormats)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var tmp = val.ToString(format, CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								                double x;
							 | 
						|||
| 
								 | 
							
								                if (double.TryParse(tmp, NumberStyles.Any, CultureInfo.InvariantCulture, out x) && x == val) return tmp;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return val.ToString("e", CultureInfo.InvariantCulture);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public T ParseOptionBlock<T>(T obj, ISchemaObject parent = null) where T : class, ISchemaOptions, new()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = Tokens;
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                while (true)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (tokens.ConsumeIf(TokenType.Symbol, "]"))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (tokens.ConsumeIf(TokenType.Symbol, ","))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ReadOption(ref obj, parent);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch (ParserException ex)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Errors.Error(ex);
							 | 
						|||
| 
								 | 
							
								                tokens.SkipToEndOptions();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return obj;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public T ParseOptionStatement<T>(T obj, ISchemaObject parent) where T : class, ISchemaOptions, new()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = Tokens;
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ReadOption(ref obj, parent);
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, ";");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch (ParserException ex)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Errors.Error(ex);
							 | 
						|||
| 
								 | 
							
								                tokens.SkipToEndStatement();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return obj;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public bool TryReadObject<T>(out T obj) where T : class, ISchemaObject, new()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            obj = new T();
							 | 
						|||
| 
								 | 
							
								            return TryReadObjectImpl(obj);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        internal bool TryReadObjectImpl<T>(T obj) where T : class, ISchemaObject
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var tokens = Tokens;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, "{");
							 | 
						|||
| 
								 | 
							
								                Token token;
							 | 
						|||
| 
								 | 
							
								                while (tokens.Peek(out token) && !token.Is(TokenType.Symbol, "}"))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (tokens.ConsumeIf(TokenType.Symbol, ";"))
							 | 
						|||
| 
								 | 
							
								                    { }
							 | 
						|||
| 
								 | 
							
								                    else
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ReadOne(obj);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                tokens.Consume(TokenType.Symbol, "}");
							 | 
						|||
| 
								 | 
							
								                return true;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch (ParserException ex)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Errors.Error(ex);
							 | 
						|||
| 
								 | 
							
								                tokens.SkipToEndObject();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            obj = null;
							 | 
						|||
| 
								 | 
							
								            return false;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public ParserContext(FileDescriptorProto file, Peekable<Token> tokens, List<Error> errors)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            Tokens = tokens;
							 | 
						|||
| 
								 | 
							
								            Errors = errors;
							 | 
						|||
| 
								 | 
							
								            _file = file;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public string Syntax
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var syntax = _file.Syntax;
							 | 
						|||
| 
								 | 
							
								                return string.IsNullOrEmpty(syntax) ? FileDescriptorProto.SyntaxProto2 : syntax;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private readonly FileDescriptorProto _file;
							 | 
						|||
| 
								 | 
							
								        public Peekable<Token> Tokens { get; }
							 | 
						|||
| 
								 | 
							
								        public List<Error> Errors { get; }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void Dispose() { Tokens?.Dispose(); }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal void CheckNames(IHazNames parent, string name, Token token
							 | 
						|||
| 
								 | 
							
								#if DEBUG && NETSTANDARD1_3
							 | 
						|||
| 
								 | 
							
								            , [System.Runtime.CompilerServices.CallerMemberName] string caller = null
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								            )
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (parent != null && parent.GetNames().Contains(name))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Errors.Error(token, $"name '{name}' is already in use"
							 | 
						|||
| 
								 | 
							
								#if DEBUG && NETSTANDARD1_3
							 | 
						|||
| 
								 | 
							
								             + $" ({caller})"
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								                    );
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 |