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. 改ucosii的中断禁止和恢复代码,这是一个荒谬的错误【 mrs msr】

    ucosii原来的禁止中断以及恢复中断的代码是最简的,但是使用之前,必须声明一个固定名为 OS_CPU_SR   cpu_sr 的变量,吊在那里感觉很怪. ;********************* ...

  2. C#反射 入门学习 02

    获取Type类型的构造函数 前言                  有了前面的 C#反射 入门学习 01的知识,学习这篇估计是搓搓有余,它教会了我们获取方法的相关信息的两种形式与 使用反射调用方法,  ...

  3. 03-Foundation中NSMutableArray遍历、复制和排序

    目录: 一.NSString补充 二.NSMutableArray可变数组 三.遍历 四.NSArray支持的新语法 五.数组复制 六.数组的排序 SDK.API.Foundation.Cocoa是什 ...

  4. APP迁移

    APP架子迁移 在完成上一篇之后,断断续续的开始重构我的Android项目代码,现在终于完成了.在重构期间又仔细阅读了一些开源项目的源码及文章,并询问了一些大神思路,按照理解自己完成了MVP结构的重构 ...

  5. SQL2005、2008、2000 清空删除日志

    SQL2005清空删除日志: 代码如下: Backup Log DNName with no_log  '这里的DNName是你要收缩的数据库名,自己注意修改下面的数据库名,我就不再注释了. go d ...

  6. 基于visual Studio2013解决C语言竞赛题之0610冒泡排序函数

      题目

  7. ajax异步请求实例

    1. 问题分析 用户管理显示页面:usermanagement.tpl(也可以说是MVC中的V,即视图) 用户管理数据发送页面:usermanagement.php(也可以说是MVC中的M,即模型) ...

  8. UF访问,一些对用友最新的旗舰级产品U9一些引进(图像)

    昨天,北京用友公司的本地UF马平之和几个同事总和.并且e创客网站访问者创始人江.双方进行了友好的交流.首先,我们公司的历史.销售.或产品介绍. 然后.用友分公司的总水平介绍了用友的情况下,,马总介绍了 ...

  9. android设置eclipse中的自动提示功能

    菜单window->Preferences->Java->Editor->Content Assist->Enable auto activation 选项要打上勾 (并 ...

  10. perl 回调函数

    在计算机程序设计中,回调函数,或简称回调(Callback),是指通过函数参数传递到其它代码的,某一块可执行代码的引用.这一设计允许了底层代码调用在高层定义的子程序. 没啥不好理解的呀,就是向函数的参 ...