现在来看看对于产生式有多个选项的情形,例如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); }
总之单元测试的目标就是想尽办法“虐”你的代码吧,不过我发现这些代码真的经不起虐。。。