JFlex 是 The Fast Lexical Analyser Generator, 是 lex/flex 的 java 版本, JLex 的升级.
帮助手册地址: http://www.jflex.de/manual.html
中文的一些资料: http://wenku.baidu.com/view/542645350b4c2e3f57276338.html
lex/flex 的规则文件(spec)的三部分为: 1.声明/选项; 2.模式和动作; 3.代码被复制的.
而 JFlex 的规则文件的三部分为: 1. 类之前用户代码(同flex 3); 2. 声明/选项; 3. 模式和动作.
注意顺序有所不同.
第二部分中, 有一些重要的选项中摘录如下: (全部的参见文档)
%class Yylex -- 生成的 lex 类名字, 缺省为 Yylex; 另有 %implements %extends 等指令可
指定类的基类和实现的接口等选项.
%{ ... 类中代码... %} -- 放到类中间的代码, 如类方法和成员变量等.
%function -- 指定调用 yylex() 的名字, 缺省为 yylex.
%int -- 指定 yylex() 返回类型为 int, 此情况下对 EOF 返回 -1.
%type -- 指定 yylex() 返回类型为 type, 缺省为 Yytoken 类型, EOF 返回 null.
%debug, %stsandalone -- 可用于生成 main() 方法, 用于调试.
%cup, %byacc -- 可用于和 cup, byacc 组合.
=================================================
例子: word count, 从 flex 中改编(需要调整一些代码和结构)
/* 第一部分: 声明 package, import 略. */
%%
/* 第二部分: 选项,类中代码 */
%class WordCountLexer
%function yylex
%int
%{
/* 类中代码 */
public int chars, words, lines;
public static void main(...) 略
%}
%%
/* 第三部分: 规则和动作 */
[a-zA-Z] { 匹配一个词 print(yytext()); ++words ... }
\n { 匹配新行 print("NEWLINE"); ++... }
. { 匹配其它任何字符. print("OTHER CHAR: " +yytext()); ++... }
需要注意的是, 这里 spec 文件结构要根据 jflex 要求更改. 最后生成一个 java 文件, 测试执行通过.
要点: %int 设置返回值类型; 抄一个 main() 函数.
====================================================
例子2: 计算器
/* first part */
package calc;
%%
/* second part */
%{
public static void print(String s) { System.out.print(s); }
%}
%int
%debug
%%
/* third part */
"+" { print("PLUS\n"); }
"-" { print("MINUS\n"); }
"*" { print("TIMES\n"); }
"/" { print("DIVIDE\n"); }
[0-9]+ { print("NUMBER: " +yytext() +"\n"); }
\n { print("NEWLINE"); }
[ \t] { /*space ignored*/ }
. { print("Mystery Char: " + yytext() + "\n"); }
与 wc 相似, 模式略微增多一些.
方法论:
1. 在现有例子中 逐步地 修改,添加, 试验.
2. 找别人工程中已经使用了的 lex/flex 文件改造.
花絮(八卦): lex 是 1975 年 Mike Lesk 和暑期实习生 Eric Schmidt 编写的, 后者现在是
Google 的 CEO.
=======================================================
一个 BYacc/J 的例子: calc.y
%token NUMBER /* 定义 token */
%type <dval> exp /* 可用于指定 yylval 值类型 */
%% /* begin second part: rules */
calclist: /*empty*/ | calclist exp EOL ...
exp: exp '+' factor | exp '-' factor ...
%% /* third part: user code */
public static void main() { ... }
public int yylex() { ... } /* 连接 jflex 在这里实现 */
生成文件使用: byaccj -J -Jpackage=calc -Jsemantic=double calc.y
其中 byaccj 指向 yacc.exe (BYacc/J 的可执行文件)
-Jpackage=calc 指定包名字.
-Jsemantic=double 表示语义值类型取为 double (计算器例子中为省事)
在 Yylex 中, 需要引用 Parser 生成的 NUMBER 等 token 值. 为给 parser 中设置 yylval 值,
需要在 Yylex 中有 Parser 的引用, 在构造中给出是易用的方式.
======================================================
Ant 文件中自动完成 cup, jflex 编译的片段, 供参考用:
<property name="CUPJar" value="lib/java-cup-11a.jar" ></property> <property name="JFlexJar" value="lib/JFlex.jar"></property> <!-- 定义新任务 cup,jflex --> <taskdef name="cup" classname="java_cup.anttask.CUPTask" classpath="${CUPJar}" /> <taskdef name="jflex" classname="JFlex.anttask.JFlexTask" classpath="${JFlexJar}" /> <!-- 使用 cup 编译 'ycalc.cup' --> <target name="ycalc_cup" > <cup srcfile="src/cup/ycalc.cup" destdir="src/cup" interface="true" package="cup" parser="Parser" symbols="sym" /> </target> <!-- 使用 jflex 编译 'lcalc.flex' --> <target name="lcalc_flex"> <jflex file="src/cup/lcalc.flex" dot="true" /> </target>