protected IConceptInfo ParseNextConcept(TokenReader tokenReader, Stack<IConceptInfo> context, IEnumerable<IConceptParser> conceptParsers) { var errors = new List<ErrorContext>(); List<Interpretation> possibleInterpretations = new List<Interpretation>(); foreach (var conceptParser in conceptParsers) { TokenReader nextPosition = new TokenReader(tokenReader); var conceptInfoOrError = conceptParser.Parse(nextPosition, context); if (!conceptInfoOrError.IsError) possibleInterpretations.Add(new Interpretation { ConceptInfo = conceptInfoOrError.Value, NextPosition = nextPosition }); else if (!string.IsNullOrEmpty(conceptInfoOrError.Error)) // Empty error means that this parser is not for this keyword. errors.Add(new ErrorContext { Error = conceptInfoOrError.Error, Postion = tokenReader.CurrentPosition, ParserName = conceptParser.GetType().Name, DslSource = _dslSource }); } if (possibleInterpretations.Count == 0) { string msg = "Unrecognized concept. " + _dslSource.ReportError(tokenReader.CurrentPosition); if (errors.Count > 0) { string listedErrors = string.Join("\r\n", errors); if (listedErrors.Length > 500) listedErrors = listedErrors.Substring(0, 500) + "..."; msg = msg + "\r\n\r\nPossible causes:\r\n" + listedErrors; } throw new DslSyntaxException(msg); } int largest = possibleInterpretations.Max(i => i.NextPosition.PositionInTokenList); possibleInterpretations.RemoveAll(i => i.NextPosition.PositionInTokenList < largest); if (possibleInterpretations.Count > 1) { string msg = "Ambiguous syntax. " + _dslSource.ReportError(tokenReader.CurrentPosition) + "\r\n Possible interpretations: " + string.Join(", ", possibleInterpretations.Select(i => i.ConceptInfo.GetType().Name)) + "."; throw new DslSyntaxException(msg); } tokenReader.CopyFrom(possibleInterpretations.Single().NextPosition); return possibleInterpretations.Single().ConceptInfo; }
protected string ReportErrorContext(IConceptInfo conceptInfo, TokenReader tokenReader) { var sb = new StringBuilder(); sb.AppendLine(tokenReader.ReportPosition()); if (conceptInfo != null) { sb.AppendFormat("Previous concept: {0}", conceptInfo.GetUserDescription()).AppendLine(); var properties = conceptInfo.GetType().GetProperties().ToList(); properties.ForEach(it => sb.AppendFormat("Property {0} ({1}) = {2}", it.Name, it.PropertyType.Name, it.GetValue(conceptInfo, null) ?? "<null>") .AppendLine()); } return sb.ToString(); }
public void ParseNextConcept_DontDescribeExceptionIfConceptNotRecognised() { string dsl = "a"; List<IConceptParser> conceptParsers = new List<IConceptParser>() { new TestErrorParser("b") }; TokenReader tokenReader = new TokenReader(Tokenizer.GetTokens(new DslSourceHelper(dsl)), 0); try { IConceptInfo actual = new TestDslParser(dsl).ParseNextConcept(tokenReader, null, conceptParsers); } catch (Exception e) { Console.WriteLine(e.Message); Assert.IsFalse(e.Message.Contains(TestErrorParser.ErrorMessage), "Exception must not contain: " + TestErrorParser.ErrorMessage); throw; } }
public void CopyFrom(TokenReader tokenReader) { this.TokenList = tokenReader.TokenList; this.PositionInTokenList = tokenReader.PositionInTokenList; }
public TokenReader(TokenReader tokenReader) { CopyFrom(tokenReader); }
public void CopyFrom(TokenReader tokenReader) { this._tokenList = tokenReader._tokenList; this.PositionInTokenList = tokenReader.PositionInTokenList; }
protected IEnumerable<IConceptInfo> ExtractConcepts(IEnumerable<IConceptParser> conceptParsers) { var stopwatch = Stopwatch.StartNew(); TokenReader tokenReader = new TokenReader(Tokenizer.GetTokens(_dslSource), 0); List<IConceptInfo> newConcepts = new List<IConceptInfo>(); Stack<IConceptInfo> context = new Stack<IConceptInfo>(); while (!tokenReader.EndOfInput) { IConceptInfo conceptInfo = ParseNextConcept(tokenReader, context, conceptParsers); newConcepts.Add(conceptInfo); UpdateContextForNextConcept(tokenReader, context, conceptInfo); } _performanceLogger.Write(stopwatch, "DslParser.ExtractConcepts."); if (context.Count > 0) throw new DslSyntaxException(string.Format( ReportErrorContext(context.Peek(), _dslSource.Script.Length - 1) + "Expected \"}\" at the end of the script to close concept \"{0}\".", context.Peek())); return newConcepts; }
protected void UpdateContextForNextConcept(TokenReader tokenReader, Stack<IConceptInfo> context, IConceptInfo conceptInfo) { if (tokenReader.TryRead("{")) context.Push(conceptInfo); else if (!tokenReader.TryRead(";")) { var sb = new StringBuilder(); sb.Append(ReportErrorContext(conceptInfo, tokenReader.CurrentPosition)); sb.AppendFormat("Expected \";\" or \"{{\"."); throw new DslSyntaxException(sb.ToString()); } while (tokenReader.TryRead("}")) { if (context.Count == 0) throw new DslSyntaxException(_dslSource.ReportError(tokenReader.CurrentPosition) + "\r\nUnexpected \"}\". "); context.Pop(); } }
private (IConceptInfo ConceptInfo, List <string> Warnings) ParseNextConcept(TokenReader tokenReader, Stack <IConceptInfo> context, MultiDictionary <string, IConceptParser> conceptParsers) { var errorReports = new List <Func <string> >(); 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; 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 { ConceptInfo = 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())); } } } if (possibleInterpretations.Count == 0) { if (errorReports.Count > 0) { string errorsReport = string.Join("\r\n", errorReports.Select(x => x.Invoke())).Limit(500, "..."); throw new DslSyntaxException($"Invalid parameters after keyword '{keyword}'. {tokenReader.ReportPosition()}\r\n\r\nPossible causes:\r\n{errorsReport}"); } else if (!string.IsNullOrEmpty(keyword)) { throw new DslSyntaxException($"Unrecognized concept keyword '{keyword}'. {tokenReader.ReportPosition()}"); } else { throw new DslSyntaxException($"Invalid DSL script syntax. {tokenReader.ReportPosition()}"); } } Disambiguate(possibleInterpretations); if (possibleInterpretations.Count > 1) { var report = new List <string>(); report.Add($"Ambiguous syntax. {tokenReader.ReportPosition()}"); report.Add($"There are multiple possible interpretations of keyword '{keyword}':"); for (int i = 0; i < possibleInterpretations.Count; i++) { report.Add($"{i + 1}. {possibleInterpretations[i].ConceptInfo.GetType().AssemblyQualifiedName}"); } throw new DslSyntaxException(string.Join("\r\n", report)); } var parsedStatement = possibleInterpretations.Single(); tokenReader.CopyFrom(parsedStatement.NextPosition); return(parsedStatement.ConceptInfo, parsedStatement.Warnings); }
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); }
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); }
protected IConceptInfo ParseNextConcept(TokenReader tokenReader, Stack <IConceptInfo> context, MultiDictionary <string, IConceptParser> conceptParsers) { var errorReports = new List <Func <string> >(); 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; if (keyword != null) { foreach (var conceptParser in conceptParsers.Get(keyword)) { TokenReader nextPosition = new TokenReader(tokenReader); var conceptInfoOrError = conceptParser.Parse(nextPosition, context); if (!conceptInfoOrError.IsError) { possibleInterpretations.Add(new Interpretation { ConceptInfo = conceptInfoOrError.Value, NextPosition = nextPosition }); } 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())); } } } if (possibleInterpretations.Count == 0) { if (errorReports.Count > 0) { string errorsReport = string.Join("\r\n", errorReports.Select(x => x.Invoke())).Limit(500, "..."); throw new DslSyntaxException($"Invalid parameters after keyword '{keyword}'. {tokenReader.ReportPosition()}\r\n\r\nPossible causes:\r\n{errorsReport}"); } else if (!string.IsNullOrEmpty(keyword)) { throw new DslSyntaxException($"Unrecognized concept keyword '{keyword}'. {tokenReader.ReportPosition()}"); } else { throw new DslSyntaxException($"Invalid DSL script syntax. {tokenReader.ReportPosition()}"); } } int largest = possibleInterpretations.Max(i => i.NextPosition.PositionInTokenList); possibleInterpretations.RemoveAll(i => i.NextPosition.PositionInTokenList < largest); if (possibleInterpretations.Count > 1) { string msg = "Ambiguous syntax. " + tokenReader.ReportPosition() + "\r\n Possible interpretations: " + string.Join(", ", possibleInterpretations.Select(i => i.ConceptInfo.GetType().Name)) + "."; throw new DslSyntaxException(msg); } tokenReader.CopyFrom(possibleInterpretations.Single().NextPosition); return(possibleInterpretations.Single().ConceptInfo); }