Пример #1
0
        public async Task <ExpressiveMapList> ParseAsync(string path, IEnumerable <DataModel> models)
        {
            // Parse the content
            var context = new MapParserContext(path, models);

            using (var lexer = lexerFactory.CreateMapLexer(path))
            {
                // Move to the first token
                lexer.Advance();

                // Keep parsing until nothing is left.
                while (lexer.Token.Kind != TokenKind.EOF)
                {
                    ParseStatement(context, lexer);
                }
            }

            // Make sure we have a name
            if (string.IsNullOrEmpty(context.MapList.Name))
            {
                // TODO - need file name/line number here!
                throw new ParserException("Map must have a name.");
            }

            // Load the examples
            await exampleLoader.LoadAsync(context);

            // Return what we've got
            return(context.MapList);
        }
Пример #2
0
        private void ParseArgument(MapLexer lexer, MapParserContext context, MappedDataExpression expression)
        {
            // We should have an identifier...
            // TODO - literals
            var ident = CompoundIdentifier.Parse(lexer);

            // The identifier is either an attribute or a model...
            if (ident.HasPrefix)
            {
                expression.Arguments.Add(new MappedDataArgument(context.Resolve(ident)));
            }
            else
            {
                // A model...look it up and add it...
                var modelId = ident.Parts.First();
                var model   = context.Models.FirstOrDefault(x => x.Id == modelId);

                if (model == null)
                {
                    throw new ParserException(ident, $"Model '{modelId}' not found.");
                }

                expression.Arguments.Add(new MappedDataArgument(model));
            }
        }
Пример #3
0
        private void ParseName(MapParserContext context, MapLexer lexer)
        {
            lexer.Consume(TokenKind.Keyword, "name");

            context.MapList.Name = lexer.Consume(TokenKind.String);

            lexer.Consume(TokenKind.Semicolon);
        }
Пример #4
0
        private void ParseMappingExtras(MapParserContext context, MapLexer lexer, ExpressiveMapping mapping)
        {
            // Consume the preamble
            lexer.Consume(TokenKind.LeftCurly);
            lexer.Consume(TokenKind.Keyword, "note");

            // Grab the note, and attach it to the mapping
            var note = lexer.Consume(TokenKind.String);

            mapping.Notes.Add(note);

            // Consume the postamble
            lexer.Consume(TokenKind.Semicolon);
            lexer.Consume(TokenKind.RightCurly);
        }
Пример #5
0
        private void ParseIgnore(MapParserContext context, MapLexer lexer)
        {
            lexer.Consume(TokenKind.Keyword, "ignore");
            lexer.Consume(TokenKind.LeftCurly);

            while (lexer.Token.Kind == TokenKind.Identifier || lexer.Token.Kind == TokenKind.Keyword)
            {
                var ignoredId        = CompoundIdentifier.Parse(lexer);
                var ignoredAttribute = context.Resolve(ignoredId);

                context.MapList.AddIgnored(ignoredAttribute);

                lexer.Consume(TokenKind.Semicolon);
            }

            lexer.Consume(TokenKind.RightCurly);
        }
Пример #6
0
        public async Task LoadAsync(MapParserContext context)
        {
            var mapDir = Path.GetDirectoryName(context.FilePath);

            // Go through all the examples
            foreach (var def in context.Examples)
            {
                foreach (var model in def.Models)
                {
                    // Find all the attributes from this model within the mapping
                    var exampleAttributes = FindAttributesForModel(context.MapList, model.ModelId);

                    // Process each directory where examples are located
                    foreach (var dir in model.Directories)
                    {
                        var dirPath   = Path.Combine(mapDir, dir);
                        var directory = new DirectoryInfo(dirPath);

                        foreach (var fileInfo in directory.GetFiles())
                        {
                            var exampleId = Path.GetFileNameWithoutExtension(fileInfo.Name);

                            logger.LogDebug("Loading {Model} sample {Id}...", model.ModelId, exampleId);

                            var builder = new StringBuilder();

                            builder.Append(model.Prefix);

                            using (var reader = fileInfo.OpenText())
                            {
                                builder.Append(await reader.ReadToEndAsync());
                            }

                            builder.Append(model.Suffix);

                            xmlLoader.LoadExamples(model.ModelId, builder.ToString(), exampleAttributes, exampleId);
                        }
                    }
                }
            }
        }
Пример #7
0
        private void ParseFunctionCall(MapLexer lexer, MapParserContext context, MappedDataExpression expression)
        {
            // We're sitting on the function name...
            expression.FunctionName = lexer.Consume(TokenKind.Identifier);

            // Start of the argument list
            lexer.Consume(TokenKind.LeftParen);

            // Parse an argument
            while (true)
            {
                ParseArgument(lexer, context, expression);

                if (lexer.Token.Kind != TokenKind.Comma)
                {
                    break;
                }

                lexer.Consume(TokenKind.Comma);
            }

            // End of the argument list
            lexer.Consume(TokenKind.RightParen);
        }
Пример #8
0
        public MappedDataExpression Parse(MapLexer lexer, MapParserContext context)
        {
            var expression = new MappedDataExpression();

            // Regardless, we should always start with an identifier. This means that functions
            // are not keywords.
            lexer.VerifyToken(TokenKind.Identifier);

            // If the identifier is a function name, parse a function call...
            if (functions.Contains(lexer.Token.Text))
            {
                ParseFunctionCall(lexer, context, expression);
            }
            else
            {
                // We don't have a function call, it must be an identifier. Parse it...
                var ident    = CompoundIdentifier.Parse(lexer);
                var resolved = context.Resolve(ident);

                expression.Arguments.Add(new MappedDataArgument(resolved));
            }

            return(expression);
        }
