Sprache парсер и символы, убегающие
Я не нашел пример - что делать с убегающими персонажами. Я нашел пример кода:
static void Main(string[] args)
{
string text = "'test \\\' text'";
var result = Grammar.QuotedText.End().Parse(text);
}
public static class Grammar
{
private static readonly Parser<char> QuoteEscape = Parse.Char('\\');
private static Parser<T> Escaped<T>(Parser<T> following)
{
return from escape in QuoteEscape
from f in following
select f;
}
private static readonly Parser<char> QuotedTextDelimiter = Parse.Char('\'');
private static readonly Parser<char> QuotedContent =
Parse.AnyChar.Except(QuotedTextDelimiter).Or(Escaped(QuotedTextDelimiter));
public static Parser<string> QuotedText = (
from lquot in QuotedTextDelimiter
from content in QuotedContent.Many().Text()
from rquot in QuotedTextDelimiter
select content
).Token();
}
Он успешно анализирует текст, если текст не содержит экранирования, но не анализирует текст с экранированием символов.
2 ответа
У меня была похожая проблема, синтаксический анализ строк с использованием "
в качестве разделителя и \
как побег персонажа. Я написал для этого простой парсер (возможно, это не самое элегантное решение), и, похоже, он работает хорошо.
Вы должны быть в состоянии приспособить это, так как единственное различие, кажется, является разделителем.
var escapedDelimiter = Parse.String("\\\"").Text().Named("Escaped delimiter");
var singleEscape = Parse.String("\\").Text().Named("Single escape character");
var doubleEscape = Parse.String("\\\\").Text().Named("Escaped escape character");
var delimiter = Parse.Char('"').Named("Delimiter");
var simpleLiteral = Parse.AnyChar.Except(singleEscape).Except(delimiter).Many().Text().Named("Literal without escape/delimiter character");
var stringLiteral = (from start in delimiter
from v in escapedDelimiter.Or(doubleEscape).Or(singleEscape).Or(simpleLiteral).Many()
from end in delimiter
select string.Concat(start) + string.Concat(v) + string.Concat(end));
Ключевая часть from v in ...
, Сначала он ищет экранированные разделители, затем двойные escape-символы и затем одиночные escape-символы, а затем пытается проанализировать его как "simpleLiteral" без каких-либо экранирующих или разделительных символов. Изменение порядка может привести к ошибкам синтаксического анализа (например, если вы попытаетесь проанализировать одиночный экранированный код перед экранированными разделителями, вы никогда не найдете последний, такой же, как для двойного экранирования и одиночного экранирования). Этот шаг повторяется много раз, пока не появится неэкранированный разделитель (from v in ...
не обрабатывает неэкранированные разделители, но from end in delimiter
делает конечно).
У меня было требование анализировать строковые литералы, которые могут быть обозначены одинарными или двойными кавычками, и, кроме того, также поддерживать их экранирование.
Метод, генерирующий парсер строкового литерала:
private readonly StringBuilder _reusableStringBuilder = new StringBuilder();
private Parser<string> BuildStringLiteralParser(char delimiterChar)
{
var escapeChar = '\\';
var delimiter = Sprache.Parse.Char(delimiterChar);
var escape = Sprache.Parse.Char(escapeChar);
var escapedDelimiter = Sprache.Parse.String($"{escapeChar}{delimiterChar}");
var splitByEscape = Sprache.Parse.AnyChar
.Except(delimiter.Or(escape))
.Many()
.Text()
.DelimitedBy(escapedDelimiter);
string BuildStr(IEnumerable<IEnumerable<string>> splittedByEscape)
{
_reusableStringBuilder.Clear();
var i = 0;
foreach (var splittedByEscapedDelimiter in splittedByEscape)
{
if (i > 0)
{
_reusableStringBuilder.Append(escapeChar);
}
var j = 0;
foreach (var str in splittedByEscapedDelimiter)
{
if (j > 0)
{
_reusableStringBuilder.Append(delimiterChar);
}
_reusableStringBuilder.Append(str);
j++;
}
i++;
}
return _reusableStringBuilder.ToString();
}
return (from ln in delimiter
from splittedByEscape in splitByEscape.DelimitedBy(escape)
from rn in delimiter
select BuildStr(splittedByEscape)).Named("string");
}
Применение:
var stringParser = BuildStringLiteralParser('\"').Or(BuildStringLiteralParser('\''));
var str1 = stringParser.Parse("\"'Hello' \\\"John\\\"\"");
Console.WriteLine(str1);
var str2 = stringParser.Parse("\'\\'Hello\\' \"John\"\'");
Console.WriteLine(str2);
Выход:
'Hello' "John"
'Hello' "John"
Проверьте рабочую демонстрацию:https://dotnetfiddle.net/8wFNbj