// Attempts to end the current block header
        static private bool TryEndHeader(ref ParseState ioState, StringSlice inLine)
        {
            TagData data = TagData.Parse(inLine, ioState.TagDelimiters);

            switch (ioState.CurrentState)
            {
            case BlockState.NotStarted:
            case BlockState.BlockDone:
            {
                BlockParser.LogError(ioState.Position, "Cannot end block header with '{0}', not currently in block", inLine);
                return(false);
            }

            case BlockState.InData:
            {
                BlockParser.LogError(ioState.Position, "Cannot end block header with '{0}', already in data section", inLine);
                return(false);
            }

            case BlockState.BlockStarted:
            case BlockState.InHeader:
            default:
            {
                ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, data);
                ioState.CurrentState = BlockState.InData;
                return(true);
            }
            }
        }
        public IEnumerator ParseAsync <TPackage>(IBlockParsingRules inRules, IBlockGenerator <TBlock, TPackage> inGenerator, BlockMetaCache inCache = null)
            where TPackage : ScriptableDataBlockPackage <TBlock>
        {
            if (m_Parsed)
            {
                return(null);
            }

            TPackage self = (TPackage)this;

            return(BlockParser.ParseAsync(ref self, name, Source(), inRules, inGenerator, inCache));
        }
        public void Parse <TPackage>(IBlockGenerator <TBlock, TPackage> inGenerator, BlockMetaCache inCache = null)
            where TPackage : ScriptableDataBlockPackage <TBlock>
        {
            if (m_Parsed)
            {
                return;
            }

            TPackage self = (TPackage)this;

            BlockParser.Parse(ref self, name, Source(), BlockParsingRules.Default, inGenerator, inCache);
        }
        // Attempts to add a comment
        static private bool TryAddComment(ref ParseState ioState, StringSlice inComment)
        {
            if (inComment.IsEmpty)
            {
                return(true);
            }

            if (!ioState.Generator.TryAddComment(ioState, ioState.Package, ioState.CurrentBlock, inComment))
            {
                BlockParser.LogError(ioState.Position, "Unhandled comment");
            }

            return(true);
        }
        // Attempts to evaluate metadata with the current block header
        static private bool TryEvaluateMeta(ref ParseState ioState, StringSlice inLine)
        {
            TagData data = TagData.Parse(inLine, ioState.TagDelimiters);

            switch (ioState.CurrentState)
            {
            case BlockState.NotStarted:
            case BlockState.BlockDone:
            {
                BlockParser.LogError(ioState.Position, "Cannot add metadata '{0}' to block, not currently in block", inLine);
                return(false);
            }

            case BlockState.InData:
            {
                BlockParser.LogError(ioState.Position, "Cannot add metadata '{0}' to block, not currently in header section", inLine);
                return(false);
            }

            case BlockState.InHeader:
                break;

            case BlockState.BlockStarted:
            {
                ioState.CurrentState = BlockState.InHeader;
                break;
            }
            }

            if (!data.IsEmpty())
            {
                bool bHandled = ioState.Generator.TryEvaluateMeta(ioState, ioState.Package, ioState.CurrentBlock, data);
                if (!bHandled)
                {
                    bHandled = ioState.Cache.TryEvaluateCommand(ioState.CurrentBlock, data);
                }
                if (!bHandled)
                {
                    BlockParser.LogError(ioState.Position, "Unrecognized block metadata '{0}'", data);
                }
                return(bHandled);
            }

            return(false);
        }
        static internal IEnumerator ParseFile(string inFileName, StringSlice inFile, TPackage ioPackage, IBlockParsingRules inRules, IBlockGenerator <TBlock, TPackage> inGenerator, BlockMetaCache inCache)
        {
            var state = new ParseState();

            state.PrefixPriorities = GeneratePrefixPriority(inRules);
            state.Rules            = inRules;
            state.Generator        = inGenerator;
            state.TagDelimiters    = new BlockParser.BlockTagDelimiters(inRules);
            state.Package          = ioPackage;
            state.Builder          = BlockParser.RentStringBuilder();
            state.ContentBuilder   = BlockParser.RentStringBuilder();
            state.Cache            = inCache ?? BlockMetaCache.Default;

            using (var disposeRef = state)
            {
                uint lineNumber = 0;
                state.Position = new BlockFilePosition(inFileName, lineNumber);
                inGenerator.OnStart(state, state.Package);

                foreach (var rawLine in SplitIntoLines(inRules, inFile))
                {
                    lineNumber++;
                    state.Position = new BlockFilePosition(inFileName, lineNumber);

                    LineResult result = ParseLine(ref state, rawLine);
                    if (result == LineResult.Exception)
                    {
                        inGenerator.OnEnd(state, state.Package, true);
                        yield break;
                    }

                    yield return(null);
                }

                if (state.CurrentBlock != null)
                {
                    state.Error |= !TryEndBlock(ref state, StringSlice.Empty);
                }

                inGenerator.OnEnd(state, state.Package, state.Error);
            }
        }
        // Attempts to end the current block
        static private bool TryEndBlock(ref ParseState ioState, StringSlice inLine)
        {
            TagData data = TagData.Parse(inLine, ioState.TagDelimiters);

            switch (ioState.CurrentState)
            {
            case BlockState.NotStarted:
            case BlockState.BlockDone:
            {
                BlockParser.LogError(ioState.Position, "Cannot close block with '{0}', not currently in block", inLine);
                return(false);
            }

            case BlockState.BlockStarted:
            {
                ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                break;
            }

            case BlockState.InData:
                break;

            case BlockState.InHeader:
            {
                if (ioState.Rules.RequireExplicitBlockHeaderEnd)
                {
                    BlockParser.LogError(ioState.Position, "Cannot close block with '{0}', header section not closed", inLine);
                    return(false);
                }

                ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                break;
            }
            }

            FlushBlock(ref ioState, data);
            ioState.CurrentState = BlockState.BlockDone;
            ioState.CurrentBlock = null;
            return(true);
        }
            public void Dispose()
            {
                if (Builder != null)
                {
                    BlockParser.ReturnStringBuilder(Builder);
                    Builder = null;
                }

                if (ContentBuilder != null)
                {
                    BlockParser.ReturnStringBuilder(ContentBuilder);
                    ContentBuilder = null;
                }

                PrefixPriorities = null;
                Rules            = null;
                Generator        = null;
                TagDelimiters    = null;
                Cache            = null;

                Package      = null;
                CurrentBlock = null;
            }
        // Attempts to add package metadata
        static private bool TryEvaluatePackage(ref ParseState ioState, StringSlice inLine)
        {
            TagData data = TagData.Parse(inLine, ioState.TagDelimiters);

            if (ioState.Rules.PackageMetaMode == PackageMetaMode.DisallowInBlock)
            {
                if (ioState.CurrentState != BlockState.NotStarted && ioState.CurrentState != BlockState.BlockDone)
                {
                    BlockParser.LogError(ioState.Position, "Cannot set package metadata '{0}', currently in a block", inLine);
                    return(false);
                }
            }

            if (ioState.Rules.PackageMetaMode == PackageMetaMode.ImplicitCloseBlock)
            {
                switch (ioState.CurrentState)
                {
                case BlockState.NotStarted:
                case BlockState.BlockDone:
                    break;

                case BlockState.InHeader:
                {
                    if (ioState.Rules.RequireExplicitBlockEnd || ioState.Rules.RequireExplicitBlockHeaderEnd)
                    {
                        BlockParser.LogError(ioState.Position, "Cannot close a block with meta command '{0}' before previous block is closed", inLine);
                        return(false);
                    }

                    ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                    FlushBlock(ref ioState, TagData.Empty);
                    break;
                }

                case BlockState.BlockStarted:
                case BlockState.InData:
                {
                    if (ioState.Rules.RequireExplicitBlockHeaderEnd)
                    {
                        BlockParser.LogError(ioState.Position, "Cannot close a block with meta command '{0}' before previous block is closed", inLine);
                        return(false);
                    }

                    if (ioState.CurrentState == BlockState.BlockStarted)
                    {
                        ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                    }
                    FlushBlock(ref ioState, TagData.Empty);
                    break;
                }
                }
            }

            if (!data.IsEmpty())
            {
                bool bHandled = ioState.Generator.TryEvaluatePackage(ioState, ioState.Package, ioState.CurrentBlock, data);
                if (!bHandled)
                {
                    bHandled = ioState.Cache.TryEvaluateCommand(ioState.Package, data);
                }
                if (!bHandled)
                {
                    BlockParser.LogError(ioState.Position, "Unrecognized package metadata '{0}'", data);
                }
                return(bHandled);
            }

            return(false);
        }
        // Attempts to add content to the current block
        static private bool TryAddContent(ref ParseState ioState, StringSlice inContent)
        {
            switch (ioState.CurrentState)
            {
            case BlockState.NotStarted:
            case BlockState.BlockDone:
            {
                BlockParser.LogError(ioState.Position, "Cannot add content '{0}', not currently in block", inContent);
                return(false);
            }

            case BlockState.BlockStarted:
            {
                ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                ioState.CurrentState = BlockState.InData;
                break;
            }

            case BlockState.InHeader:
            {
                if (ioState.Rules.RequireExplicitBlockHeaderEnd)
                {
                    BlockParser.LogError(ioState.Position, "Cannot add content '{0}', block header is not exited", inContent);
                    return(false);
                }

                ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                ioState.CurrentState = BlockState.InData;
                break;
            }

            case BlockState.InData:
                break;
            }

            bool bHandled = ioState.Generator.TryAddContent(ioState, ioState.Package, ioState.CurrentBlock, inContent);

            if (!bHandled)
            {
                BlockMetaCache.ContentInfo contentSetter;
                if (ioState.Cache.TryGetContent(ioState.CurrentBlock, out contentSetter))
                {
                    if (contentSetter.Mode == BlockContentMode.LineByLine)
                    {
                        StringSlice contentString = inContent;
                        if (contentString.Contains('\\'))
                        {
                            contentString = inContent.Unescape();
                        }

                        bHandled = contentSetter.Invoke(ioState.CurrentBlock, contentString, ioState.Cache.SharedResources);
                    }
                    else
                    {
                        if (ioState.ContentBuilder.Length == 0)
                        {
                            inContent = inContent.TrimStart(BlockParser.TrimCharsWithSpace);
                        }
                        else if (ioState.ContentBuilder.Length > 0 && contentSetter.LineSeparator != 0)
                        {
                            ioState.ContentBuilder.Append(contentSetter.LineSeparator);
                        }
                        ioState.ContentBuilder.AppendSlice(inContent);
                        bHandled = true;
                    }
                }
            }

            if (!bHandled)
            {
                BlockParser.LogError(ioState.Position, "Unable to add content");
            }

            return(bHandled);
        }
        // Attempts to start a new data block
        static private bool TryStartBlock(ref ParseState ioState, StringSlice inLine)
        {
            TagData data = TagData.Parse(inLine, ioState.TagDelimiters);

            if (data.IsEmpty())
            {
                BlockParser.LogError(ioState.Position, "Cannot start block with an empty id '{0}'", inLine);
                return(false);
            }

            switch (ioState.CurrentState)
            {
            case BlockState.NotStarted:
            {
                ioState.Generator.OnBlocksStart(ioState, ioState.Package);
                break;
            }

            case BlockState.InHeader:
            {
                if (ioState.Rules.RequireExplicitBlockEnd || ioState.Rules.RequireExplicitBlockHeaderEnd)
                {
                    BlockParser.LogError(ioState.Position, "Cannot start a new block '{0}' before previous block is closed", inLine);
                    return(false);
                }

                ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                FlushBlock(ref ioState, TagData.Empty);
                break;
            }

            case BlockState.BlockStarted:
            case BlockState.InData:
            {
                if (ioState.Rules.RequireExplicitBlockHeaderEnd)
                {
                    BlockParser.LogError(ioState.Position, "Cannot start a new block '{0}' before previous block is closed", inLine);
                    return(false);
                }

                if (ioState.CurrentState == BlockState.BlockStarted)
                {
                    ioState.Generator.CompleteHeader(ioState, ioState.Package, ioState.CurrentBlock, TagData.Empty);
                }
                FlushBlock(ref ioState, TagData.Empty);
                break;
            }

            case BlockState.BlockDone:
                break;
            }

            TBlock newBlock;

            if (!ioState.Generator.TryCreateBlock(ioState, ioState.Package, data, out newBlock))
            {
                BlockParser.LogError(ioState.Position, "Failed to create a new block '{0}'", inLine);
                ioState.CurrentBlock = null;
                ioState.CurrentState = BlockState.BlockDone;
                return(false);
            }

            ioState.CurrentBlock = newBlock;
            ioState.CurrentState = BlockState.BlockStarted;
            ioState.BlockError   = false;
            return(true);
        }
        static private LineResult ParseLine(ref ParseState ioState, StringSlice ioLine)
        {
            StringSlice lineContents = ioLine.TrimStart(BlockParser.TrimCharsWithSpace).TrimEnd(BlockParser.TrimCharsWithoutSpace);
            StringSlice lineComment  = StringSlice.Empty;

            if (lineContents.IsEmpty)
            {
                if (ioState.CurrentState != BlockState.InData)
                {
                    return(LineResult.Empty);
                }
            }

            int commentIdx = lineContents.IndexOf(ioState.Rules.CommentPrefix);

            if (commentIdx >= 0)
            {
                lineComment  = lineContents.Substring(commentIdx + ioState.Rules.CommentPrefix.Length).TrimStart(BlockParser.TrimCharsWithSpace);
                lineContents = lineContents.Substring(0, commentIdx).TrimEnd(BlockParser.TrimCharsWithSpace);
            }

            try
            {
                bool bSuccess          = true;
                bool bProcessedCommand = false;

                if (!lineContents.IsEmpty)
                {
                    for (int i = 0; i < ioState.PrefixPriorities.Length && !bProcessedCommand; ++i)
                    {
                        PrefixType type = ioState.PrefixPriorities[i];
                        switch (type)
                        {
                        case PrefixType.BlockId:
                        {
                            if (lineContents.StartsWith(ioState.Rules.BlockIdPrefix))
                            {
                                lineContents      = lineContents.Substring(ioState.Rules.BlockIdPrefix.Length);
                                bSuccess         &= TryStartBlock(ref ioState, lineContents);
                                bProcessedCommand = true;
                            }
                            break;
                        }

                        case PrefixType.BlockMeta:
                        {
                            if (ShouldCheckMeta(ref ioState) && lineContents.StartsWith(ioState.Rules.BlockMetaPrefix))
                            {
                                lineContents      = lineContents.Substring(ioState.Rules.BlockMetaPrefix.Length);
                                bSuccess         &= TryEvaluateMeta(ref ioState, lineContents);
                                bProcessedCommand = true;
                            }
                            break;
                        }

                        case PrefixType.BlockHeaderEnd:
                        {
                            if (lineContents.StartsWith(ioState.Rules.BlockHeaderEndPrefix))
                            {
                                lineContents      = lineContents.Substring(ioState.Rules.BlockHeaderEndPrefix.Length);
                                bSuccess         &= TryEndHeader(ref ioState, lineContents);
                                bProcessedCommand = true;
                            }
                            break;
                        }

                        case PrefixType.BlockContent:
                        {
                            if (lineContents.StartsWith(ioState.Rules.BlockContentPrefix))
                            {
                                lineContents      = lineContents.Substring(ioState.Rules.BlockContentPrefix.Length);
                                bSuccess         &= TryAddContent(ref ioState, lineContents);
                                bProcessedCommand = true;
                            }
                            break;
                        }

                        case PrefixType.BlockEnd:
                        {
                            if (lineContents.StartsWith(ioState.Rules.BlockEndPrefix))
                            {
                                lineContents      = lineContents.Substring(ioState.Rules.BlockEndPrefix.Length);
                                bSuccess         &= TryEndBlock(ref ioState, lineContents);
                                bProcessedCommand = true;
                            }
                            break;
                        }

                        case PrefixType.PackageMeta:
                        {
                            if (ShouldCheckPackageMeta(ref ioState) && lineContents.StartsWith(ioState.Rules.PackageMetaPrefix))
                            {
                                lineContents      = lineContents.Substring(ioState.Rules.PackageMetaPrefix.Length);
                                bSuccess         &= TryEvaluatePackage(ref ioState, lineContents);
                                bProcessedCommand = true;
                            }
                            break;
                        }
                        }
                    }
                }

                if (!bProcessedCommand)
                {
                    if (ioState.CurrentState == BlockState.InData && ioState.Rules.RequireExplicitBlockContent && !string.IsNullOrEmpty(ioState.Rules.BlockContentPrefix))
                    {
                        BlockParser.LogError(ioState.Position, "Cannot add content '{0}', must have content prefix '{1}'", lineContents, ioState.Rules.BlockContentPrefix);
                        bSuccess = false;
                    }
                    else if (!lineContents.IsEmpty || ioState.CurrentState == BlockState.InData)
                    {
                        bSuccess &= TryAddContent(ref ioState, lineContents);
                    }
                }

                if (!lineComment.IsEmpty)
                {
                    bSuccess &= TryAddComment(ref ioState, lineComment);
                }

                ioState.Error |= !bSuccess;
                return(bSuccess ? LineResult.Error : LineResult.NoError);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogException(e);
                return(LineResult.Exception);
            }
        }