Example #1
0
        /// <summary>
        /// Implements TryParseDefinition.
        /// </summary>
        protected override bool TryParseDefinitionCore(ServiceDefinitionText source, out ServiceInfo?service, out IReadOnlyList <ServiceDefinitionError> errors)
        {
            var errorList                  = new List <ServiceDefinitionError>();
            var definitionLines            = new List <string>();
            var remarksSectionsByName      = new Dictionary <string, FsdRemarksSection>(StringComparer.OrdinalIgnoreCase);
            var interleavedRemarksSections = new List <FsdRemarksSection>();

            if (!s_interleavedMarkdown.IsMatch(source.Text))
            {
                ReadRemarksAfterDefinition(source, definitionLines, remarksSectionsByName, errorList);
            }
            else
            {
                ReadInterleavedRemarks(source, definitionLines, interleavedRemarksSections);
            }

            source  = new ServiceDefinitionText(source.Name, string.Join("\n", definitionLines));
            service = null;

            try
            {
                service = FsdParsers.ParseDefinition(source, remarksSectionsByName);
                errorList.AddRange(service.GetValidationErrors());

                // check for unused remarks sections
                foreach (var remarksSectionPair in remarksSectionsByName)
                {
                    var sectionName = remarksSectionPair.Key;
                    if (service.Name != sectionName && service.FindMember(sectionName) == null)
                    {
                        errorList.Add(new ServiceDefinitionError($"Unused remarks heading: {sectionName}", remarksSectionPair.Value.Position));
                    }
                }

                // check for interleaved remarks sections
                foreach (var remarksSection in interleavedRemarksSections)
                {
                    var remarksLineNumber = remarksSection.Position.LineNumber;
                    if (remarksLineNumber > service.GetPart(ServicePartKind.Name) !.Position.LineNumber &&
                        remarksLineNumber < service.GetPart(ServicePartKind.End) !.Position.LineNumber)
                    {
                        ServiceMemberInfo targetMember = service;
                        var targetLineNumber           = 0;
                        foreach (var member in service.Members)
                        {
                            var memberLineNumber = member.GetPart(ServicePartKind.Name) !.Position.LineNumber;
                            if (remarksLineNumber > memberLineNumber && memberLineNumber > targetLineNumber)
                            {
                                targetMember     = member;
                                targetLineNumber = memberLineNumber;
                            }
                        }

                        if (targetMember.Remarks.Count == 0)
                        {
                            targetMember.Remarks = remarksSection.Lines;
                        }
                        else
                        {
                            targetMember.Remarks = targetMember.Remarks.Concat(new[] { "" }).Concat(remarksSection.Lines).ToList();
                        }
                    }
                }
            }
            catch (ParseException exception)
            {
                var expectation = exception
                                  .Result
                                  .GetNamedFailures()
                                  .Distinct()
                                  .GroupBy(x => x.Position)
                                  .Select(x => new { LineColumn = x.Key.GetLineColumn(), Names = x.Select(y => y.Name) })
                                  .OrderByDescending(x => x.LineColumn.LineNumber)
                                  .ThenByDescending(x => x.LineColumn.ColumnNumber)
                                  .First();

                int GetExpectationNameRank(string name) => name == "')'" || name == "']'" || name == "'}'" || name == "';'" ? 1 : 2;

                errorList.Add(new ServiceDefinitionError(
                                  "expected " + string.Join(" or ", expectation.Names.Distinct().OrderBy(GetExpectationNameRank).ThenBy(x => x, StringComparer.Ordinal)),
                                  new ServiceDefinitionPosition(source.Name, expectation.LineColumn.LineNumber, expectation.LineColumn.ColumnNumber)));
            }

            errors = errorList;
            return(errorList.Count == 0);
        }
Example #2
0
        /// <summary>
        /// Parses an FSD file into a service definition.
        /// </summary>
        public ServiceInfo ParseDefinition(NamedText source)
        {
            IReadOnlyList <string> definitionLines = null;
            var remarksSections = new Dictionary <string, FsdRemarksSection>(StringComparer.OrdinalIgnoreCase);

            // read remarks after definition
            using (var reader = new StringReader(source.Text))
            {
                string name              = null;
                var    lines             = new List <string>();
                int    lineNumber        = 0;
                int    headingLineNumber = 0;

                while (true)
                {
                    string line = reader.ReadLine();
                    lineNumber++;

                    Match match = line == null ? null : s_markdownHeading.Match(line);
                    if (match == null || match.Success)
                    {
                        if (name == null)
                        {
                            definitionLines = lines;
                        }
                        else
                        {
                            while (lines.Count != 0 && string.IsNullOrWhiteSpace(lines[0]))
                            {
                                lines.RemoveAt(0);
                            }
                            while (lines.Count != 0 && string.IsNullOrWhiteSpace(lines[lines.Count - 1]))
                            {
                                lines.RemoveAt(lines.Count - 1);
                            }

                            var position = new NamedTextPosition(source.Name, headingLineNumber, 1);
                            if (remarksSections.ContainsKey(name))
                            {
                                throw new ServiceDefinitionException("Duplicate remarks heading: " + name, position);
                            }
                            remarksSections.Add(name, new FsdRemarksSection(name, lines, position));
                        }

                        if (match == null)
                        {
                            break;
                        }

                        name              = line.Substring(match.Index + match.Length).Trim();
                        lines             = new List <string>();
                        headingLineNumber = lineNumber;
                    }
                    else
                    {
                        lines.Add(line);
                    }
                }
            }

            source = new NamedText(source.Name, string.Join("\n", definitionLines));

            ServiceInfo service;

            try
            {
                service = FsdParsers.ParseDefinition(source, remarksSections);
            }
            catch (ParseException exception)
            {
                var expectation = exception
                                  .Result
                                  .GetNamedFailures()
                                  .Distinct()
                                  .GroupBy(x => x.Position)
                                  .Select(x => new { LineColumn = x.Key.GetLineColumn(), Names = x.Select(y => y.Name) })
                                  .OrderByDescending(x => x.LineColumn.LineNumber)
                                  .ThenByDescending(x => x.LineColumn.ColumnNumber)
                                  .First();

                throw new ServiceDefinitionException(
                          "expected " + string.Join(" or ", expectation.Names.Distinct().OrderBy(GetExpectationNameRank).ThenBy(x => x, StringComparer.Ordinal)),
                          new NamedTextPosition(source.Name, expectation.LineColumn.LineNumber, expectation.LineColumn.ColumnNumber),
                          exception);
            }

            // check for unused remarks sections
            foreach (var remarksSection in remarksSections.Values)
            {
                string sectionName = remarksSection.Name;
                if (service.Name != sectionName && service.FindMember(sectionName) == null)
                {
                    throw new ServiceDefinitionException($"Unused remarks heading: {sectionName}", remarksSection.Position);
                }
            }

            return(service);
        }