現在來看看對於產生式有多個選項的情形,例如WSP可以是空格SP或者跳格HTAB。對於這種情況,一般是向前看一個字符,根據這個字符來選擇產生式。當然,如果兩個產生式的起始字符都一樣,那麼只向前看一個字符就不夠了,這種情況下需要向前看2個或者更多。
WSP、c-nl和element的文法解析程序:
/*
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: [email protected])
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/>.
*/
//WSP = SP / HTAB
protected String WSP() throws IOException, MatchException {
// 向前看一個字符
switch (is.peek()) {
// 如果這個字符是0x20,則是SP(空格),調用SP()方法
case 0x20: return SP();
// 如果這個字符是0x09,則是HTAB(跳格),調用HTAB()方法
case 0x09: return HTAB();
// 否則拋出匹配異常MatchException
default: throw new MatchException("[0x20, 0x09]", is.peek(), is.getPos(), is.getLine());
}
}
// c-nl = comment / CRLF
protected String c_nl() throws IOException, MatchException {
// 向前看一個字符
switch (is.peek()) {
// 如果是分號,則是註釋,調用comment()方法進行解析
case ';': return comment();
// 如果是0x0D,則是回車,調用CRLF()方法進行解析
case 0x0D: return CRLF();
// 否則拋出異常
default: throw new MatchException("[';', 0x0D]", is.peek(), is.getPos(), is.getLine());
}
}
// element = rulename / group / option /
// char-val / num-val / prose-val
protected Element element() throws IOException, MatchException {
// 向前看一個字符,如果在0x41~0x5A或0x61~0x7A之間(即大小寫英文字母),則是規則名,調用rulename()方法進行解析
if (match(is.peek(), 0x41, 0x5A) || match(is.peek(), 0x61, 0x7A)) {
return rulename();
}
// 否則再檢查這個字符
switch (is.peek()) {
// 如果是左括號,則是group,調用group()
case '(': return group();
// 如果是左方括號,則調用option()
case '[': return option();
// 如果是雙引號,則調用char_var()
case 0x22: return char_val();
// 如果是百分號,則調用num_val()
case '%': return num_val();
// 如果是左尖括號(小於號),則調用prose_val()
case '<': return prose_val();
// 否則拋出匹配異常
default: throw new MatchException("['(', '[', 0x22, '%', '<']", 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: [email protected])
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/>.
*/
//WSP = SP / HTAB
@Test
public void testWSP() throws Exception {
Tester<String> tester = new Tester<String>() {
@Override
public String test(AbnfParser parser) throws MatchException, IOException {
return parser.WSP();
}
};
Assert.assertEquals(String.valueOf((char)0x09), AbnfParserFactory.newInstance(new char[] {0x09}).WSP());
Assert.assertEquals(String.valueOf((char)0x20), AbnfParserFactory.newInstance(new char[] {0x20}).WSP());
Assert.assertEquals(String.valueOf((char)0x09), AbnfParserFactory.newInstance(new char[] {0x09, 0x09}).WSP());
Assert.assertEquals(String.valueOf((char)0x09), AbnfParserFactory.newInstance(new char[] {0x09, 0x20}).WSP());
Assert.assertEquals(String.valueOf((char)0x20), AbnfParserFactory.newInstance(new char[] {0x20, 0x20}).WSP());
Assert.assertEquals(String.valueOf((char)0x20), AbnfParserFactory.newInstance(new char[] {0x20, 0x09}).WSP());
Assert.assertEquals(String.valueOf((char)0x20), AbnfParserFactory.newInstance(new char[] {0x20, 0x30}).WSP());
Assertion.assertMatchException("", tester, 1, 1);
Assertion.assertMatchException("" + (char)0x08, tester, 1, 1);
}
// c-nl = comment / CRLF
@Test
public void testC_nl() throws Exception {
Tester<String> tester = new Tester() {
public String test(AbnfParser parser) throws MatchException, IOException {
return parser.c_nl();
}
};
Assertion.assertMatch("" + (char)0x0D + (char)0x0A, tester, 1, 2);
Assertion.assertMatch(";" + (char)0x0D + (char)0x0A, tester, 1, 2);
Assertion.assertMatch(";" + (char)0x21 + (char)0x0D + (char)0x0A, tester, 1, 2);
Assertion.assertMatch(";" + (char)0x20 + (char)0x0D + (char)0x0A, tester, 1, 2);
}
// element = rulename / group / option /
// char-val / num-val / prose-val
@Test
public void testElement() throws Exception {
Tester<Element> tester = new Tester<Element>() {
@Override
public Element test(AbnfParser parser) throws MatchException, IOException {
return parser.element();
}
};
String input;
input = "aBcD1234";
RuleName ruleName = AbnfParserFactory.newInstance(input).rulename();
Assertion.assertMatch(input, tester, ruleName, 9, 1);
input = "(aBcD1234/%d88)";
Group group = AbnfParserFactory.newInstance(input).group();
Assertion.assertMatch(input, tester, group, 16, 1);
input = "[aBcD1234/%d88]";
Option option = AbnfParserFactory.newInstance(input).option();
Assertion.assertMatch(input, tester, option, 16, 1);
input = "\"#$%^\"";
CharVal charVal = AbnfParserFactory.newInstance(input).char_val();
Assertion.assertMatch(input, tester, charVal, 7, 1);
input = "%b0101.1010.1111";
Element numVal = AbnfParserFactory.newInstance(input).num_val();
Assertion.assertMatch(input, tester, numVal, 17, 1);
input = "<aBcD1234/%d88>";
ProseVal proseVal = AbnfParserFactory.newInstance(input).prose_val();
Assertion.assertMatch(input, tester, proseVal, 16, 1);
}
總之單元測試的目標就是想盡辦法“虐”你的代碼吧,不過我發現這些代碼真的經不起虐。。。本系列文章索引:基於預測的ABNF文法分析器