基於Predictive Parsing的ABNF語法分析器(七)——AbnfParser文法解析器之多個選項的情形(如WSP、c-nl和element)

現在來看看對於產生式有多個選項的情形,例如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文法分析器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章