Beispiel #1
0
        private List <ConceptSyntaxNode> ExtractConcepts(MultiDictionary <string, IConceptParser> conceptParsers)
        {
            var stopwatch = Stopwatch.StartNew();

            var tokenizerResult = _tokenizer.GetTokens();

            if (tokenizerResult.SyntaxError != null)
            {
                ExceptionsUtility.Rethrow(tokenizerResult.SyntaxError);
            }
            var tokenReader = new TokenReader(tokenizerResult.Tokens, 0);

            var newConcepts = new List <ConceptSyntaxNode>();
            var context     = new Stack <ConceptSyntaxNode>();
            var warnings    = new List <string>();

            tokenReader.SkipEndOfFile();
            while (!tokenReader.EndOfInput)
            {
                var parsed = ParseNextConcept(tokenReader, context, conceptParsers);
                newConcepts.Add(parsed.ConceptInfo);

                if (parsed.Warnings != null)
                {
                    warnings.AddRange(parsed.Warnings);
                }

                UpdateContextForNextConcept(tokenReader, context, parsed.ConceptInfo);
                OnKeyword?.Invoke(tokenReader, null);

                if (context.Count == 0)
                {
                    tokenReader.SkipEndOfFile();
                }
            }

            _performanceLogger.Write(stopwatch, "ExtractConcepts (" + newConcepts.Count + " concepts).");

            if (context.Count > 0)
            {
                var(dslScript, position) = tokenReader.GetPositionInScript();
                throw new DslSyntaxException($"Expected \"}}\" to close concept \"{context.Peek()}\".",
                                             "RH0002", dslScript, position, 0, ReportPreviousConcept(context.Peek()));
            }

            foreach (string warning in warnings)
            {
                if (_syntax.Value.ExcessDotInKey == ExcessDotInKey.Ignore)
                {
                    _logger.Trace(warning);
                }
                else
                {
                    _logger.Warning(warning);
                }
            }
            if (_syntax.Value.ExcessDotInKey == ExcessDotInKey.Error && warnings.Any())
            {
                throw new DslSyntaxException(warnings.First());
            }

            return(newConcepts);
        }
Beispiel #2
0
        private (ConceptSyntaxNode ConceptInfo, List <string> Warnings) ParseNextConcept(TokenReader tokenReader, Stack <ConceptSyntaxNode> context, MultiDictionary <string, IConceptParser> conceptParsers)
        {
            var errorReports = new List <Func <(string formattedError, string simpleError)> >();
            List <Interpretation> possibleInterpretations = new List <Interpretation>();

            var keywordReader = new TokenReader(tokenReader).ReadText(); // Peek, without changing the original tokenReader's position.
            var keyword       = keywordReader.IsError ? null : keywordReader.Value;

            OnKeyword?.Invoke(tokenReader, keyword);
            if (keyword != null)
            {
                foreach (var conceptParser in conceptParsers.Get(keyword))
                {
                    TokenReader nextPosition       = new TokenReader(tokenReader);
                    var         conceptInfoOrError = conceptParser.Parse(nextPosition, context, out var warnings);

                    if (!conceptInfoOrError.IsError)
                    {
                        possibleInterpretations.Add(new Interpretation
                        {
                            Node         = conceptInfoOrError.Value,
                            NextPosition = nextPosition,
                            Warnings     = warnings
                        });
                    }
                    else if (!string.IsNullOrEmpty(conceptInfoOrError.Error)) // Empty error means that this parser is not for this keyword.
                    {
                        errorReports.Add(() =>
                                         (string.Format("{0}: {1}\r\n{2}", conceptParser.GetType().Name, conceptInfoOrError.Error, tokenReader.ReportPosition()), conceptInfoOrError.Error));
                    }
                }
            }

            if (possibleInterpretations.Count == 0)
            {
                var(dslScript, position) = tokenReader.GetPositionInScript();
                if (errorReports.Count > 0)
                {
                    var errorReportValues  = errorReports.Select(x => x.Invoke()).ToList();
                    var errorsReport       = string.Join("\r\n", errorReportValues.Select(x => x.formattedError)).Limit(500, "...");
                    var simpleErrorsReport = string.Join("\n", errorReportValues.Select(x => x.simpleError));
                    var simpleMessage      = $"Invalid parameters after keyword '{keyword}'. Possible causes: {simpleErrorsReport}";
                    var possibleCauses     = $"Possible causes:\r\n{errorsReport}";
                    throw new DslSyntaxException(simpleMessage, "RH0003", dslScript, position, 0, possibleCauses);
                }
                else if (!string.IsNullOrEmpty(keyword))
                {
                    var simpleMessage = $"Unrecognized concept keyword '{keyword}'.";
                    throw new DslSyntaxException(simpleMessage, "RH0004", dslScript, position, 0, null);
                }
                else
                {
                    var simpleMessage = $"Invalid DSL script syntax.";
                    throw new DslSyntaxException(simpleMessage, "RH0005", dslScript, position, 0, null);
                }
            }

            Disambiguate(possibleInterpretations);
            if (possibleInterpretations.Count > 1)
            {
                var interpretations = new List <string>();
                for (int i = 0; i < possibleInterpretations.Count; i++)
                {
                    interpretations.Add($"{i + 1}. {possibleInterpretations[i].Node.Concept.AssemblyQualifiedName}");
                }

                var simpleMessage = $"Ambiguous syntax. There are multiple possible interpretations of keyword '{keyword}': {string.Join(", ", interpretations)}.";
                var(dslScript, position) = tokenReader.GetPositionInScript();

                throw new DslSyntaxException(simpleMessage, "RH0006", dslScript, position, 0, null);
            }

            var parsedStatement = possibleInterpretations.Single();

            tokenReader.CopyFrom(parsedStatement.NextPosition);
            return(parsedStatement.Node, parsedStatement.Warnings);
        }