java 是默认的,指定语言为 C#.
options {
language = "CSharp";
}
语法分析器,派生自 Parser。
以下依次为
选项
语法定义
class CalcParser extends Parser;
options {
buildAST = true; // uses CommonAST by default
}
expr
: mexpr (PLUS^ mexpr)* SEMI!
;
mexpr
: atom (STAR^ atom)*
;
atom: INT
;
词法分析器,派生自 Lexer。
class CalcLexer extends Lexer;
WS : (' '
| '/t'
| '/n'
| '/r')
{ _ttype = Token.SKIP; }
;
LPAREN: '('
;
RPAREN: ')'
;
STAR: '*'
;
PLUS: '+'
;
SEMI: ';'
;
protected
DIGIT
: '0'..'9'
;
INT : (DIGIT)+
;
解释器。
定义对 expr 的解释方法。
class CalcTreeWalker extends TreeParser;
expr returns [float r]
{
float a,b;
r=0;
}
: #(PLUS a=expr b=expr) {r = a+b;}
| #(STAR a=expr b=expr) {r = a*b;}
| i:INT {r = Convert.ToSingle(i.getText());}
;
过程为先由词法分析器识别出单词/符号,交由语法分析程序识别出其语境的含义。
细看词法分析器的写法:
class CalcLexer extends Lexer;
// 遇到 tab、换行,识别为 WS
// 对 WS,语法分析器不去识别,注明为可跳过去单词/字符
WS : (' '
| '/t'
| '/n'
| '/r')
{ _ttype = Token.SKIP; }
;
// 括号
LPAREN: '('
;
RPAREN: ')'
;
// 星星
STAR: '*'
;
// 加号
PLUS: '+'
;
// ....
SEMI: ';'
;
// 这些可以继承
protected
// 数字
DIGIT
: '0'..'9'
;
// 整数
// 整数的定义在词法分析器中。
// 如果要做 C# 文法,识别 Console.WriteLine,xxx.yyy() xxx 该在何处定义呢
INT : (DIGIT)+
;
语法分析器:
class CalcParser extends Parser;
// 指定生成抽象语法树
options {
buildAST = true; // uses CommonAST by default
}
// 表达式定义
// 算子,整数
atom: INT
;
// 乘法定义(不支持除法)
// 乘法是算子乘算子乘算子乘算子乘算子。。。。
// 或者直接就是算子。以 星星 为树把。
mexpr
: atom (STAR^ atom)*
;
// 表达式为乘法加乘法加乘法加乘法加乘法加乘法加乘法。。
// 用分号结束
// 以加号作为树把
expr
: mexpr (PLUS^ mexpr)* SEMI!
;
如何解释分析出来的树
class CalcTreeWalker extends TreeParser;
expr returns [float r]
{
float a,b;
r=0;
}
: #(PLUS a=expr b=expr) {r = a+b;}
| #(STAR a=expr b=expr) {r = a*b;}
| i:INT {r = Convert.ToSingle(i.getText());}
;
这个家伙变成 public float expr(AST ) 的方法,处理传入的抽象语法树。
它返回 float r。
具体的代码含义为:
如果传入 AST 把类型为加,亦即由 PLUS^ 建立的树把,把树分成 a b 两枝,分别递归求值。返回二者之和。
如果 AST 把类型为乘。分成两支,相乘。
最后,如果传入的是 int 单词,转为 int 类型数字。
ANTLR 将该文件变成 CalcLexer.cs,CalcParser.cs,CalcParserTokenTypes.cs 和 CalcTreeWalker.cs
在如下 C# 中使用它:
class Calc
{
public static void Main(string[] args)
{
try
{
CalcLexer lexer = new CalcLexer(new CharBuffer(Console.In));
lexer.setFilename("<stdin>");
CalcParser parser = new CalcParser(lexer);
parser.setFilename("<stdin>");
// 分析传入的文本
parser.expr();
// 取 AST 根把
CommonAST t = (CommonAST)parser.getAST();
// Print the resulting tree out in LISP notation
Console.Out.WriteLine(t.ToStringTree());
// 分析器登场
CalcTreeWalker walker = new CalcTreeWalker();
// 使用分析器的方法分析根把
float r = walker.expr(t);
Console.Out.WriteLine("value is "+r); // 出结果
}
catch(TokenStreamException e)
{
Console.Error.WriteLine("exception: "+e);
}
catch(RecognitionException e)
{
Console.Error.WriteLine("exception: "+e);
}
}