Пример #9
0
        private void ParseWith(MapParserContext context, MapLexer lexer)
        {
            // with cident = cident ...statement...
            lexer.Consume(TokenKind.Keyword, "with");

            var alias = lexer.Consume(TokenKind.Identifier);

            lexer.Consume(TokenKind.Equals);

            var rhs = CompoundIdentifier.Parse(lexer);

            if (string.IsNullOrEmpty(rhs.Prefix))
            {
                throw new ParserException(rhs, "Right-hand side of 'with' statement must have a prefix.");
            }

            // TODO - perform some sort of validation on the RHS - does it resolve to a model, etc

            context.Push(alias, rhs);

            ParseStatement(context, lexer);

            context.Pop();
        }
Пример #10
0
        private void ParseStatement(MapParserContext context, MapLexer lexer)
        {
            // Constructs:
            //      examples ...
            //      ignore ...
            //      with ...
            //      { statements }
            if (lexer.Token.Kind == TokenKind.Keyword)
            {
                if (lexer.Token.Text == "with")
                {
                    ParseWith(context, lexer);
                    return;
                }
                else if (lexer.Token.Text == "name")
                {
                    ParseName(context, lexer);
                    return;
                }
                else if (lexer.Token.Text == "examples")
                {
                    ParseExamples(context, lexer);
                    return;
                }
                else if (lexer.Token.Text == "ignore")
                {
                    ParseIgnore(context, lexer);
                    return;
                }
                else
                {
                    throw new ParserException($"Unexpected keyword '{lexer.Token.Text}'.", lexer.Token);
                }
            }
            else if (lexer.Token.Kind == TokenKind.LeftCurly)
            {
                lexer.Consume(TokenKind.LeftCurly);

                while (lexer.Token.Kind != TokenKind.RightCurly)
                {
                    ParseStatement(context, lexer);
                }

                lexer.Consume(TokenKind.RightCurly);

                return;
            }

            // Constructs:
            //      ident = model(ident);
            //      ident:ident = ident:ident;
            var lhs = CompoundIdentifier.Parse(lexer);

            lexer.Consume(TokenKind.Equals);

            var rhs = expressionParser.Parse(lexer, context);

            // A little special handling for the model function
            if (rhs.FunctionName == "model")
            {
                var model = rhs.Arguments.First().Model;

                if (model == null)
                {
                    throw new ParserException("The 'model' function requires a model argument.");
                }

                context.AddModelAlias(lhs.Parts.First(), model);

                lexer.Consume(TokenKind.Semicolon);
            }
            else
            {
                var target = context.Resolve(lhs);

                var mapCount = context.MapList.Maps.Where(x => x.TargetAttribute.Equals(target)).Count();
                var mapping  = new ExpressiveMapping(IdNames.Substring(mapCount, 1), target, rhs);

                context.MapList.Maps.Add(mapping);

                if (lexer.Token.Kind == TokenKind.LeftCurly)
                {
                    ParseMappingExtras(context, lexer, mapping);
                }
                else
                {
                    lexer.Consume(TokenKind.Semicolon);
                }
            }
        }
Пример #11
0
        private void ParseExamples(MapParserContext context, MapLexer lexer)
        {
            // Build up the definition, so we can later load all the examples.
            var definition = new MapParserExamplesDefinition();

            // Parse the preamble
            lexer.Consume(TokenKind.Keyword, "examples");
            lexer.Consume(TokenKind.LeftCurly);

            // Keep parsing model blocks until we run out
            while (lexer.Token.Kind == TokenKind.Identifier || lexer.Token.Kind == TokenKind.Keyword)
            {
                // Grab the model name, and create a model object that we'll populate
                // TODO - create a new model example class thingy
                var modelId = lexer.Consume(TokenKind.Identifier, TokenKind.Keyword);
                lexer.Consume(TokenKind.LeftCurly);

                var model = new MapParserExamplesModel(modelId);
                definition.Models.Add(model);

                // Process model directives until we run out
                while (lexer.Token.Kind == TokenKind.Keyword)
                {
                    switch (lexer.Token.Text)
                    {
                    case "prefix":
                        lexer.Consume(TokenKind.Keyword, "prefix");
                        lexer.Consume(TokenKind.Colon);
                        model.Prefix = lexer.Consume(TokenKind.String);
                        lexer.Consume(TokenKind.Semicolon);
                        break;

                    case "suffix":
                        lexer.Consume(TokenKind.Keyword, "suffix");
                        lexer.Consume(TokenKind.Colon);
                        model.Suffix = lexer.Consume(TokenKind.String);
                        lexer.Consume(TokenKind.Semicolon);
                        break;

                    case "directory":
                        lexer.Consume(TokenKind.Keyword, "directory");
                        lexer.Consume(TokenKind.Colon);
                        model.Directories.Add(lexer.Consume(TokenKind.String));
                        lexer.Consume(TokenKind.Semicolon);
                        break;

                    default:
                        throw new ParserException($"Unexpected keyword in examples block: '{lexer.Token.Text}'.");
                    }
                }

                // Finish up the model block
                lexer.Consume(TokenKind.RightCurly);
            }

            // Finish up the examples block
            lexer.Consume(TokenKind.RightCurly);

            // Record what we found
            context.Examples.Add(definition);
        }