private bool ReadStartSymbol(ISourceStream source, CompoundTokenDetails details) { if (!_startSymbolsFirsts.Contains(source.PreviewChar)) { return(false); } foreach (var subType in _subTypes) { if (!source.MatchSymbol(subType.Start)) { continue; } var previewPos = source.PreviewPosition; source.PreviewPosition += subType.Start.Length; Grammar.SkipWhitespace(source, true); string quote = null; // Search if there should be a quote. if (subType.Quotes.Count > 0) { // Must be quoted. var quoteMatchSuccessful = false; foreach (var q in subType.Quotes) { // TODO: what if not case sensitive? if (!source.MatchSymbol(q)) { continue; } quoteMatchSuccessful = true; quote = q; } if (!quoteMatchSuccessful) { // Revert, revert! source.PreviewPosition = previewPos; continue; } } // Now the preview position is at the beginning of quotes, or the name. var sb = new StringBuilder(); var previewChar = source.PreviewChar; while (previewChar != '\r' && previewChar != '\n') { sb.Append(previewChar); ++source.PreviewPosition; previewChar = source.PreviewChar; } var endLiteral = sb.ToString(); if (quote != null) { var comparisonType = QuoteCaseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; // Check the quotes. if (!(endLiteral.StartsWith(quote, comparisonType) && endLiteral.EndsWith(quote, comparisonType))) { // Malformed end literal. return(false); } else { endLiteral = endLiteral.Substring(quote.Length, endLiteral.Length - quote.Length * 2); } } int elStart = 0, elEnd = endLiteral.Length - 1; // Trim while (elStart < endLiteral.Length && Grammar.IsWhitespace(endLiteral[elStart])) { elStart++; } while (elEnd >= 0 && Grammar.IsWhitespace(endLiteral[elEnd])) { elStart--; } if (elEnd <= elStart) { // Malformed end literal. return(false); } if (elStart != 0 || elEnd != endLiteral.Length - 1) { endLiteral = endLiteral.Substring(elStart, elEnd - elStart + 1); } //We found start symbol details.StartSymbol = subType.Start; details.Flags |= (short)subType.Flags; details.TokenName = endLiteral; details.TokenQuote = quote; details.SubTypeIndex = subType.Index; // No need to set source.PreviewPosition, we've done it. return(true); } return(false); }
private bool CompleteReadBody(ISourceStream source, CompoundTokenDetails details) { var escapeEnabled = !details.IsSet((short)HereDocOptions.NoEscapes); var start = source.PreviewPosition; var endQuoteSymbol = details.TokenName; //1. Find the string end // first get the position of the next line break; we are interested in it to detect malformed string, // therefore do it only if linebreak is NOT allowed; if linebreak is allowed, set it to -1 (we don't care). // MIC: in heredoc, multiline is always allowed. //fix by ashmind for EOF right after opening symbol while (true) { var endPos = source.Text.IndexOf(endQuoteSymbol, source.PreviewPosition, StringComparison.InvariantCulture); //Check for partial token in line-scanning mode if (endPos < 0 && details.PartialOk) { ProcessPartialBody(source, details); return(true); } //Check for malformed string: either EndSymbol not found, or LineBreak is found before EndSymbol var malformed = endPos < 0; if (malformed) { if (endPos > 0) { source.PreviewPosition = endPos + 1; } details.Error = Resources.ErrBadStrLiteral; // "Mal-formed string literal - cannot find termination symbol."; return(true); //we did find start symbol, so it is definitely string, only malformed } if (source.EOF) { return(true); } //We found EndSymbol - check if it is escaped; if yes, skip it and continue search if (escapeEnabled && IsEndQuoteEscaped(source.Text, endPos)) { source.PreviewPosition = endPos + endQuoteSymbol.Length; continue; //searching for end symbol } // Check if not correctly indented. if (source.Text[endPos - 1] != '\n' && !details.IsSet((short)HereDocOptions.AllowIndentedEndToken)) { return(false); } //Ok, this is normal endSymbol that terminates the string. // Advance source position and get out from the loop source.PreviewPosition = endPos + endQuoteSymbol.Length; var indent = 0; // Remove the last newline. while (source.Text[endPos] != '\n') { // Remove the indentations. --endPos; ++indent; } // Compensation for the extra '\n'. --indent; // Text[endPos] is now always \n. if (source.Text[endPos - 1] == '\r') { --endPos; } if (details.IsSet((short)HereDocOptions.RemoveBeginningNewLine)) { if (source.Text[start] == '\r') { start += 2; } else { start += 1; } } var body = source.Text.Substring(start, endPos - start); if (details.IsSet((short)HereDocOptions.AllowIndentedEndToken) && details.IsSet((short)HereDocOptions.RemoveIndents) && indent > 0) { // Remove indentations. var undented = false; var lines = body.Split('\n'); for (var i = 0; i < lines.Length; ++i) { var line = lines[i]; if (line.Length == 0 || !Grammar.IsWhitespace(line[0])) { continue; } var n = 0; while (n < indent && n < line.Length && Grammar.IsWhitespace(line[n])) { ++n; } if (n != 0) { line = n == line.Length ? string.Empty : line.Substring(n, line.Length - n); lines[i] = line; undented = true; } } if (undented) { body = string.Join("\n", lines); } } details.Body = body; //if we come here it means we're done - we found string end. return(true); } }