ANBF语法中的数值类型有3种:二进制、十进制和十六进制,可以是一个以点号分隔的数列,也可以是一个数值的范围。例如,%d11.22.33.44.55表示五个有次序的十进制数字“11、22、33、44、55”,而%x80-ff表示一个字节,这个字节的数值可以是在0x80至0xff之间。

我把以点号分隔的数列定义为NumVal,把范围类型的数值定义为RangedNumVal。这两个类实现了Element,其实我觉得应该定义一个接口NumVal(继承Element),然后一个SerialNumVal和一个RangedNumVal(实现NumVal),这样看起来更漂亮?作为一个完美主义者看到现在这个定义真是很蛋疼,有时间再重新考虑吧。

由于二进制、十进制和十六进制的构成都是很相似的,只是进制符号(b、d、x)以及数字符号(01、0123456789、0123456789abcdef)不同而已,为了避免重复地写三个很相像的方法,我投机取巧的定义了一个Matcher接口,这个接口是用来判断字符是否在预设的符号集里面的,没什么技术含量,看代码就明白了。

先来看看解析代码:

/*
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/>.
*/ // bin-val = "b" 1*BIT
// [ 1*("." 1*BIT) / ("-" 1*BIT) ]
// BIT = "0" / "1"
// 二进制解析器
protected Element bin_val() throws IOException, MatchException {
// 真正的解析工作由val方法完成,只要把二进制数的符号集{0、1}通过Matcher实例传递给它就OK了。
return val('b', new Matcher() {
@Override
public boolean match(int value) {
// 如果符号是0或1就匹配
return value == '0' || value == '1';
} @Override
public String expected() {
// 提示符号不在符号集内(仅用于异常情况)
return "['0', '1']";
}
});
} // dec-val = "d" 1*DIGIT
// [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]
protected Element dec_val() throws IOException, MatchException {
// 同上,把十进制的符号集0~9传递给val方法
return val('d', new Matcher() {
@Override
public boolean match(int value) {
// 直到写博客才发现这段代码错了,符号集不应该包含A~F的情形啊,居然单元测试已经通过了,尼玛这是什么测试质量!
// PS:单元测试代码也是我自己写的。。。
return (value >= 0x30 && value <= 0x39) || (value >= 'A' && value <= 'F') || (value >= 'a' && value <= 'f');
} @Override
public String expected() {
// 错误代码,无语了。。。
return "['0'-'9', 'A'-'F', 'a'-'f']";
}
});
} // hex-val = "x" 1*HEXDIG
// [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]
protected Element hex_val() throws IOException, MatchException {
// 将十六进制的符号集通过Matcher实例传递给val方法进行解析
return val('x', new Matcher() {
@Override
public boolean match(int value) {
return (value >= 0x30 && value <= 0x39) || (value >= 'A' && value <= 'F') || (value >= 'a' && value <= 'f');
} @Override
public String expected() {
return "['0'-'9', 'A'-'F', 'a'-'f']";
}
});
} // 解析各个进制
protected Element val(char base, Matcher matcher) throws IOException, MatchException {
// 检查进制符号
assertMatch(is.peek(), base);
int baseValue = is.read();
String from = "";
String val = ""; // 进制符号之后的第一个字符,必须在Matcher定义的字符集内,否则异常
if (matcher.match(is.peek())) {
// 连续读入符合字符的字符,构成NumVal的第一个数值。
while (matcher.match(is.peek())) {
from += (char)is.read();
}
// 第一个数值后面如果是跟着点号,则是一个数列NumVal,如果是-破折号,则是一个范围型数值RangedNumVal,如果都不是,则是单一个数值
if (match(is.peek(), '.')) {
NumVal numval = new NumVal(String.valueOf((char)baseValue));
// 将刚才匹配到的数值作为第一个数值加到将要返回的NumVal中
numval.addValue(from);
// 如果后面跟着点号,则继续加入新的数值到NumVal中
while (match(is.peek(), '.')) {
int next = is.peek(1);
if (!(matcher.match(next))) {
break;
}
is.read();
val = "";
while (matcher.match(is.peek())) {
val += (char)is.read();
}
numval.addValue(val);
}
// 直到不能匹配到点号,数列结束,返回
return numval;
} else if (match(is.peek(), '-')) {
// 这里向前读取两个字符,因此即使破折号后面跟着的不是数字,也能返回单一个数字而且将破折号留给后面的分析程序
// 这是本程序里为数不多的能够具备回溯的代码段之一,嘿嘿。
int next = is.peek(1);
if (!(matcher.match(next))) {
// 如果破折号后面跟的不是数字,则破折号不读入,返回单一数值
NumVal numval = new NumVal(String.valueOf((char)baseValue));
numval.addValue(from);
return numval;
}
// 否则,破折号后面是数值,读取之,并返回RangedNumVal类型
is.read();
val ="";
val += (char)is.read();
while (matcher.match(is.peek())) {
val += (char)is.read();
}
return new RangedNumVal(String.valueOf((char)baseValue), from, val);
} else {
// 第一个数值之后跟的既不是点号,也不是破折号,则返回单一数值
NumVal numval = new NumVal(String.valueOf((char)baseValue));
numval.addValue(from);
return numval;
}
} else {
throw new MatchException(matcher.expected(), is.peek(), is.getPos(), is.getLine());
} } // num-val = "%" (bin-val / dec-val / hex-val)
// 解析num-val
protected Element num_val() throws IOException, MatchException {
String base = "", from ="", val ="";
// 百分号开头
assertMatch(is.peek(), '%');
is.read();
// 根据进制符号选择相应的解析方法(函数)
switch ((char)is.peek()) {
case 'b': case 'B': return bin_val();
case 'd': case 'D': return dec_val();
case 'x': case 'X': return hex_val();
default: throw new MatchException("['b', 'd', 'x']", 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/>.
*/ // bin-val = "b" 1*BIT
// [ 1*("." 1*BIT) / ("-" 1*BIT) ]
// BIT = "0" / "1"
// 测试二进制数的解析
@Test
public void testBin_val() throws Exception {
Tester<String> tester = new Tester<String>() {
@Override
public String test(AbnfParser parser) throws MatchException, IOException {
return parser.bin_val().toString();
}
};
String input;
input = "b1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
input = "b1010";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
input = "B1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
input = "b1.1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
input = "b0101.1111";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
input = "b0000-1111";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+".00").bin_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"-1234").bin_val().toString());
input = "b00.11.00.01.10.00.11.00.11";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).bin_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+".").bin_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"..").bin_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+".bb").bin_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"-00").bin_val().toString()); Assertion.assertMatchException("", tester, 1, 1);
Assertion.assertMatchException("b", tester, 2,1);
Assertion.assertMatchException("bg", tester, 2, 1);
Assertion.assertMatchException("b.", tester, 2, 1);
Assertion.assertMatchException("b-", tester, 2, 1);
} // dec-val = "d" 1*DIGIT
// [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]
// 测试十进制数的解析
@Test
public void testDec_val() throws Exception {
Tester<String> tester = new Tester<String>() {
@Override
public String test(AbnfParser parser) throws MatchException, IOException {
return parser.dec_val().toString();
}
}; String input;
input = "d1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
input = "d1234";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
input = "D1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
input = "d1.2";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
input = "d1234.5678";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
input = "d1234-5678";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+".00").dec_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"-1234").dec_val().toString());
input = "d12.34.56.78.9a.bc.de.f0";
// 看看这里,就明白为什么单元测试测不出十进制数带有a~f符号的问题了,竟然有这样错误的测试用例!!!
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).dec_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+".").dec_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"..").dec_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+".##").dec_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"-00").dec_val().toString()); Assertion.assertMatchException("", tester, 1, 1);
Assertion.assertMatchException("d", tester, 2, 1);
Assertion.assertMatchException("dg", tester, 2, 1);
Assertion.assertMatchException("d.", tester, 2, 1);
Assertion.assertMatchException("d-", tester, 2, 1);
} // hex-val = "x" 1*HEXDIG
// [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]
// 测试十六进制数的解析
@Test
public void testHex_val() throws Exception {
Tester<String> tester = new Tester<String>() {
@Override
public String test(AbnfParser parser) throws MatchException, IOException {
return parser.hex_val().toString();
}
}; String input;
input = "x1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "x1234";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "X1";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "x1.2";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "x1234.5678";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "xabcd.ef";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "xA1.2B";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
input = "x1234-abCD";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input+"-").hex_val().toString());
input = "x12.34.56.78.9a.bc.de.f0";
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input).hex_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input + ".").hex_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input + ".g0").hex_val().toString());
Assert.assertEquals("%" + input, AbnfParserFactory.newInstance(input + "-00").hex_val().toString()); Assertion.assertMatchException("", tester, 1, 1);
Assertion.assertMatchException("x", tester, 2, 1);
Assertion.assertMatchException("xg", tester, 2, 1);
Assertion.assertMatchException("x.", tester, 2, 1);
Assertion.assertMatchException("x-", tester, 2, 1); } // num-val = "%" (bin-val / dec-val / hex-val)
// 综合情况测试
@Test
public void testNum_val() throws Exception {
String input;
input = "%b0101";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%b0101.1010.1111";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%b0101-1111";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%d1234";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%d0123.4567.8901";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%d12345-67890";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%x0123";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%x0123.4567.89ab.CDEF";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
input = "%x0123456789-ABCDEFabcdef09";
Assert.assertEquals(input, AbnfParserFactory.newInstance(input).num_val().toString());
}

