現在來看看對於產生式有多個選項的情形,例如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: 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/>. *///WSP = SP / HTABprotected String WSP() throws IOException, MatchException {// 向前看一個字元switch (is.peek()) {// 如果這個字元是0x20,則是SP(空格),調用SP()方法case 0x20: return SP();// 如果這個字元是0x09,則是HTAB(跳格),調用HTAB()方法case 0x09: return HTAB();// 否則拋出匹配異常MatchExceptiondefault: throw new MatchException("[0x20, 0x09]", is.peek(), is.getPos(), is.getLine());}}// c-nl = comment / CRLFprotected 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-valprotected 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: 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/>. */ //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); }
總之單元測試的目標就是想盡辦法“虐”你的代碼吧,不過我發現這些代碼真的經不起虐。。。