今天写的是关于重复文法的解析,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: [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/>. */ // 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: [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/>. */ // 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); }