本节学习了三个例子,分别是分析逗号分割的数据文件,JSON的解析,及Cymbol语言语法分析。
分析逗号分割的数据
逗号分割的输入文件如下所示。
Details,Month,Amount
Mid Bonus,June,"$2,000"
,January,"""zippo"""
Total Bonuses,"","$5,000"
下面是这种结构文件的定义。
file : hdr row+;
hdr : row;
为了是文法更加清晰,这里引入了hdr。Hdr仅仅是一个行,但将行单独表示使文法更加清晰。规则row是一组由逗号分隔的值组成,结尾是一个换行符。
row : field (‘,’ field)* ‘\r’?’\n’;
下面是field的定义,它由任意的文本,字符串,或是空field构成。
field
: TEXT
|STRING
|
;
TEXT : ~[,\n\r]+;
STRING: '"' ('""'|~'"') '"';
file: hdr row+;
hdr : row;
row : field (',' field)* '\r'?'\n';
field
: TEXT
| STRING
|
;
TEXT : ~[,\r\n]+;
STRING : '"'('""'|~'"')*'"';
输入测试数据。
Detail,Month,Amount
,January,"""zippo"""
生成如下的分析树。
解析JSON
下面是JSON的一个例子,下面将学习如何用Antlr文法来描述JSON文本结构。
{
"antlr.org": {
"owners" : [],
"live" : true,
"speed" : 1e100,
"menus" : ["File", "Help\nMenu"]
}
}
JSON文法可以由对象或一组值构成。因此其结构可以定义为。
json : object
| array
;
Object是一组 (名称,数值)所构成的无序集。一个对象以一个左括号开始,并以右括号结束。每个 名称后都跟有一个 冒号(:),而(名称,数值)对由逗号分隔开。而名称必须是字符串。 为了将上述定义转换为文法结构,首先将上述定义分解,寻找基本的文法结构(顺序,选择,标识符依赖,及嵌套短语)。根据JSON结构描述,首先需要创建一个名为Object的规则。其次无序集可以通过顺序结构,顺序结构是(名,值)对构成的序列。 其次,对象是以括号开始,因此可以通过符号依赖的结构描述。最后通过逗号分隔(名,值)。因此得到下面的Antlr文法定义。
object : '{'pair (','pair)* '}'
| '{' '}'
|
;
pair : STRING ':' value;
接下来看一看数组的定义。数组是一组值的集合。数组以左方括号开始([ ),以右方括号结束( ] ),每个值以逗号(,)分隔。因此可以得到数组的定义
array
: '[' value (','value)* ']'
| '[' ']' // 空数组
;
接下来定义value。value可以是由双引号包含的字符串,数字,布尔值,null,object,或是一个数组,这些结构可以嵌套定义。下面是value的定义。
value
: STRING
| NUMBER
| object
| array
|'true'
|'false'
|'null'
;
规则中直接引用了字符串常量来匹配JSON中的关键字。
接下来定义STRING。字符串可以使零个或多个Unicode字符构成,通过\符号来表示。一个字符被描述为单字符的字符串。
STRING : '"'(ESC | ~[“\\])* '"';
ESC匹配预定义的转义符,或一个Unicode字符序列。
fragment ESC : '\\'([" \\/bfnrt] | UNICODE);
fragment UNICODE: 'u' HEX HEX HEX HEX;
fragment HEX :[0-9a-fA-F];
最后来看一下NUMBER的定义。
NUMBER
: '-'? INT '.' INT EXP ?
| '-'? INT EXP
| '-'? INT
;
fragment INT : ‘0’| [1-9][0-9]*;
fragment EXP :[Ee] [+\-]? INT; // -需要转意,因为原本用来表示范围,而EXP用来定义指数。
最后在分析JSON时需要忽略空白符。
WS : [ \t\n\r]+-> skip;
最后完整的文法如下。
grammar Json;
json :
array
|object;
object: '{' '}'
| '{'pair(',' pair)* '}'
|
;
pair : STRING ':' value;
array : '[' ']'
|'[' value (','value)* ']'
;
value : STRING
| NUMBER
| object
| array
| 'true'
| 'false'
| 'null'
;
STRING : '"' (ESC | ~["\\])* '"';
fragment ESC : '\\' ([ \\/bfnrt] | UNICODE);
fragment UNICODE : 'u' HEX HEX HEX HEX;
fragment HEX : [0-9a-fA-F];
NUMBER
: '-' ? INT '.' INT EXP ?
| '-' ? INT EXP
| '-' ? INT
;
fragment INT : '0' | [1-9][0-9]*;
fragment EXP : [Ee] [+\-]? INT;
WS : [ \t\n\r]+ -> skip;
输入测试数据
{
"pageNum":1,
"list":[{"id":"F8CE9B"}],
"lastPage":1
}
生成分析树
分析Cymbol语言
为了演示如何解析编程语言语法,接下将编写名为Cymbol语言的文法,该文法从C语言继承而来。Cymbol是一个简单的非面向对象的编程语言,与C语言类似,但没有struct。Cymbol的一个例子如下所示。
int g = 9; // 全局变量
int fact(int x) { // 递归求阶乘
if x==0 then return 1;
return x * fact(x-1);
}
该语言有一个全局变量,并有一个递归函数。
首先Cymbol语言可以由一个 或多个全局变量与函数声明构成。
文法如下
file :(functionDecl | varDecal)+;
变量声明由 id 或一个复制表达式构成。
varDecal : typeID ('=' expr)? ';'
而变量类型可以是float、int、或void。
type : 'float' |'int' | 'void';
而函数是由一个类型标识符、ID、参数列表及函数体构成。
functionDecl :type ID '(' formalParameters? ')' block;
formalParameters: formalParameter (',' formalParameter)*;
formalParameter: type ID;
而函数体是由花括号包围,内部可以有嵌套块,变量声明,if语句,return 语句,赋值语句,及函数调用构成。因此文法可以写成如下的形式。接下来是表达式语法。这里可使用的符号–(取反) , !(逻辑非) ,*(乘法),-(减法),()函数调用,数组索引,相等比较,变量,整数,括号表达式。
expr : ID '(' exprList? ')'
| expr '[' expr ']'
| '-'expr
| '!'expr
| expr '*' expr
| expr ('+'|'-') expr
| expr '==' expr
| ID
| INT
| '(' expr ')'
;
最后该语言完整文法如下。
grammar Cymbol;
file : (functionDecl | varDecal)+;
varDecal : type ID ('=' expr)? ';';
functionDecl : type ID '(' formalParameters? ')' block;
formalParameters : formalParameter (',' formalParameter)*;
formalParameter : type ID;
type : 'float' | 'int' | 'void';
block : '{' stat* '}';
stat : block
| varDecal
| 'if' expr 'then' stat ('else' stat)?
| 'return' expr? ';'
| expr '=' expr ';'
| expr ';'
;
expr : ID '(' exprList? ')'
| expr '[' expr ']'
| '-'expr
| '!'expr
| expr '*' expr
| expr ('+'|'-') expr
| expr '==' expr
| ID
| INT
| '(' expr ')'
;
exprList : expr (',' expr)*;
ID : [a-zA-Z]+;
INT : '0' | [1-9][0-9]*;
WS : [ \t\n\r]+ -> skip;
测试输入:
int g = 9;
int fact(int x){
if x==0 then return1;
return x*fact(x-1);
}
生成结果: