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. Hadoop HDFS (3) JAVA訪问HDFS之二 文件分布式读写策略

    先把上节未完毕的部分补全,再剖析一下HDFS读写文件的内部原理 列举文件 FileSystem(org.apache.hadoop.fs.FileSystem)的listStatus()方法能够列出一 ...

  2. 【转】[Mysql] Linux Mysql 日志专题

    原文链接:http://blog.csdn.net/xiaoxu0123/article/details/6258538 1, 设置存放的目录: [root@Linux etc]# more /etc ...

  3. 【转】如何在CentOS/RHEL中安装基于Web的监控系统 linux-das

    Linux-dash是一款为Linux设计的基于Web的轻量级监控面板.这个程序会实时显示各种不同的系统属性,比如CPU负载.RAM使用率.磁盘使用率.网速.网络连接.RX/TX带宽.登录用户.运行的 ...

  4. handler.postDelayed()和timerTask

    public static void scrollToListviewTop(final XListView listView) { listView.smoothScrollToPosition(0 ...

  5. java正则去掉小数点后多余0

    需求:已知字符串为一数字字符形式,多为float,double转换过来,将其后多余的0与.去掉. package test; /** * 去掉多余的.与0 * @author Hust * @Time ...

  6. Route@简单应用

    路由的简单应用(生成URL) 这篇文章讲的核心问题是利用路由系统生成URL但是我们,但是我们不是仅仅生成URL,我们的超链接的href属性中,我们在跳转的时候,都需要URL,我们要将的就是在这些实际情 ...

  7. 遭遇“HTTP 错误 500.19 无法访问请求的页面,因为该页的相关配置数据无效。”

    windows 2008下IIS7 安装ASP.NET 遇到如下错误: HTTP 错误 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效. ...

  8. openssl生成pem,密钥证书的创建

    使用OpenSSL生成证书 首先得安装OpenSSL软件包openssl,安装了这个软件包之后,我们可以做这些事情: o Creation of RSA, DH and DSA Key Paramet ...

  9. 一个必用的javascript框架:underscore.js - wine的思考 - ITeye技术网站

    AngularJS+JqueryMobile+PhoneGap 打造APP « Dogeek AngularJS+JqueryMobile+PhoneGap 打造APP

  10. Study notes for Latent Dirichlet Allocation

    1. Topic Models Topic models are based upon the idea that documents are mixtures of topics, where a ...