Superpower Parser:处理组合器中子解析器的部分匹配?
Superpower Parser: Deal with partial match of a sub parser in a combinator?
所以我为专有文件类型编写了一个解析器。我已经完成了 95%,但是我的解析器在文件的最后一行失败,即 #
。这是对其他几个解析器的部分匹配。看起来它试图将行解析为 PropertyList 但失败导致整个解析器失败。
我该怎么做才能解决这个问题?
mcve如下,Fiddle这里是https://dotnetfiddle.net/f30sN9
using System;
using Superpower;
using Superpower.Model;
using Superpower.Parsers;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var result1 = TagTXTParser.firstLine.TryParse(sampleFirstLine);
Console.WriteLine(result1);
var result2 = TagTXTParser.propertyList.TryParse(samplePropertyList);
Console.WriteLine(result2);
var result8 = TagTXTParser.record.TryParse(sampleRecord);
Console.WriteLine(result8);
var result9 = TagTXTParser.TagRecordsFileParser.TryParse(sampleFile);
Console.WriteLine(result9);
}
public static string sampleFirstLine = "// 895.34 - Tags\n";
public static string sampleSectionHeading = "#Parameters\n";
public static string sampleItemList = "<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |\n";
public static string samplePropertyList = "#4|NavDisabled|0|1| | |0|0| |\n";
public static string sampleSection1 = "#Parameters\n<[0] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | | \n";
public static string sampleSection2 = "#Parameters\n<[0] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | | \n<[1] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | | \n";
public static string sampleSection3 = "#Parameters\n";
public static string sampleRecord = "#3|ScreenNumber|0|1| | |1|0| |\n#Parameters\n<[0] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | |\n#Alarm\n#History\n#ItemParameters\n<[0] >| 1 | 1 | \n";
public static string sampleFile =
@"// 8.10 - Application Tags
#1|SelectedWindowOnNavBar|0|1| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#2|ScreenName|0|3| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#3|ScreenNumber|0|1| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#4|NavDisabled|0|1| | |0|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#5|PLC_1_IPAddress|0|3| | |1|0| |
#Parameters
<[0]>|1|1|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#6|NumOfMeters|0|3| | |1|0| |
#Parameters
<[0]>|1|1|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#7|ComputerName|0|3| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#8|TZPosition|0|1| | |0|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#9|NewTime|0|3| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#";
}
public class TagTXTParser
{
public record TagProperies(int Id, string Name, List<string> others);
public record TagRecordSection(List<string>[] items);
public class TagRecord
{
public int Id { get; set; } //** first element of First line
public string Name { get; set; }
public List<string> Properties { get; set; }
public TagRecordSection Parameters { get; set; }
public TagRecordSection Alarm { get; set; }
public TagRecordSection History { get; set; }
public TagRecordSection ItemParameters { get; set; }
}
public static TextParser<char> hash = Character.EqualTo('#');
public static TextParser<TextSpan> eol = Span.EqualTo('\n').Or(Span.EqualTo("\r\n"));
public static TextParser<char> pipe = Character.EqualTo('|');
public static TextParser<string> text = Character.ExceptIn('#', '|', '\n', '\r').Many().Select(x => new string(x));
public static TextParser<string> firstLine = from _ in Span.EqualTo("//")
from rest in text
from end in eol
select new string(rest.ToCharArray());
public static TextParser<char[]> heading =
from h in hash
from name in Character.Letter.AtLeastOnce()
from end in eol
select name;
public static TextParser<List<string>> itemList = //from _ in eol
from items in text.ManyDelimitedBy<string, char>(pipe)
from end in eol
select new List<string>(items);
public static TextParser<TagProperies> propertyList =
from _ in hash
from id in Character.Digit.AtLeastOnce()
from endOfId in pipe
from name in text
from endOfName in pipe
from otherItems in itemList
select new TagProperies(Convert.ToInt32(new string(id)), name, otherItems);
public static TextParser<TagRecordSection> section =
from _ in heading
from items in itemList.Many()
select new TagRecordSection(items);
public static TextParser<TagRecord> record =
from props in propertyList
from sections in section.Repeat(4)
select new TagRecord()
{
Id = props.Id,
Name = props.Name,
Properties = props.others,
Parameters = sections[0],
Alarm = sections[1],
History = sections[2],
ItemParameters = sections[3]
};
public static TextParser<TagRecord[]> TagRecordsFileParser =
from _ in firstLine
from records in record.Many()
from end in hash
select records;
}
您需要在 TagRecordsFileParser
中添加对 .Try()
的调用:
public static TextParser<TagRecord[]> TagRecordsFileParser =
from _ in firstLine
from records in record.Try().Many() // <--- HERE
from end in hash
select records;
原因是因为当你刚刚调用 record.Many()
时,它会遍历你的 9 条记录,然后看到另一个散列,所以它认为它是另一个 record
。解析器“消耗”了散列,但事实证明它根本不是一条记录,这导致它失败。调用 record.Try().Many()
告诉 Superpower 尝试解析尽可能多的 record
,但如果其中一个失败,不要让整个解析器失败,只需回溯已经消耗的内容,然后继续其余的解析器定义。
您要做的另一件事是在解析输入时添加对 .AtEnd()
的调用:
var result9 = TagTXTParser.TagRecordsFileParser.AtEnd().TryParse(sampleFile);
这样,解析器只有在成功解析您的整个输入时才会成功。这将使您的输入始终必须以单个 #
字符结尾。否则,如果文件以 #x
之类的结尾,解析器将成功,我认为您不想要。
所以我为专有文件类型编写了一个解析器。我已经完成了 95%,但是我的解析器在文件的最后一行失败,即 #
。这是对其他几个解析器的部分匹配。看起来它试图将行解析为 PropertyList 但失败导致整个解析器失败。
我该怎么做才能解决这个问题?
mcve如下,Fiddle这里是https://dotnetfiddle.net/f30sN9
using System;
using Superpower;
using Superpower.Model;
using Superpower.Parsers;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var result1 = TagTXTParser.firstLine.TryParse(sampleFirstLine);
Console.WriteLine(result1);
var result2 = TagTXTParser.propertyList.TryParse(samplePropertyList);
Console.WriteLine(result2);
var result8 = TagTXTParser.record.TryParse(sampleRecord);
Console.WriteLine(result8);
var result9 = TagTXTParser.TagRecordsFileParser.TryParse(sampleFile);
Console.WriteLine(result9);
}
public static string sampleFirstLine = "// 895.34 - Tags\n";
public static string sampleSectionHeading = "#Parameters\n";
public static string sampleItemList = "<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |\n";
public static string samplePropertyList = "#4|NavDisabled|0|1| | |0|0| |\n";
public static string sampleSection1 = "#Parameters\n<[0] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | | \n";
public static string sampleSection2 = "#Parameters\n<[0] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | | \n<[1] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | | \n";
public static string sampleSection3 = "#Parameters\n";
public static string sampleRecord = "#3|ScreenNumber|0|1| | |1|0| |\n#Parameters\n<[0] >| 1 | 0 | 0 | 0 | 0 | 0.000000 | 0.000000 | 0.000000 | | | |\n#Alarm\n#History\n#ItemParameters\n<[0] >| 1 | 1 | \n";
public static string sampleFile =
@"// 8.10 - Application Tags
#1|SelectedWindowOnNavBar|0|1| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#2|ScreenName|0|3| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#3|ScreenNumber|0|1| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#4|NavDisabled|0|1| | |0|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#5|PLC_1_IPAddress|0|3| | |1|0| |
#Parameters
<[0]>|1|1|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#6|NumOfMeters|0|3| | |1|0| |
#Parameters
<[0]>|1|1|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#7|ComputerName|0|3| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#8|TZPosition|0|1| | |0|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#9|NewTime|0|3| | |1|0| |
#Parameters
<[0]>|1|0|0|0|0|0.000000|0.000000|0.000000| | | |
#Alarm
#History
#ItemParameters
<[0]>|1|1|
#";
}
public class TagTXTParser
{
public record TagProperies(int Id, string Name, List<string> others);
public record TagRecordSection(List<string>[] items);
public class TagRecord
{
public int Id { get; set; } //** first element of First line
public string Name { get; set; }
public List<string> Properties { get; set; }
public TagRecordSection Parameters { get; set; }
public TagRecordSection Alarm { get; set; }
public TagRecordSection History { get; set; }
public TagRecordSection ItemParameters { get; set; }
}
public static TextParser<char> hash = Character.EqualTo('#');
public static TextParser<TextSpan> eol = Span.EqualTo('\n').Or(Span.EqualTo("\r\n"));
public static TextParser<char> pipe = Character.EqualTo('|');
public static TextParser<string> text = Character.ExceptIn('#', '|', '\n', '\r').Many().Select(x => new string(x));
public static TextParser<string> firstLine = from _ in Span.EqualTo("//")
from rest in text
from end in eol
select new string(rest.ToCharArray());
public static TextParser<char[]> heading =
from h in hash
from name in Character.Letter.AtLeastOnce()
from end in eol
select name;
public static TextParser<List<string>> itemList = //from _ in eol
from items in text.ManyDelimitedBy<string, char>(pipe)
from end in eol
select new List<string>(items);
public static TextParser<TagProperies> propertyList =
from _ in hash
from id in Character.Digit.AtLeastOnce()
from endOfId in pipe
from name in text
from endOfName in pipe
from otherItems in itemList
select new TagProperies(Convert.ToInt32(new string(id)), name, otherItems);
public static TextParser<TagRecordSection> section =
from _ in heading
from items in itemList.Many()
select new TagRecordSection(items);
public static TextParser<TagRecord> record =
from props in propertyList
from sections in section.Repeat(4)
select new TagRecord()
{
Id = props.Id,
Name = props.Name,
Properties = props.others,
Parameters = sections[0],
Alarm = sections[1],
History = sections[2],
ItemParameters = sections[3]
};
public static TextParser<TagRecord[]> TagRecordsFileParser =
from _ in firstLine
from records in record.Many()
from end in hash
select records;
}
您需要在 TagRecordsFileParser
中添加对 .Try()
的调用:
public static TextParser<TagRecord[]> TagRecordsFileParser =
from _ in firstLine
from records in record.Try().Many() // <--- HERE
from end in hash
select records;
原因是因为当你刚刚调用 record.Many()
时,它会遍历你的 9 条记录,然后看到另一个散列,所以它认为它是另一个 record
。解析器“消耗”了散列,但事实证明它根本不是一条记录,这导致它失败。调用 record.Try().Many()
告诉 Superpower 尝试解析尽可能多的 record
,但如果其中一个失败,不要让整个解析器失败,只需回溯已经消耗的内容,然后继续其余的解析器定义。
您要做的另一件事是在解析输入时添加对 .AtEnd()
的调用:
var result9 = TagTXTParser.TagRecordsFileParser.AtEnd().TryParse(sampleFile);
这样,解析器只有在成功解析您的整个输入时才会成功。这将使您的输入始终必须以单个 #
字符结尾。否则,如果文件以 #x
之类的结尾,解析器将成功,我认为您不想要。