1. JDK环境 http://java.sun.com/
JDK bin路径设置。如果只是生成C#程序,装JRE就可以,生成Java程序,编译测试才需要JDK。网上Java的示例多一些,所以不妨装个Eclipse。
2. 安装ANTLR v3 http://www.antlr.org/download.html
我没有用ANTLRWorks,下载的ANTLR 3.0.1 source distribution,用命令行生成。解压后antlr-3.0.1\lib目录下的所有jar都需要包括到CLASSPATH中。
Getting started:
按照http://www.antlr.org/wiki/display/ANTLR3/FAQ+-+Getting+Started可以把示例跑起来。
示例1 C#
下面例子取自http://www.antlr.org/wiki/display/ANTLR3/Five+minute+introduction+to+ANTLR+3,生成C#,可以完整run起来,我做了些修改,使得运行后可以看到效果。
1. 建一个工作目录Antlr(名字无所谓)
2. 在工作目录中建立一个SimpleCalc.g语法文件,内容如下:
grammar SimpleCalc
;
options {
language = CSharp ;
}
tokens {
PLUS = ' + ' ;
MINUS = '-' ;
MULT = '*' ;
DIV = ' / ' ;
}
@members {
static void Main ( string[] args )
{
string source = Console . ReadLine ();
SimpleCalcLexer lex = new SimpleCalcLexer ( new ANTLRStringStream ( source ));
CommonTokenStream tokens = new CommonTokenStream ( lex );
SimpleCalcParser parser = new SimpleCalcParser ( tokens );
try
{
Console . WriteLine ( parser . expr ());
}
catch ( RecognitionException e )
{
Console . Error . WriteLine ( e . StackTrace );
}
Console . ReadLine ();
}
}
// PARSER RULES
expr returns [int value] :
e = term { $ value =$ e . value ; }
(
PLUS e = term { $ value += $ e . value ; }
|
MINUS e = term { $ value - = $ e . value ; }
) * ;
term returns [int value] :
e = factor { $ value = $ e . value ; }
(
MULT e = factor { $ value * = $ e . value ; }
|
DIV e = factor { $ value /= $ e . value ; }
) * ;
factor returns [int value] : NUMBER { $ value = int . Parse ($ NUMBER . text ); } ;
// LEXER RULES
NUMBER : ( DIGIT )+ ;
WHITESPACE : ( ' \ t' | ' ' | ' \ r' | ' \ n'| ' \ u000C' )+ { $ channel = HIDDEN ; } ;
fragment DIGIT : ' 0 ' .. ' 9 ' ;
注意: 语法文件名必须跟grammar指定的名称一致,否则ANTLR生成时会报错error(8): file *** contains grammar SimpleCalc; names must be identical。
options {
language = CSharp ;
}
tokens {
PLUS = ' + ' ;
MINUS = '-' ;
MULT = '*' ;
DIV = ' / ' ;
}
@members {
static void Main ( string[] args )
{
string source = Console . ReadLine ();
SimpleCalcLexer lex = new SimpleCalcLexer ( new ANTLRStringStream ( source ));
CommonTokenStream tokens = new CommonTokenStream ( lex );
SimpleCalcParser parser = new SimpleCalcParser ( tokens );
try
{
Console . WriteLine ( parser . expr ());
}
catch ( RecognitionException e )
{
Console . Error . WriteLine ( e . StackTrace );
}
Console . ReadLine ();
}
}
// PARSER RULES
expr returns [int value] :
e = term { $ value =$ e . value ; }
(
PLUS e = term { $ value += $ e . value ; }
|
MINUS e = term { $ value - = $ e . value ; }
) * ;
term returns [int value] :
e = factor { $ value = $ e . value ; }
(
MULT e = factor { $ value * = $ e . value ; }
|
DIV e = factor { $ value /= $ e . value ; }
) * ;
factor returns [int value] : NUMBER { $ value = int . Parse ($ NUMBER . text ); } ;
// LEXER RULES
NUMBER : ( DIGIT )+ ;
WHITESPACE : ( ' \ t' | ' ' | ' \ r' | ' \ n'| ' \ u000C' )+ { $ channel = HIDDEN ; } ;
fragment DIGIT : ' 0 ' .. ' 9 ' ;
3. 命令行,当前路径为工作目录,运行java org.antlr.Tool SimpleCalc.g。这会在工作目录下生成两个文件SimpleCalcLexer.cs和SimpleCalcParser.cs。
4. 用VS建立一个Console工程,把program.cs删掉,把SimpleCalcLexer.cs和SimpleCalcParser.cs添加到工程中,添加引用antlr-3.0.1\runtime\CSharp\bin\net-2.0\Antlr3.Runtime.dll,编译运行。(一种是只引用Antlr3.Runtime.dll,另一种是引用Antlr3.Utility.dll、antlr.runtime.dll、StringTemplate.dll这3个,看是否需要用到StringTemplate的一些特性而定)
5. 在命令行输入表达式,程序会把结果输出来。因为语法文件简单,所以只能是简单表达式,包含+, -, *, /操作,不能有括号以及其它非数字字符,运算结果必须为整数(尤其针对除法而言)。例如输入3+4*5,结果为23。
示例2 Java:
取自http://www.antlr.org/wiki/display/ANTLR3/Expression+evaluator,同样是一个简单的求值计算示例,可以使用变量。需要有JDK。
1. 在工作目录中建一个语法文件Expr.g,内容如下:
grammar Expr
;
@header {
import java . util . HashMap ;
}
@members {
/ ** Map variable name to Integer object holding value * /
HashMap memory = new HashMap ();
}
prog: stat + ;
stat: expr NEWLINE {System . out . println ($ expr . value ); }
| ID ' = ' expr NEWLINE
{memory . put ($ ID . text , new Integer ($ expr . value )); }
| NEWLINE
;
expr returns [int value]
: e = multExpr { $ value = $ e . value ; }
( ' + ' e = multExpr { $ value += $ e . value ; }
| '-' e = multExpr { $ value - = $ e . value ; }
) *
;
multExpr returns [int value]
: e = atom { $ value = $ e . value ; } ( '*' e = atom { $ value * = $ e . value ; } ) *
;
atom returns [int value]
: INT { $ value = Integer . parseInt ($ INT . text ); }
| ID
{
Integer v = ( Integer ) memory . get ($ ID . text );
if ( v! = null ) $ value = v . intValue ();
else System . err . println ( " #ff0000 variable " +$ ID . text );
}
| ' ( ' expr ' ) ' { $ value = $ expr . value ; }
;
ID : ( 'a' .. 'z'|'A' .. 'Z' )+ ;
INT : ' 0 ' .. ' 9 ' + ;
NEWLINE:' \ r'? ' \ n' ;
WS : ( ' '|' \ t' )+ {skip (); } ;
2. 命令行,当前路径为工作目录,运行java org.antlr.Tool Expr.g。这会在工作目录下生成两个文件ExprLexer.java和ExprParser.java。
@header {
import java . util . HashMap ;
}
@members {
/ ** Map variable name to Integer object holding value * /
HashMap memory = new HashMap ();
}
prog: stat + ;
stat: expr NEWLINE {System . out . println ($ expr . value ); }
| ID ' = ' expr NEWLINE
{memory . put ($ ID . text , new Integer ($ expr . value )); }
| NEWLINE
;
expr returns [int value]
: e = multExpr { $ value = $ e . value ; }
( ' + ' e = multExpr { $ value += $ e . value ; }
| '-' e = multExpr { $ value - = $ e . value ; }
) *
;
multExpr returns [int value]
: e = atom { $ value = $ e . value ; } ( '*' e = atom { $ value * = $ e . value ; } ) *
;
atom returns [int value]
: INT { $ value = Integer . parseInt ($ INT . text ); }
| ID
{
Integer v = ( Integer ) memory . get ($ ID . text );
if ( v! = null ) $ value = v . intValue ();
else System . err . println ( " #ff0000 variable " +$ ID . text );
}
| ' ( ' expr ' ) ' { $ value = $ expr . value ; }
;
ID : ( 'a' .. 'z'|'A' .. 'Z' )+ ;
INT : ' 0 ' .. ' 9 ' + ;
NEWLINE:' \ r'? ' \ n' ;
WS : ( ' '|' \ t' )+ {skip (); } ;
3. 建立Test.java文件,测试代码如下:
import
org.antlr.runtime.
*
;
public class Test {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
ExprLexer lexer = new ExprLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExprParser parser = new ExprParser(tokens);
parser.prog();
}
}
4. 编译java文件: javac Test.java ExprLexer.java ExprParser.java。
public class Test {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
ExprLexer lexer = new ExprLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExprParser parser = new ExprParser(tokens);
parser.prog();
}
}
5. 运行测试: java Test。运行后命令行为等待输入状态,例如输入:
x=1
y=2
3*(x+y)+12
<EOF>
将显示结果为21。在Windows DOS中输入EOF的方法为Ctrl+Z,回车。