今天一鼓作气再写多点东西吧,这个题目差不多接近尾声了。来看看alternation、concatenation、group和option的解析代码:
/*
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/>.
*/
// alternation = concatenation
// *(*c-wsp "/" *c-wsp concatenation)
protected Alternation alternation() throws IOException, MatchException {
Alternation alternation = new Alternation();
// 每个alternation至少有一个候选项,这个候选项的类型是concatenation(连结项)
alternation.addConcatenation(concatenation());
// 从第二个候选项开始,每个候选项都是都是以空格(可选)以及“/”引导的,
// 因此,只要遇到空格或者/号,就认为接下来的又是一个候选项
// 当然,如果遇到空格但后面跟的不是/号,又或者如果/号之后跟的不是候选项,
// 那就只能异常了,因为这个算法不能回溯到空格或者/号之前
while (match(is.peek(), new int[] { 0x20, ';', '/'})) {
// 如遇到空格或者分号,则进入c_wsp()
while (match(is.peek(), 0x20) || match(is.peek(), ';')) {
c_wsp();
}
// 此处必须是/号了,否则异常,没有办法回溯
assertMatch(is.peek(), '/');
is.read();
// /号后面可以跟若干空格或注释
while (match(is.peek(), 0x20) || match(is.peek(), ';')) {
c_wsp();
}
// 空格之后的新的候选项,候选项本身是concatenation,所以进入相应的函数。
alternation.addConcatenation(concatenation());
}
return alternation;
}
// concatenation = repetition *(1*c-wsp repetition)
protected Concatenation concatenation() throws IOException, MatchException {
Concatenation concatenation = new Concatenation();
// 一个concatenation是由至少一个repetition组成的,
// 这些repetition有先后顺序之分,用若干空格隔开
concatenation.addRepetition(repetition());
// 后面有空格或分号,则认为会接着一个repetition
// 其实这样是不严谨的,因为空格后面其实不必然是repetition,
// 也可能是其他文法单位,但作为一个手工编写的解析器
// 暂时接受它诸多的缺陷吧。
while (match(is.peek(), 0x20) || match(is.peek(), ';')) {
while (match(is.peek(), 0x20) || match(is.peek(), ';')) {
c_wsp();
}
concatenation.addRepetition(repetition());
}
return concatenation;
}
// group = "(" *c-wsp alternation *c-wsp ")"
protected Group group() throws IOException, MatchException {
// 一个group以左圆括号引导
assertMatch(is.peek(), '(');
is.read();
// 括号后面的若干空格
while (match(is.peek(), new int[] {0x20, ';', 0x0D})) {
c_wsp();
}
// 一个group包含一个alternation
Alternation alternation = alternation();
while (match(is.peek(), new int[] {0x20, ';', 0x0D})) {
c_wsp();
}
// 以右圆括号结束
assertMatch(is.peek(), ')');
is.read();
return new Group(alternation);
}
// option = "[" *c-wsp alternation *c-wsp "]"
// option与group类似,差别在于是方括号而不是圆括号。
protected Option option() throws IOException, MatchException {
assertMatch(is.peek(), '[');
is.read();
while (match(is.peek(), new int[] {0x20, ';', 0x0D})) {
c_wsp();
}
Alternation alternation = alternation();
while (match(is.peek(), new int[] {0x20, ';', 0x0D})) {
c_wsp();
}
assertMatch(is.peek(), ']');
is.read();
return new Option(alternation);
}
测试用例方面,其实没啥好说的,在测试alternation的时候,主要是将一个比较复杂的concatenation复制给input,然后测试input + "/" + input形式的候选项的情况。
/*
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/>.
*/
// alternation = concatenation
// *(*c-wsp "/" *c-wsp concatenation)
@Test
public void testAlternation() throws Exception {
Tester<Alternation> tester = new Tester<Alternation>() {
@Override
public Alternation test(AbnfParser parser) throws MatchException, IOException {
return parser.alternation();
}
};
Alternation alternation;
String input;
input = "A B C %xff \"abc\" <12-34>";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
Assertion.assertMatch(input + "/" + input, tester, alternation, 50, 1);
input = "A";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
Assertion.assertMatch(input + "/" + input, tester, alternation, 4, 1);
input = "A";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
// TODO
// Does not support currently
Assertion.assertMatchException(input + " / " + input, tester, 3, 1);
input = "(A B C D E)";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
Assertion.assertMatch(input + "/" + input, tester, alternation, 24, 1);
input = "[A B C D E]";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
Assertion.assertMatch(input + "/" + input, tester, alternation, 24, 1);
input = "*(A B C)";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
Assertion.assertMatch(input + "/" + input, tester, alternation, 18, 1);
input = "1*2(A B C)";
alternation = new Alternation();
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
alternation.addConcatenation(AbnfParserFactory.newInstance(input).concatenation());
Assertion.assertMatch(input + "/" + input, tester, alternation, 22, 1);
}
// concatenation = repetition *(1*c-wsp repetition)
@Test
public void testConcatenation() throws Exception {
Tester<Concatenation> tester = new Tester<Concatenation>() {
@Override
public Concatenation test(AbnfParser parser) throws MatchException, IOException {
return parser.concatenation();
}
};
String input;
input = "a b c *a 1*b 1*2c *3d %d88 %x11.22.33 %b00-1111 *(aa bb) *2[a b c] 5*(a/b)";
Assertion.assertMatch(input, tester, AbnfParserFactory.newInstance(input).concatenation(), 75, 1);
input = "a b c d e";
Assertion.assertMatch(input, tester, AbnfParserFactory.newInstance(input).concatenation(), 15, 1);
// TODO
// Does not support currently
input = " a ";
Assertion.assertMatchException(input, tester, 1, 1);
}
// group = "(" *c-wsp alternation *c-wsp ")"
@Test
public void testGroup() throws Exception {
Tester<Group> tester = new Tester<Group>() {
@Override
public Group test(AbnfParser parser) throws MatchException, IOException {
return parser.group();
}
};
Alternation alternation = AbnfParserFactory.newInstance("A/B").alternation();
Assertion.assertMatch("(A/B)", tester, new Group(alternation), 6, 1);
// TODO
// Does not support this case
Assertion.assertMatchException("( A/B )", tester, 9, 1);
}
// option = "[" *c-wsp alternation *c-wsp "]"
@Test
public void testOption() throws Exception {
Tester<Option> tester = new Tester<Option>() {
@Override
public Option test(AbnfParser parser) throws MatchException, IOException {
return parser.option();
}
};
Alternation alternation = AbnfParserFactory.newInstance("A/B").alternation();
Assertion.assertMatch("[A/B]", tester, new Option(alternation), 6, 1);
// TODO
// Does not support this case
Assertion.assertMatchException("[ A/B ]", tester, 9, 1);
}