構文解析器のSpracheで特定のパターンを繰り返し読み取るためのメソッド一覧について説明します。
Many()
パターンが0回以上出現する文字列を読み取ります。
Parser<IEnumerable<T>> Many<T>(this Parser<T> parser)
Parser<string> quotedString =
from open in Parse.Char('"')
from value in Parse.CharExcept('"').Many().Text()
from close in Parse.Char('"')
select value;
Assert.Equal("Hello, World!", quotedString.Parse("\"Hello, World!\""));
Assert.Equal("", quotedString.Parse("\"\""));
XMany()
パターンが0回以上出現する文字列を読み取ります。 ただし、XMany はバックトラックが発生しないため、Many よりもエラーメッセージから問題点を見つけやすくなります。
Parser<IEnumerable<T>> XMany<T>(this Parser<T> parser)
// Single record e.g. "(monday)"
Parser<string> record =
from lparem in Parse.Char('(')
from name in Parse.Letter.Many().Text()
from rparem in Parse.Char(')')
select name;
string input = "(monday)(tuesday0(wednesday)(thursday)";
Assert.Equal(["monday"], record.Many().Parse(input));
// unexpected '('; expected end of input
Assert.Throws<ParseException>(() => record.XMany().End().Parse(input));
// unexpected '0'; expected )
Assert.Throws<ParseException>(() => record.XMany().Parse(input));
AtLeastOnce()
パターンが1回以上出現する文字列を読み取ります。
Parser<IEnumerable<T>> AtLeastOnce<T>(this Parser<T> parser)
Parser<IEnumerable<string>> parser = Parse.String("Foo").Text().AtLeastOnce();
Assert.Equal(["Foo", "Foo"], parser.Parse("FooFooBar"));
// unexpected 'B'; expected Foo
Assert.Throws<ParseException>(() => parser.Parse("Bar"));
XAtLeastOnce()
パターンが1回以上出現する文字列を読み取ります。 1個目のパターン以降は、すべて XMany() で読み取りを試みます。
Parser<IEnumerable<T>> XAtLeastOnce<T>(this Parser<T> parser)
Parser<IEnumerable<string>> parser = Parse.String("Foo").Text().XAtLeastOnce();
Assert.Equal(["Foo", "Foo"], parser.Parse("FooFooBar"));
// unexpected 'B'; expected Foo
Assert.Throws<ParseException>(() => parser.Parse("Bar"));
Until()
引数のパターンにマッチするまで文字を読み取り続けます。
Parser<IEnumerable<T>> Until<T, U>(this Parser<T> parser, Parser<U> until)
Parser<string> parser =
from first in Parse.String("/*")
from comment in Parse.AnyChar.Until(Parse.String("*/")).Text()
select comment;
Assert.Equal("this is a comment", parser.Parse("/*this is a comment*/"));
parser.Parse(
@"/*
This comment
can span
over multiple lines*/");
Repeat()
引数で指定した回数繰り返し出現するパターンのみ読み取りします。
Parser<IEnumerable<T>> Repeat<T>(this Parser<T> parser, int count)
Parser<IEnumerable<T>> Repeat<T>(this Parser<T> parser, int? minimumCount, int? maximumCount)
Parser<string> parserRepeat3Times = Parse.Char('a').Repeat(3).Text();
Assert.Equal("aaa", parserRepeat3Times.Parse("aaa"));
Assert.Throws<ParseException>(() => parserRepeat3Times.Parse("aab"));
Parser<string> parser = Parse.Digit.Repeat(3, 6).Text();
Assert.Equal("123", parser.Parse("123"));
Assert.Equal("123456", parser.Parse("123456"));
// 繰り返しの最大値以上は読み取らない
Assert.Equal("123456", parser.Parse("123456789"));
// Unexpected 'end of input'; expected 'digit' between 3 and 6 times, but found 2
Assert.Throws<ParseException>(() => parser.Parse("12"));
Once()
1回のみ出現するパターンのみ読み取りします。 string型をIEnumerable<string>型に変換するときなどに利用します。
Parser<IEnumerable<T>> Once<T>(this Parser<T> parser)
Parser<string> identifier = Parse.Identifier(Parse.Letter, Parse.LetterOrDigit);
Parser<IEnumerable<string>> memberAccess =
from first in identifier.Once()
from subs in Parse.Char('.').Then(_ => identifier).Many()
select first.Concat(subs);
Assert.Equal(["foo", "bar", "baz"], memberAccess.Parse("foo.bar.baz"));
以上です。
参考資料
- sprache/Sprache: A tiny, friendly, C# parser construction library
- Sprache Part 3: Repetition (Many, AtLeastOnce, Until, Repeat, Once) - Justin Pealing
- Many()
- AtLeastOnce()
- Until()
- Repeat()
- Once()