今天写的是关于重复文法的解析,ABNF和BNF相比,一个明显的差异就是引入了重复语法,使得我们可以方便的让一个文法元素重复若干次。
例如30"B"表示30个字母B,30*60表示最少30个,最多60个字母B,等等。
先来看看解析部分的代码:
/*
This file is one of the component a Context-free Grammar Parser Generator,
which accept a piece of text as the input, and generates a parser
for the inputted context-free grammar.
Copyright (C) 2013, Junbiao Pan (Email: panjunbiao@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// repetition = [repeat] element
// DIGIT = %x30-39
protected Repetition repetition() throws IOException, MatchException {
Repeat repeat = null;
// 若以数字或者星号开头,则进入repeat
if (match(is.peek(), 0x30, 0x39) || match(is.peek(), '*')) {
repeat = repeat();
}
// element是必须的
Element element = element();
return new Repetition(repeat, element);
}
// repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)
protected Repeat repeat() throws IOException, MatchException {
int min = 0, max = 0;
// 如果repeat是以星号开头,则重复的最小次数为0次,即repeat后面的element可以不出现。
if (match(is.peek(), '*')) {
is.read();
// 如果星号后面有数字,则重复的最大次数是该数字所表示的次数,否则最大次数没有限制
if (match(is.peek(), 0x30, 0x39)) {
while (match(is.peek(), 0x30, 0x39)) {
max = max * 10 + Integer.valueOf(String.valueOf((char)is.read()));
}
}
return new Repeat(min, max);
} else if (match(is.peek(), 0x30, 0x39)) {
// repeat是以数字开头,其值表示重复的最小次数
while (match(is.peek(), 0x30, 0x39)) {
min = min * 10 + Integer.valueOf(String.valueOf((char)is.read()));
}
// 如果有星号,则表示有范围
if (match(is.peek(), '*')) {
is.read();
// 星号后面接着数字,表示重复的最大次数,否则最大次数没有限制
if (match(is.peek(), 0x30, 0x39)) {
while (match(is.peek(), 0x30, 0x39)) {
max = max * 10 + Integer.valueOf(String.valueOf((char)is.read()));
}
}
return new Repeat(min, max);
} else {
// 没有星号,表示固定的重复次数
return new Repeat(min, min);
}
} else {
throw new MatchException("['0'-'9', '*']", is.peek(), is.getPos(), is.getLine());
}
}
接下来是单元测试部分,直接看代码吧,代码会说话,嘿嘿:/*
This file is one of the component a Context-free Grammar Parser Generator,
which accept a piece of text as the input, and generates a parser
for the inputted context-free grammar.
Copyright (C) 2013, Junbiao Pan (Email: panjunbiao@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)
@Test
public void testRepeat() throws Exception {
Tester<Repeat> tester = new Tester<Repeat>() {
@Override
public Repeat test(AbnfParser parser) throws MatchException, IOException {
return parser.repeat();
}
};
String input;
Assert.assertEquals(new Repeat(0,0), AbnfParserFactory.newInstance("*").repeat());
Assert.assertEquals(new Repeat(0,0), AbnfParserFactory.newInstance("**").repeat());
Assert.assertEquals(new Repeat(0,0), AbnfParserFactory.newInstance("*C").repeat());
Assert.assertEquals(new Repeat(1,1), AbnfParserFactory.newInstance("1").repeat());
Assert.assertEquals(new Repeat(1,1), AbnfParserFactory.newInstance("1M").repeat());
Assert.assertEquals(new Repeat(2,0), AbnfParserFactory.newInstance("2*").repeat());
Assert.assertEquals(new Repeat(2,0), AbnfParserFactory.newInstance("2*_").repeat());
Assert.assertEquals(new Repeat(0,3), AbnfParserFactory.newInstance("*3").repeat());
Assert.assertEquals(new Repeat(0,3), AbnfParserFactory.newInstance("*3").repeat());
Assert.assertEquals(new Repeat(0,3), AbnfParserFactory.newInstance("*3J").repeat());
Assert.assertEquals(new Repeat(4,9), AbnfParserFactory.newInstance("4*9").repeat());
Assert.assertEquals(new Repeat(4,9), AbnfParserFactory.newInstance("4*9#").repeat());
Assert.assertEquals(new Repeat(5,0), AbnfParserFactory.newInstance("5**").repeat());
Assert.assertEquals(new Repeat(6, 0), AbnfParserFactory.newInstance("6*B").repeat());
Assertion.assertMatchException("", tester, 1, 1);
Assertion.assertMatchException("#", tester, 1, 1);
}
// element = rulename / group / option /
// char-val / num-val / prose-val
// repetition = [repeat] element
// DIGIT = %x30-39
@Test
public void testRepetition() throws Exception {
Tester<Repetition> tester = new Tester<Repetition>() {
@Override
public Repetition test(AbnfParser parser) throws MatchException, IOException {
return parser.repetition();
}
};
Assertion.assertMatch(
"B", tester,
new Repetition(new RuleName("", "B")),
2, 1);
Assertion.assertMatch("1B", tester,
new Repetition(new Repeat(1, 1), new RuleName("", "B")),
3, 1);
Assertion.assertMatch("2*6B", tester,
new Repetition(new Repeat(2, 6), new RuleName("", "B")),
5, 1);
Assertion.assertMatch("3*B", tester,
new Repetition(new Repeat(3, 0), new RuleName("", "B")),
4, 1);
Assertion.assertMatch("*8B", tester,
new Repetition(new Repeat(0, 8), new RuleName("", "B")),
4, 1);
Assertion.assertMatch("*B", tester,
new Repetition(new Repeat(0, 0), new RuleName("", "B")),
3, 1);
Option option = AbnfParserFactory.newInstance("[B]").option();
Assertion.assertMatch(
"[B]", tester,
new Repetition(option),
4, 1);
Assertion.assertMatch("1[B]", tester,
new Repetition(new Repeat(1, 1), option),
5, 1);
Assertion.assertMatch("2*6[B]", tester,
new Repetition(new Repeat(2, 6), option),
7, 1);
Assertion.assertMatch("3*[B]", tester,
new Repetition(new Repeat(3, 0), option),
6, 1);
Assertion.assertMatch("*8[B]", tester,
new Repetition(new Repeat(0, 8), option),
6, 1);
Assertion.assertMatch("*[B]", tester,
new Repetition(new Repeat(0, 0), option),
5, 1);
Group group = AbnfParserFactory.newInstance("(B)").group();
Assertion.assertMatch(
"(B)", tester,
new Repetition(group),
4, 1);
Assertion.assertMatch("1(B)", tester,
new Repetition(new Repeat(1, 1), group),
5, 1);
Assertion.assertMatch("2*6(B)", tester,
new Repetition(new Repeat(2, 6), group),
7, 1);
Assertion.assertMatch("3*(B)", tester,
new Repetition(new Repeat(3, 0), group),
6, 1);
Assertion.assertMatch("*8(B)", tester,
new Repetition(new Repeat(0, 8), group),
6, 1);
Assertion.assertMatch("*(B)", tester,
new Repetition(new Repeat(0, 0), group),
5, 1);
CharVal charVal = AbnfParserFactory.newInstance("\"ABC\"").char_val();
Assertion.assertMatch(
"\"ABC\"", tester,
new Repetition(charVal),
6, 1);
Assertion.assertMatch("1\"ABC\"", tester,
new Repetition(new Repeat(1, 1), charVal),
7, 1);
Assertion.assertMatch("2*6\"ABC\"", tester,
new Repetition(new Repeat(2, 6), charVal),
9, 1);
Assertion.assertMatch("3*\"ABC\"", tester,
new Repetition(new Repeat(3, 0), charVal),
8, 1);
Assertion.assertMatch("*8\"ABC\"", tester,
new Repetition(new Repeat(0, 8), charVal),
8, 1);
Assertion.assertMatch("*\"ABC\"", tester,
new Repetition(new Repeat(0, 0), charVal),
7, 1);
Element numVal = AbnfParserFactory.newInstance("%x00-FF").num_val();
Assertion.assertMatch(
"%x00-FF", tester,
new Repetition(numVal),
8, 1);
Assertion.assertMatch("1%x00-FF", tester,
new Repetition(new Repeat(1, 1), numVal),
9, 1);
Assertion.assertMatch("2*6%x00-FF", tester,
new Repetition(new Repeat(2, 6), numVal),
11, 1);
Assertion.assertMatch("3*%x00-FF", tester,
new Repetition(new Repeat(3, 0), numVal),
10, 1);
Assertion.assertMatch("*8%x00-FF", tester,
new Repetition(new Repeat(0, 8), numVal),
10, 1);
Assertion.assertMatch("*%x00-FF", tester,
new Repetition(new Repeat(0, 0), numVal),
9, 1);
ProseVal proseVal = AbnfParserFactory.newInstance("<ABC>").prose_val();
Assertion.assertMatch(
"<ABC>", tester,
new Repetition(proseVal),
6, 1);
Assertion.assertMatch("1<ABC>", tester,
new Repetition(new Repeat(1, 1), proseVal),
7, 1);
Assertion.assertMatch("2*6<ABC>", tester,
new Repetition(new Repeat(2, 6), proseVal),
9, 1);
Assertion.assertMatch("3*<ABC>", tester,
new Repetition(new Repeat(3, 0), proseVal),
8, 1);
Assertion.assertMatch("*8<ABC>", tester,
new Repetition(new Repeat(0, 8), proseVal),
8, 1);
Assertion.assertMatch("*<ABC>", tester,
new Repetition(new Repeat(0, 0), proseVal),
7, 1);
Assertion.assertMatchException("**", tester, 2, 1);
Assertion.assertMatchException("1", tester, 2, 1);
Assertion.assertMatchException("*1", tester, 3, 1);
Assertion.assertMatchException("*(", tester, 3, 1);
Assertion.assertMatchException("*[", tester, 3, 1);
Assertion.assertMatchException("1*", tester, 3, 1);
Assertion.assertMatchException(".", tester, 1, 1);
}

本文详细解析了重复文法的概念及其在ABNF中的应用,包括重复文法的解析过程、代码实现以及通过单元测试验证实现的正确性。
3117

被折叠的 条评论
为什么被折叠?



