`
zhoulianglg
  • 浏览: 18362 次
  • 性别: Icon_minigender_1
  • 来自: 南京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

antlr笔记

 
阅读更多

文法定义基础

规则的表示: ANTLR用A:a; 来表示规则。ANTLR的规则要以分号“;”结束。在规则中有几种运算关系,

选择、连接、重复、可选。

→连接“ ”:规则A : a b c; a、b、c之间用空格分隔。此规则接收句型abc,符号a、

b、c是按顺序连接起来的关系。

→选择“| ”:规则A : a | b| c; “|”表示“或”的关系,符号A可以推导出a或b

或c,也就是在a、b、c中选择。这要比写成A : a; A : b; A : c;方便得多。连接和选择

可以联合起来使用,如A : ab c |c d e;。有进也会使句型的数量增多如:A : B D; B :

a | b; D : c | d;这时符号A推导出的句型有 ac、ad、bc、bd四种。

→重复“*,+”:规则A : a*;“*”表示a可以出现0次或多次。A : a*; 相当于A : A a |

;。这样可以避免递归的定义,可文法定义中递归往往引起文法的二义性。如果a至少要出

现一次可以表示为A : a+;“+”表示a可以出现1次或多次。相当于A : A a | a;。重复

可以和连接、选择一起使用如:A : a*b | c+d;。

→可选“?”:规则A : a?;“?”表示a可以出现0次或1次,即a可有可无。相当于A : a |

;。可选可以和连接、选择、重复一起使用如:A : a* b?| c+ d?;。

→子规则“()”:规则A : (a b) | b; a与b在括号中,这样“(ab)”形成了一个子规

则,也就是说可以把规则写成A : B| b; B : a b;两个规则表示,我们把B规则用括号括

起来放到A规则中这样就是A规则的子规则了。利用子规则也可以把多个符一起进行描述,

A : (a b c)* 规则中a、b、c三个符号可以一起重复0次或多次。子规则有利于我们把很

复杂的多个规则写到一起,有时这样写会使文法既简练又直观。子规则和前面的各种特性用

到一起可以把复杂的文法写的很浓缩。如:A : (a b c)* | (c d)+ e?;。

值得注意的是如果我们的规则中有“()”的字符该如何表示?因为子规则也是用“()”

表示的。在ANTLR中表示字符要用“’”单引号括起来,用‘(’‘)’来表示括号字符。前

面讲到的表示文法规则的符号“| * + () ?”叫做文法的元符号。

→注释“// /**/”:和一般编程语言一样,ANTLR在文法定义中也可以添加注释。用//

来添加单行注释,如规则E : ‘(’ E ‘)’ | INT // E表示算术表达式。用/*…*/添加

多行注释,与C++相同。


→“..”符号,从上面的LETTER示例可以看出,定义一个表示英文字母的符号写起来非

常繁琐。为了让定义变得简单ANTLR加入“..”符号通过指定首尾的字符可以很方便的定义

一定范围内的字符。

LETTER : ‘A’ .. ‘Z’| ‘a’ .. ‘z’;

→“~”符号,如果我们想表示除某些符号以外的符号时,可以使用“~”符号。“~”代表

取反的意思。

A : ~ ‘B’;

符号A匹配除字符“B”以外的所有字符。

A : ~ (‘A’ |‘B’); B : ~(‘A’.. ‘B’); C : ~‘\u00FF';

上面的例子中定义三个符号。符号A匹配除字符“A”和“B”以外的所有字符,符号B

匹配除大写字符母以外的所有字符。符号C匹配除编码为“u00FF”的字符以外的所有字符。

→“.”符号,ANTLR中可以用“.”表示单个任意字符,起通配符的作用。

A : .; B : .*; C : .*‘C’; D : ~ .;//error

上面的例子中符号A匹配一个任意字符,符号B符号匹配0到多个任意字符,符号C

匹配0到多个任意字符直到遇到字符“C”为止。D的定义是错误的,不能定义任意字符以

外的字符。

→skip()方法

有些字符是不属于源程序范畴内的,这些字符在分析过程中应该忽略掉。在ANTLR中可

以在词法定义中加入skip();,(如果是C#为目标语言为Skip();)。在规则的定义的之后与

表示定义结束的分号之前加入“{skip();}”。例如下面定义了一个跳过空白的词法定义。

WS : ( ' ' | '\t' | '\n' | '\r' ) + {skip();} ;

空白符号WS中有空格符、制表符、回车和换行符,当遇到这些字符时词法分析程序会

调用skip()方法跳过这些字符。

B : 'A' | 'B'{Skip();} | 'C' ;

上面的例子中符号B只在匹配字符“B”时跳过,从这个例子可以看出{Skip();}要写在

忽略内容的后面,如果它处于某选择分支中那么它只对某分支起作用。下面我们定义一些实

际中经常出现的词法定义。

→$channel=HIDDEN

有时象注释这样的部分在编译时并不是完全放弃的,如java中在/**….*/的注释形式

中可以添加说明最终生成程序的文档,在.NET中使用 /// 注释形式也有类似的功能。也就

是说我们在编译代码时忽略这些注释,而在分析生成文档时又要用到这些注释。这样的话就

不能使用skip();方法了,skip()方法是抛弃其内容。所以ANTLR中加入了channel属性用

来指定当前匹配的内容是属于哪一个频道,分析过程中关心哪个频道就可以只接收哪个频道

的内容。ANTLR中定义有两个频道常量DEFAULT_CHANNEL和HIDDEN_CHANNEL,一般情况下匹

配的内容都中放到DEFAULT_CHANNEL中。我们可以指定让当前匹配的内容放到哪个频道中。

前面示例中的词法规则可以改成如下形式。

COMMENT : '/*' . * '*/' {$channel=HIDDEN;} ;

LINE_COMMENT :'//' ~ ('\n'| '\r') * '\r'?'\n' {$channel=HIDDEN;} ;

WS : ( ' ' | '\t' | '\n' | '\r' ) + {$channel=HIDDEN;};

COMMENT、LINE_COMMENT和WS词法规则后面加入{$channel=HIDDEN;},这样匹配的字

符就会被存放到HIDDEN频道中。下面将这个修改后的文法编译运行,其结果和使用skip()

方法时是一样的。这就是说存在于HIDDEN频道中的内容被语法分析器忽略了,因为在默认

情况下ANTLR语法分析程序只分析DEFAULT_CHANNEL中的内容。


→options{greedy=false;}

ANTLR词法分析中还可以加入greedy设置项,当greedy=true时当前规则会尽可能的

匹配可以匹配的输入。ANTLR中默认情况greedy为true,所以COMMENT : '/*' . * '*/'

{$channel=HIDDEN;} ;规则中符号“.”可以匹配“*/”字符,也就是说当遇到“*/”字符

时它是匹配“.”还是匹配’*/’出现了二义的情况,前面的例子中已经显示出在这种情况
下分析器是可以正确分析的,但加入greedy=false后可以消除这种二义性,就是说

greedy=false时分析器遇到“*/”字符时会很确定的做为注释结束符号来匹配,这样的话

就消除了二义性。

COMMENT : '/*' ( options {greedy=false;} : . ) * '*/'

{$channel=HIDDEN;} ;

→ +=的用法

修改SimpleAction文法,使能够在类型标识符后可以定义多个变量。这文法中加入了

“+=”操作符功能是将所有定义的变量收集到一个集合当中。

grammar SimpleAction;

variable : type ids+=ID (',' ids+=ID)* ';'

{ System.out.println($type.text);

for(Object t : $ids)

System.out.print(" " + ((Token)t).getText()); }

;

运行此示例输入:intx, y,z; 输出:intx y z 我们来看一下生成的代码。

public final void variable() throws RecognitionException {

Tokenids=null;

Listlist_ids=null;

type_return type1= null;

try{

pushFollow(FOLLOW_type_in_variable9);

type1=type();

_fsp--;

ids=(Token)input.LT(1);

match(input,ID,FOLLOW_ID_in_variable13);

if (list_ids==null) list_ids=newArrayList();

list_ids.add(ids);

loop1:

do{ int alt1=2;

int LA1_0= input.LA(1); if ( (LA1_0==6) ) { alt1=1; }

switch (alt1) {
case1 : {

match(input,6,FOLLOW_6_in_variable16);

ids=(Token)input.LT(1);

match(input,ID,FOLLOW_ID_in_variable20);

if (list_ids==null) list_ids=newArrayList();

list_ids.add(ids);

} break;

default: break loop1;

}

} while (true);

match(input,7,FOLLOW_7_in_variable24);

System.out.println(input.toString(type1.start,type1.stop));

for(Object t : list_ids) System.out.print(" " +
((Token)t).getText());

}

catch (RecognitionException re) ……

}
代码中添加了一个List类型的list_ids对象,每匹配一个ID符号时就加入到list_ids

中。List中存放的是Object类型所以我们输出时要加类型转换

→ @header用法

ANTLR中每一个文法规则都生成对应的函数,这些函数是分析器类的方法。第一章已经

介绍过语法分析器和词法分析器都分别生成XXXParser类和XXXLexer类。当需要向类的顶

端加入代码时可以用@header定义。一般情况下(如java)用@header来加入包名和导入

imports其它java类。

grammar SimpleMember;

@options { language=Java; }
@header { package Simple;}
……

@header在默认的情况下会设置XXXParser类代码,也就是语法分析类。我们也可以显

示的指明设置词法类还是设置语法类。

@parser::header { package Simple.Parser; }

@lexer::header { package Simple.Lexer; }


@header用法

ANTLR中每一个文法规则都生成对应的函数,这些函数是分析器类的方法。第一章已经

介绍过语法分析器和词法分析器都分别生成XXXParser类和XXXLexer类。当需要向类的顶

端加入代码时可以用@header定义。一般情况下(如java)用@header来加入包名和导入

imports其它java类。

grammar SimpleMember;

@options { language=Java; }
@header { package Simple;}
……

@header在默认的情况下会设置XXXParser类代码,也就是语法分析类。我们也可以显

示的指明设置词法类还是设置语法类。

@parser::header { package Simple.Parser; }

@lexer::header { package Simple.Lexer; }


@header用法

ANTLR中每一个文法规则都生成对应的函数,这些函数是分析器类的方法。第一章已经

介绍过语法分析器和词法分析器都分别生成XXXParser类和XXXLexer类。当需要向类的顶

端加入代码时可以用@header定义。一般情况下(如java)用@header来加入包名和导入

imports其它java类。

grammar SimpleMember;

@options { language=Java; }
@header { package Simple;}
……

@header在默认的情况下会设置XXXParser类代码,也就是语法分析类。我们也可以显

示的指明设置词法类还是设置语法类。

@parser::header { package Simple.Parser; }

@lexer::header { package Simple.Lexer; }

→@members用法

在文法中可以用@members定义分析器类的成员,和@header、Actions一样并没有具体

的限制,你可以在其中任何内容如属、方法、事件、内部类和注释等,还有一个重要的应用

是可以用@members重写基类的方法实现一些高级功能。

下面请看示例:

grammar SimpleMember;

options {
language=CSharp;
}
@header {
using System.Collections.Generic;
}
@members {
private List<IToken>RetList = new List<IToken>();
private int Count= 0;
}
variable : type idList
{
foreach(IToken t in RetList)
System.Console.WriteLine(t.Text);
System.Console.WriteLine(Count);
}';';

idList
:ids+=ID (',' ids+=ID)*
{
RetList= new List<IToken>();
foreach(objectt in $ids)
RetList.Add((IToken)t);
Count= $ids.Count;
};
type : 'int' |'float';
INT : ('0'..'9')+;
FLOAT :('0'..'9')+ ('.' ('0'..'9')+)?;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ;
WS : ( ' ' |'\t' |'\r' | '\n' )+ { $channel = HIDDEN; } ;

这个示例实现了与之前的示例相同的功能,但没有使用返回值,idList()函数中利用分

析器类的成员RetList和Count来保存变量的信息,然后在variable()函数中输出。类成

员是在不同的成员函数间传递信息的一种方法,但要注意对类成员赋值和取值的先后顺序,

不然会在分析过程中出现异常。

分享到:
评论

相关推荐

    antlr4读书笔记七八章

    antlr4读书笔记七八章,antlr4读书笔记七八章,antlr4读书笔记七八章

    The Definitive ANTLR4Reference 学习笔记

    The Definitive ANTLR4Reference 学习笔记 The Definitive ANTLR4Reference 学习笔记

    The+Definitive+ANTLR+4+Reference 学习笔记word

    The+Definitive+ANTLR+4+Reference 学习笔记word The+Definitive+ANTLR+4+Reference 学习笔记word

    UniVR-informatica:在维罗纳大学计算机科学系学习期间写的笔记和总结

    UniVR-informatica 注释和为学士学位的计算机科学硕士学位在工程和计算机科学课程摘要。 内容 ...为ANTLR语言课程详述 高手 Magistrale I anno硕士学位(AY 2020-2021): 编程基础和规范语言(第

    awesome-stars-by-sandeep

    为ANTLR v4编写的文法; 期望语法没有动作。 反抗 6077 :up_arrow: 返回索引 脑干 姓名 描述 作者 星星 1个 费拉·弗洛特(FiraFlott) 基于FiraCode和FlottFlott的免费运营商Mono替代方案 科西姆斯特 316 :up...

    很棒

    很棒的星星 我的GitHub明星精选清单! 由产生 内容 ANTLR v4-为ANTLR v4编写的语法;... 曾经是Coursera的作业和笔记。 现在这是一个知识厨房水槽:) 从1970年至今的连续Unix提交历史 -不兼容的分时系统 学习x8

    eecs2311-tab2xml:EECS 2311-2021冬季第2组的项目

    antlr4版本4.9.1 基本用法 有关完整说明和安装说明,请参阅。 在文本框中输入您的文本标签,或按“从文件加载”以将文本标签加载到文件中。 按“转换并保存”将选项卡转换为MusicXML并将输出保存到文件。 屏幕截图 ...

    matlab黑科技代码-Yesrat_Rahman_Portfolio:Yesrat_Rahman_Portfolio

    ANTLR 构架: 角 Vue.js 弹簧/弹簧靴 技术: Linux/Unix GitHub MATLAB Jupyter 笔记本 乳胶 微软Office 欧特克 数据库: PostgreSQL MySQL JPA/Hibernate 查询 个人项目 所有者和创建者 一个全栈 Web 应用程序,它...

    pad-tools:可以除数地操作Presburger算术公式的库

    PAD工具可以除数地操作Presburger算术公式的库笔记公式解析器是使用antlr4生成的,并且还包括使用的语法(请参阅PAD.g4)。 解析器接受prenex正常形式的PAD公式。

Global site tag (gtag.js) - Google Analytics