基于Predictive Parsing的ABNF语法分析器(十)——AbnfParser文法解析器之数值类型(num-val)
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)的更多相关文章
- 基于Predictive Parsing的ABNF语法分析器(十三)——rulelist、rule、rulename、define-as和elements
我们来看看rulelist,它是整个ABNF文法的入口,就是说一个ABNF文法就是一个规则列表rulelist.一个rulelist由若干个rule规则组成,每个rule由规则名rulename.定义 ...
- 开源语法分析器--ANTLR
序言 有的时候,我还真是怀疑过上本科时候学的那些原理课究竟是不是在浪费时间.比方学完操作系统原理之后我们并不能自己动手实现一个操作系统:学完数据库原理我们也不能弄出个像样的DBMS出来:相同,学完 ...
- C# 语法分析器(二)LR(0) 语法分析
系列导航 (一)语法分析介绍 (二)LR(0) 语法分析 (三)LALR 语法分析 (四)二义性文法 (五)错误恢复 (六)构造语法分析器 首先,需要介绍下 LALR 语法分析的基础:LR(0) 语法 ...
- 编译原理简单语法分析器(first,follow,分析表)源码下载
编译原理(简单语法分析器下载) http://files.cnblogs.com/files/hujunzheng/%E5%8A%A0%E5%85%A5%E5%90%8C%E6%AD%A5%E7%AC ...
- Tiny语法分析器(递归下降分析法实现)
递归规约规则是这样的 program→stmt-sequence stmt-sequence→stmt-sequence;statement|statement statement→if-stmt|r ...
- 有没有好用的开源sql语法分析器? - 匿名用户的回答 - 知乎
有没有好用的开源sql语法分析器? - 匿名用户的回答 - 知乎 presto,hive,drill,calcite,sparksq
- SQLite Lemon 语法分析器学习与使用
本文是浙江大学出版社的<LEMON语法分析生成器(LALR 1类型)源代码情景分析>学习笔记. 用到的Windows下的编译器介绍MinGW(http://www.mingw.org/): ...
- [Swift]LeetCode385. 迷你语法分析器 | Mini Parser
Given a nested list of integers represented as a string, implement a parser to deserialize it. Each ...
- 语法分析器初步学习——LISP语法分析
语法分析器初步学习——LISP语法分析 本文参考自vczh的<如何手写语法分析器>. LISP的表达式是按照前缀的形式写的,比如(1+2)*(3+4)在LISP中会写成(*(+ 1 2)( ...
随机推荐
- Cocos2d-x 3.0final手机游戏开发视频教程2014 - 自学编程 -(陆续更新中)
内容: 非常多人问我:沈老师,要不要更新引擎版本号到3.0,更新这么快,以后会不会每一个月都有一次,好怕呀. 我说:无论你曾经是哪个版本号,3.0final是一个架构级别的升级,能够在新项目中果断升级 ...
- java--继承的一些笔记
public class Person { public void display(){ System.out.println("Play Person..."); } stati ...
- HTML 5与HTML 4的区别
严重的拖延者患者,本来前天就打算写的,奈何拖到的今天才...(对拖延症有啥好的建议还望留言下,感激不尽...) 过完年了,跳槽蠢蠢欲动了,看些基础知识好应对面试,正好就整理整理... 正文开始 1.推 ...
- 【译】在Asp.Net中操作PDF – iTextSharp - 操作图片
原文 [译]在Asp.Net中操作PDF – iTextSharp - 操作图片 作为我的iTextSharp系列的文章的第七篇,开始探索使用iTextSharp在PDF中操作图片,理解本篇文章需要看 ...
- python中打印文件名,行号,路径
print "I have a proble! And here is at Line: %s"%sys._getframe().f_lineno PDB,哈哈http://doc ...
- cocos2d-x游戏开发系列教程-坦克大战游戏之子弹和地图碰撞
上篇文章实现了坦克与地图碰撞的检测, 这篇我们继续完成子弹和地图的碰撞检测. 1.先设计一个子弹类Bullet,如下所示: class Bullet : public CCSprite { publi ...
- 一步一步重写 CodeIgniter 框架 (1) —— url 如何映射到具体的方法
CodeIgniter 框架最显著的特征就是 MVC 模式,它的做法就是提取 url 中的'分段', 映射到某个类的某个方法,从而由该方法来输出最终显示的页面内容.那么我们第一课中就是实现一个这样的原 ...
- K&R练习题6-1统计关键词出现的次数
这道练习题训练了: 1.结构体数组 2.二分查找 3.指针操作 ---- 都不难.但非常基础,我认为非常好,做完了记到博客上来,题目见k&R,实现例如以下: /* * Practice of ...
- 在VS中实现webService的一个demo(图解)
在VS中实现webService的一个demo(图解) 先创建一个web项目,创建好web项目后,添加新建项——web服务 在新建好的web服务文件中写如下代码: 生成当前解决方案. 新建一个winf ...
- 基于visual Studio2013解决C语言竞赛题之1043求末尾0个数
题目 解决代码及点评 /* 43. 求n!的末尾有多少个零.可以通过检查n!含有多少个10的因数来求它末尾零的个数. 因为10=2×5,在n!中含有2的因数显然多于含有5的因数. 一 ...