// --- Cobol text line scanner private void MapVariableLengthLineWithReferenceFormat() { string line = textLine.Text; int lastIndexOfLine = line.Length - 1; // Test for free format compiler directives embedded in a reference format file int compilerDirectiveIndex = FindFirstCharOfCompilerDirectiveBeforeColumn8(line); if (compilerDirectiveIndex >= 0) { // Free text format line embedded in reference format file SequenceNumber = new TextArea(TextAreaType.SequenceNumber, 0, compilerDirectiveIndex - 1); Indicator = new TextArea(TextAreaType.Indicator, compilerDirectiveIndex, compilerDirectiveIndex - 1); Source = new TextArea(TextAreaType.Source, compilerDirectiveIndex, lastIndexOfLine > 71 ? 71 : lastIndexOfLine); Comment = new TextArea(TextAreaType.Comment, lastIndexOfLine > 71 ? 72 : lastIndexOfLine + 1, lastIndexOfLine); } else { // Cobol reference format if (lastIndexOfLine >= 7) { SequenceNumber = new TextArea(TextAreaType.SequenceNumber, 0, 5); Indicator = new TextArea(TextAreaType.Indicator, 6, 6); Source = new TextArea(TextAreaType.Source, 7, lastIndexOfLine > 71 ? 71 : lastIndexOfLine); Comment = new TextArea(TextAreaType.Comment, lastIndexOfLine > 71 ? 72 : lastIndexOfLine + 1, lastIndexOfLine); } else if (lastIndexOfLine == 6) { SequenceNumber = new TextArea(TextAreaType.SequenceNumber, 0, 5); Indicator = new TextArea(TextAreaType.Indicator, 6, 6); Source = new TextArea(TextAreaType.Source, 7, 6); Comment = new TextArea(TextAreaType.Comment, 7, 6); } else { SequenceNumber = new TextArea(TextAreaType.SequenceNumber, 0, lastIndexOfLine); Indicator = new TextArea(TextAreaType.Indicator, lastIndexOfLine + 1, lastIndexOfLine); Source = new TextArea(TextAreaType.Source, lastIndexOfLine + 1, lastIndexOfLine); Comment = new TextArea(TextAreaType.Comment, lastIndexOfLine + 1, lastIndexOfLine); } } }
/// <summary> /// Scan a group of continuation lines /// </summary> public static void ScanTokensLineContinuationGroup(IList<TokensLine> continuationLinesGroup, MultilineScanState initialScanState, TypeCobolOptions compilerOptions) { // p54: Continuation lines // Any sentence, entry, clause, or phrase that requires more than one line can be // continued in Area B of the next line that is neither a comment line nor a blank line. // The line being continued is a continued line; the succeeding lines are continuation // lines. // Track the length of text contributed to the continuation text by each individual line TextArea[] textAreasForOriginalLinesInConcatenatedLine = new TextArea[continuationLinesGroup.Count]; int[] startIndexForTextAreasInOriginalLines = new int[continuationLinesGroup.Count]; int[] offsetForLiteralContinuationInOriginalLines = new int[continuationLinesGroup.Count]; // Initialize the continuation text with the complete source text of the first line TokensLine firstLine = continuationLinesGroup[0]; string concatenatedLine = null; if (firstLine.Type == CobolTextLineType.Source || (firstLine.Type == CobolTextLineType.Debug && initialScanState.WithDebuggingMode)) { concatenatedLine = firstLine.SourceText; } else { concatenatedLine = String.Empty; Scanner.ScanTokensLine(firstLine, initialScanState, compilerOptions); } textAreasForOriginalLinesInConcatenatedLine[0] = new TextArea(TextAreaType.Source, 0, concatenatedLine.Length -1); startIndexForTextAreasInOriginalLines[0] = firstLine.Source.StartIndex; offsetForLiteralContinuationInOriginalLines[0] = 0; // All the following lines are continuation lines // => build a character string representing the complete continuation text along the way for (int i = 1; i < continuationLinesGroup.Count; i++) { TokensLine continuationLine = continuationLinesGroup[i]; int startIndex = continuationLine.Source.StartIndex; int lastIndex = continuationLine.Source.EndIndex; string line = continuationLine.Text; // 1. Match and remove all blank characters at the beginning of the continuation line int startOfContinuationIndex = startIndex; for (; startOfContinuationIndex <= lastIndex && line[startOfContinuationIndex] == ' '; startOfContinuationIndex++) { } if (startOfContinuationIndex > startIndex) { Token whitespaceToken = new Token(TokenType.SpaceSeparator, startIndex, startOfContinuationIndex - 1, continuationLine); continuationLine.SourceTokens.Add(whitespaceToken); startIndex = startOfContinuationIndex; } if (startOfContinuationIndex <= lastIndex) { if (startOfContinuationIndex < 4) { continuationLine.AddDiagnostic(MessageCode.AreaAOfContinuationLineMustBeBlank, startOfContinuationIndex, startOfContinuationIndex); } } else { // Nothing but spaces on the continuation line continue; } // p55: Continuation of alphanumeric and national literals // Alphanumeric and national literals can be continued only when there are no DBCS // characters in the content of the literal. // The following rules apply to alphanumeric and national literals that do not contain // DBCS characters: // - If the continued line contains an alphanumeric or national literal without a // closing quotation mark, all spaces at the end of the continued line (through // column 72) are considered to be part of the literal. The continuation line must // contain a hyphen in the indicator area, and the first nonblank character must be // a quotation mark. The continuation of the literal begins with the character // immediately following the quotation mark. // - If an alphanumeric or national literal that is to be continued on the next line has // as its last character a quotation mark in column 72, the continuation line must // start with two consecutive quotation marks. This will result in a single quotation // mark as part of the value of the literal. // - If the last character on the continued line of an alphanumeric or national literal // is a single quotation mark in Area B, the continuation line can start with a single // quotation mark. This will result in two consecutive literals instead of one // continued literal. // The rules are the same when an apostrophe is used instead of a quotation mark in // delimiters. // ... p55 -> p56: examples of continuations and expected behavior ... int offsetForLiteralContinuation = 0; if (concatenatedLine.Length > 0) { // Scan the continuation text, and get its last token so far TokensLine temporaryTokensLine = TokensLine.CreateVirtualLineForInsertedToken(firstLine.InitialLineIndex, concatenatedLine); Scanner.ScanTokensLine(temporaryTokensLine, initialScanState, compilerOptions); Token lastTokenOfConcatenatedLineSoFar = temporaryTokensLine.SourceTokens[temporaryTokensLine.SourceTokens.Count - 1]; // Check if the last token so far is an alphanumeric or national literal if (lastTokenOfConcatenatedLineSoFar.TokenFamily == TokenFamily.AlphanumericLiteral) { // The continuation line must contain a hyphen in the indicator area, and the first nonblank character must be a quotation mark if (line[startOfContinuationIndex] != lastTokenOfConcatenatedLineSoFar.ExpectedClosingDelimiter) { continuationLine.AddDiagnostic(MessageCode.InvalidFirstCharForContinuationLine, startOfContinuationIndex, startOfContinuationIndex, lastTokenOfConcatenatedLineSoFar.ExpectedClosingDelimiter); } // The continuation of the literal begins with the character immediately following the quotation mark. else { offsetForLiteralContinuation = 1; // If an alphanumeric literal that is to be continued on the next line has as its last character a quotation mark in column 72, // the continuation line must start with two consecutive quotation marks. if (lastTokenOfConcatenatedLineSoFar.HasClosingDelimiter) { if ((startOfContinuationIndex + 1) > lastIndex || line[startOfContinuationIndex + 1] != lastTokenOfConcatenatedLineSoFar.ExpectedClosingDelimiter) { continuationLine.AddDiagnostic(MessageCode.InvalidFirstTwoCharsForContinuationLine, startOfContinuationIndex, startOfContinuationIndex + 1, lastTokenOfConcatenatedLineSoFar.ExpectedClosingDelimiter); // Use the first quotation mark to avoid a complete mess while scanning the rest of the line offsetForLiteralContinuation = 0; } } } } // Check if the last token so far is a floating comment else if (lastTokenOfConcatenatedLineSoFar.TokenType == TokenType.FloatingComment) { // => remove the floating comment from the text of the continuation concatenatedLine = concatenatedLine.Substring(0, concatenatedLine.Length - lastTokenOfConcatenatedLineSoFar.Length); textAreasForOriginalLinesInConcatenatedLine[i - 1] = new TextArea(TextAreaType.Source, textAreasForOriginalLinesInConcatenatedLine[i - 1].StartIndex, textAreasForOriginalLinesInConcatenatedLine[i - 1].EndIndex - lastTokenOfConcatenatedLineSoFar.Length); TokensLine lineWithFloatingComment = continuationLinesGroup[i - 1]; Token floatingCommentToken = new Token(TokenType.FloatingComment, lineWithFloatingComment.Length - lastTokenOfConcatenatedLineSoFar.Length, lineWithFloatingComment.Length - 1, lineWithFloatingComment); lineWithFloatingComment.SourceTokens.Add(floatingCommentToken); } // Check if the last token so far is a comment entry else if (lastTokenOfConcatenatedLineSoFar.TokenType == TokenType.CommentEntry) { // p105: A hyphen in the indicator area (column 7) is not permitted in comment - entries. // => impossible to ignore the continuation indicator here, it is too late // (we can not know there is a comment entry before scanning the continuation lines groups) // => register an error message continuationLine.AddDiagnostic(MessageCode.HyphenIndicatorNotPermittedInCommenEntries, continuationLine.Indicator.StartIndex + 1, continuationLine.Indicator.EndIndex + 1); } } // p54: If there is no hyphen (-) in the indicator area (column 7) of a line, the last character // of the preceding line is assumed to be followed by a space. // If there is a hyphen in the indicator area of a line, the first nonblank character of // the continuation line immediately follows the last nonblank character of the // continued line without an intervening space. // Concatenate the continuation text so far with the text of the current continuation line int startIndexOfContinuationStringInContinuationLine = startOfContinuationIndex + offsetForLiteralContinuation; int lengthOfContinuationStringInContinuationLine = lastIndex - startIndexOfContinuationStringInContinuationLine + 1; textAreasForOriginalLinesInConcatenatedLine[i] = new TextArea(TextAreaType.Source, concatenatedLine.Length, concatenatedLine.Length + lengthOfContinuationStringInContinuationLine - 1); startIndexForTextAreasInOriginalLines[i] = startIndexOfContinuationStringInContinuationLine; offsetForLiteralContinuationInOriginalLines[i] = offsetForLiteralContinuation; concatenatedLine += line.Substring(startIndexOfContinuationStringInContinuationLine, lengthOfContinuationStringInContinuationLine); } // Scan the complete continuation text as a whole TokensLine virtualContinuationTokensLine = TokensLine.CreateVirtualLineForInsertedToken(firstLine.InitialLineIndex, concatenatedLine); Scanner.ScanTokensLine(virtualContinuationTokensLine, initialScanState, compilerOptions); // Then attribute each token and diagnostic to its corresponding tokens line MultilineScanState scanState = initialScanState; for (int i = 0; i < continuationLinesGroup.Count; i++) { TokensLine originalLine = continuationLinesGroup[i]; originalLine.InitializeScanState(scanState); TextArea textAreaForOriginalLine = textAreasForOriginalLinesInConcatenatedLine[i]; int concatenatedLineToOriginalLineOffset = startIndexForTextAreasInOriginalLines[i] - textAreaForOriginalLine.StartIndex; foreach (Token token in virtualContinuationTokensLine.SourceTokens) { // Token located after the current line if(token.StartIndex > textAreaForOriginalLine.EndIndex) { break; } // Token located before the current line else if(token.StopIndex < textAreaForOriginalLine.StartIndex) { continue; } // Token completely completely included inside the current line else if(token.StartIndex >= textAreaForOriginalLine.StartIndex && token.StopIndex <= textAreaForOriginalLine.EndIndex) { int startIndexInOriginalLine = token.StartIndex + concatenatedLineToOriginalLineOffset; int stopIndexInOriginalLine = token.StopIndex + concatenatedLineToOriginalLineOffset; token.CorrectTokensLine(originalLine, startIndexInOriginalLine, stopIndexInOriginalLine); originalLine.AddToken(token); foreach(Diagnostic diag in virtualContinuationTokensLine.GetDiagnosticsForToken(token)) { originalLine.AddDiagnostic((MessageCode)diag.Info.Code, token, diag.MessageArgs); } } // Multiline continuation token only partially located on this line else { bool isContinuationFromPreviousLine = token.StartIndex < textAreaForOriginalLine.StartIndex; bool isContinuedOnNextLine = token.StopIndex > textAreaForOriginalLine.EndIndex; int startIndexInOriginalLine = 0; if (isContinuationFromPreviousLine) { startIndexInOriginalLine = startIndexForTextAreasInOriginalLines[i] - offsetForLiteralContinuationInOriginalLines[i]; } else { startIndexInOriginalLine = token.StartIndex + concatenatedLineToOriginalLineOffset; } int stopIndexInOriginalLine = 0; if (isContinuedOnNextLine) { stopIndexInOriginalLine = originalLine.Source.EndIndex; // If a continued line ends with a floating comment, the continued token ends just before the floating comment if (originalLine.SourceTokens.Count > 0 && originalLine.SourceTokens[originalLine.SourceTokens.Count - 1].TokenType == TokenType.FloatingComment) { stopIndexInOriginalLine -= originalLine.SourceTokens[originalLine.SourceTokens.Count - 1].Length; } } else { stopIndexInOriginalLine = token.StopIndex + concatenatedLineToOriginalLineOffset; } ContinuationToken continuationToken = new ContinuationToken(token, startIndexInOriginalLine, stopIndexInOriginalLine, originalLine, isContinuationFromPreviousLine, isContinuedOnNextLine); originalLine.AddToken(continuationToken); // Copy diagnostics on the first line only if(!isContinuationFromPreviousLine) { foreach (Diagnostic diag in virtualContinuationTokensLine.GetDiagnosticsForToken(token)) { originalLine.AddDiagnostic((MessageCode)diag.Info.Code, token, diag.MessageArgs); } } } } scanState = originalLine.ScanState; } }
private void MapVariableLengthLineWithFreeFormat() { string line = textLine.Text; int lastIndexOfLine = line.Length - 1; // No SequenceNumber area in free format text SequenceNumber = new TextArea(TextAreaType.SequenceNumber, 0, -1); // In free format source text : // - a line starting with char * is a comment line or a compiler directive if (lastIndexOfLine >= 0 && line[0] == '*') { // Check for compiler directives if ((line.Length >= 5 && line.StartsWith("*CBL ")) || (line.Length >= 9 && line.StartsWith("*CONTROL "))) { Indicator = new TextArea(TextAreaType.Indicator, 0, -1); Source = new TextArea(TextAreaType.Source, 0, lastIndexOfLine); } else { Indicator = new TextArea(TextAreaType.Indicator, 0, 0); Source = new TextArea(TextAreaType.Source, 1, lastIndexOfLine); } } // - a line starting with char / is a comment line // - a line starting with char - is a continuation line // => a free format program line cannot start with one of these three chars, insert a space before if needed else if (lastIndexOfLine >= 0 && (line[0] == '/' || line[0] == '-')) { Indicator = new TextArea(TextAreaType.Indicator, 0, 0); Source = new TextArea(TextAreaType.Source, 1, lastIndexOfLine); } // - a line starting with d or D + space char is a debug ligne else if (lastIndexOfLine >= 1 && ((line[0] == 'd' || line[0] == 'D') & line[1] == ' ')) { Indicator = new TextArea(TextAreaType.Indicator, 0, 1); Source = new TextArea(TextAreaType.Source, 2, lastIndexOfLine); } else // no indicator { Indicator = new TextArea(TextAreaType.Indicator, 0, -1); Source = new TextArea(TextAreaType.Source, 0, lastIndexOfLine); } // No Comment area in free format text Comment = new TextArea(TextAreaType.Comment, lastIndexOfLine + 1, lastIndexOfLine); }
private void ApplyTextAreaStyle(int lineStartOffset, TextArea textArea, HighlightingColor areaStyle) { base.ChangeLinePart(lineStartOffset + textArea.StartIndex, // startOffset lineStartOffset + textArea.EndIndex + 1, // endOffset visualLineElement => ApplyColorToElement(visualLineElement, areaStyle)); }