本系列文章索引:基于预测的ABNF文法分析器

基于Predictive Parsing的ABNF语法分析器(十)——AbnfParser文法解析器之数值类型(num-val)的更多相关文章

  1. 基于Predictive Parsing的ABNF语法分析器(十三)——rulelist、rule、rulename、define-as和elements

    我们来看看rulelist,它是整个ABNF文法的入口,就是说一个ABNF文法就是一个规则列表rulelist.一个rulelist由若干个rule规则组成,每个rule由规则名rulename.定义 ...

  2. 开源语法分析器--ANTLR

      序言 有的时候,我还真是怀疑过上本科时候学的那些原理课究竟是不是在浪费时间.比方学完操作系统原理之后我们并不能自己动手实现一个操作系统:学完数据库原理我们也不能弄出个像样的DBMS出来:相同,学完 ...

  3. C# 语法分析器(二)LR(0) 语法分析

    系列导航 (一)语法分析介绍 (二)LR(0) 语法分析 (三)LALR 语法分析 (四)二义性文法 (五)错误恢复 (六)构造语法分析器 首先,需要介绍下 LALR 语法分析的基础:LR(0) 语法 ...

  4. 编译原理简单语法分析器(first,follow,分析表)源码下载

    编译原理(简单语法分析器下载) http://files.cnblogs.com/files/hujunzheng/%E5%8A%A0%E5%85%A5%E5%90%8C%E6%AD%A5%E7%AC ...

  5. Tiny语法分析器(递归下降分析法实现)

    递归规约规则是这样的 program→stmt-sequence stmt-sequence→stmt-sequence;statement|statement statement→if-stmt|r ...

  6. 有没有好用的开源sql语法分析器? - 匿名用户的回答 - 知乎

    有没有好用的开源sql语法分析器? - 匿名用户的回答 - 知乎 presto,hive,drill,calcite,sparksq

  7. SQLite Lemon 语法分析器学习与使用

    本文是浙江大学出版社的<LEMON语法分析生成器(LALR 1类型)源代码情景分析>学习笔记. 用到的Windows下的编译器介绍MinGW(http://www.mingw.org/): ...

  8. [Swift]LeetCode385. 迷你语法分析器 | Mini Parser

    Given a nested list of integers represented as a string, implement a parser to deserialize it. Each ...

  9. 语法分析器初步学习——LISP语法分析

    语法分析器初步学习——LISP语法分析 本文参考自vczh的<如何手写语法分析器>. LISP的表达式是按照前缀的形式写的,比如(1+2)*(3+4)在LISP中会写成(*(+ 1 2)( ...

随机推荐

  1. Ubuntu下编译Android JNI最靠谱的方法...

    网上资料太杂乱,搞了大半天都还是没搞懂怎么系统的调用NDK.最后干脆放弃了Win改用Ubuntu编译JNI,虽然编译环境简单了,但是资料却少了不少.几乎没有一篇完整的文章.我想或许是能在Ubuntu下 ...

  2. Axis2(7):将Spring的装配JavaBean发布成WebService

    在现今的Web应用中经常使用Spring框架来装载JavaBean.如果要想将某些在Spring中装配的JavaBean发布成WebService,使用Axis2的Spring感知功能是非常容易做到的 ...

  3. js判断IP js判断域名

    <html> <head> <script language="javascript" type="text/javascript" ...

  4. Android核心基础(十一)

    1.Android的状态栏通知(Notification) 通知用于在状态栏显示消息,消息到来时以图标方式表示,如下: //获取通知管理器 NotificationManager mNotificat ...

  5. Swift - 如何实现字符串的HMAC_SHA1加密

    前段时间有个网友问是否有Swift的HMAC_SHA1算法.这次就专门写篇相关文章进行介绍.要说明HMAC-SHA1,首先要先了解什么是HMAC,什么是SHA. 1,HMAC(散列消息身份验证码:Ha ...

  6. javascript笔记整理(window对象)

    浏览器对象模型 (BOM--Browser Object Model),window对象是BOM中所有对象的核心 A.属性 1.(位置类型-获得浏览器的位置) IE:window.screenLeft ...

  7. 配置nexus仓库

    Nexus有许多默认仓库:Central,Releases,Snapshots,和3rd Party 1.配置central仓库 Nexus内置了Maven中央代理仓库Central.选择仓库列表中的 ...

  8. Cocos2d-x游戏的场景结构布局

  9. [置顶] Windows Phone后台音乐详解一

    应用于: Windows Phone 8 | Windows PhoneOS 7.1 你可以为winphone编写在后台播放音乐的app.这表示即使当用户点击返回或开始按钮离开你的应用界面时,你的应用 ...

  10. java运行脚本语言demo

    public class Test { /** * @param args * @throws IOException  */ public static void main(String[] arg ...