private void OptionallyConsume(ProjectLexer lexer, TokenKind kind)
 {
     if (lexer.Token.Kind == kind)
     {
         lexer.Advance();
     }
 }
        private void ParseMap(ProjectDefinition definition, ProjectLexer lexer)
        {
            var map = new ProjectMap();

            definition.Maps.Add(map);

            lexer.Consume(TokenKind.LeftCurly);

            while (lexer.Token.Kind == TokenKind.Keyword)
            {
                var keyword = lexer.Consume(TokenKind.Keyword);

                switch (keyword)
                {
                case "path":
                    map.Path = ConsumeFile(lexer);
                    OptionallyConsume(lexer, TokenKind.Semicolon);
                    break;

                default:
                    throw new ParserException($"Unexpected map keyword: {keyword}.", lexer.Token);
                }
            }

            lexer.Consume(TokenKind.RightCurly);
        }
        private void ParseSamplePopulator(ProjectModel model, ProjectLexer lexer)
        {
            var populator = new SampleProjectModelPopulator
            {
                Type = ProjectModelPopulatorType.Sample,
                Id   = lexer.Consume(TokenKind.Identifier)
            };

            model.Populators.Add(populator);

            lexer.Consume(TokenKind.LeftCurly);

            while (lexer.Token.Kind == TokenKind.Keyword)
            {
                var keyword = lexer.Consume(TokenKind.Keyword);

                switch (keyword)
                {
                case "files":
                    populator.Files.AddRange(ConsumeWildcardFiles(lexer));
                    OptionallyConsume(lexer, TokenKind.Semicolon);
                    break;

                case "zip-mask":
                    populator.ZipMask = lexer.Consume(TokenKind.String);
                    OptionallyConsume(lexer, TokenKind.Semicolon);
                    break;
                }
            }

            lexer.Consume(TokenKind.RightCurly);
        }
        private void ParseXsdPopulator(ProjectModel model, ProjectLexer lexer)
        {
            var populator = new XsdProjectModelPopulator
            {
                Type = ProjectModelPopulatorType.Xsd
            };

            model.Populators.Add(populator);

            lexer.Consume(TokenKind.LeftCurly);

            while (lexer.Token.Kind == TokenKind.Keyword)
            {
                var keyword = lexer.Consume(TokenKind.Keyword);

                switch (keyword)
                {
                case "path":
                    populator.Path = ConsumeFile(lexer);
                    OptionallyConsume(lexer, TokenKind.Semicolon);
                    break;

                default:
                    throw new ParserException($"Unexpected populator keyword: {keyword}.", lexer.Token);
                }
            }

            lexer.Consume(TokenKind.RightCurly);
        }
        private IEnumerable <SampleInputFile> ConsumeWildcardFiles(ProjectLexer lexer)
        {
            var mask = lexer.Consume(TokenKind.String);

            var dirPath   = Path.Combine(Path.GetDirectoryName(lexer.FilePath), Path.GetDirectoryName(mask));
            var directory = new DirectoryInfo(dirPath);
            var filemask  = Path.GetFileName(mask);

            foreach (var info in directory.GetFiles(filemask).OrderBy(x => x.Name))
            {
                yield return(new SampleInputFile
                {
                    Path = info.FullName,
                    LastWriteUtc = info.LastWriteTimeUtc
                });
            }
        }
        private void ParseModel(ProjectDefinition definition, ProjectLexer lexer)
        {
            var model = new ProjectModel();

            definition.Models.Add(model);

            model.Id = lexer.Consume(TokenKind.Identifier);

            lexer.Consume(TokenKind.LeftCurly);

            while (lexer.Token.Kind == TokenKind.Keyword)
            {
                var keyword = lexer.Consume(TokenKind.Keyword);

                switch (keyword)
                {
                case "csv":
                    ParseCsvPopulator(model, lexer);
                    break;

                case "name":
                    model.Name = lexer.Consume(TokenKind.String);
                    OptionallyConsume(lexer, TokenKind.Semicolon);
                    break;

                case "samples":
                    ParseSamplePopulator(model, lexer);
                    break;

                case "xsd":
                    ParseXsdPopulator(model, lexer);
                    break;

                default:
                    throw new ParserException($"Unexpected model keyword: {keyword}.", lexer.Token);
                }
            }

            lexer.Consume(TokenKind.RightCurly);
        }
        private void ParseStatement(ProjectDefinition definition, ProjectLexer lexer)
        {
            lexer.VerifyToken(TokenKind.Keyword);

            var text = lexer.Token.Text;

            lexer.Advance();

            switch (text)
            {
            case "map":
                ParseMap(definition, lexer);
                break;

            case "model":
                ParseModel(definition, lexer);
                break;

            default:
                throw new ParserException($"Unexpected statement keyword: {lexer.Token.Text}.", lexer.Token);
            }
        }
        private string ConsumeFile(ProjectLexer lexer)
        {
            var fragment = lexer.Consume(TokenKind.String);

            return(Path.Combine(Path.GetDirectoryName(lexer.FilePath), fragment));
        }