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. 开源 免费 java CMS - FreeCMS1.9 职位管理

    项目地址:http://code.google.com/p/freecms/ 职位管理 管理职位,实现招聘功能. 1. 职位管理 从左側管理菜单点击职位管理进入. 2. 加入职位 在职位列表下方点击& ...

  2. BZOJ 1014: [JSOI2008]火星人prefix( splay + hash )

    用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long ...

  3. mysql禁用autocommit,以及遇到的问题(转)

    MySQL的autocommit(自动提交)默认是开启,其对mysql的性能有一定影响,举个例子来说,如果你插入了1000条数据,mysql会commit1000次的,如果我们把autocommit关 ...

  4. Linux less命令

    less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大.less 的用法比起 more 更加的有弹性.在 more 的时候,我们并没有办法向前面翻 ...

  5. android Gallery滑动不流畅的解决

    import android.content.Context; import android.util.AttributeSet; import android.view.KeyEvent; impo ...

  6. EF 简单的 CRUD、分页 代码笔记

    添加: static void Main(string[] args)        {            CCDBEntities ccdbContext = new CCDBEntities( ...

  7. UML04-顺序图

    1.顺序图由哪几部分组成? 2.下面列出了打印文件时的工作流: 用户通过计算机指定要打印的文件. 打印服务器根据打印机是否空闲,操作打印机打印文件. 如果打印机空闲,则打印机打印文件: 如果打印机忙, ...

  8. urllib模块

    python爬虫-urllib模块   urllib 模块是一个高级的 web 交流库,其核心功能就是模仿web浏览器等客户端,去请求相应的资源,并返回一个类文件对象.urllib 支持各种 web ...

  9. WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

    原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...

  10. Qt5窗口设计

    主窗口设计通常是应用程序界面设计的第一步,主窗口主要分为窗口标题,菜单栏,工具栏和状态栏这四个部分,只要在程序设计中分别对四个项目进行设计就可以实现主窗口的编程了.在下面的例子中,我们就以一个打开文件 ...