The Definitive Antlr 4 第6章学习笔记

本节学习了三个例子,分别是分析逗号分割的数据文件,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);
}

生成结果:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值