学习 JFlex 和 BYacc/J 简要笔记

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>

 

你可能感兴趣的:(学习 JFlex 和 BYacc/J 简要笔记)