-
2021-03-09 23:10:45
前言
本系列的文章的宗旨是让大家能够写出自己的编译器,解释器或者脚本引擎,所以每到理论介绍到一个程度后,我都会来讨论实践问题.理论方面,编译原理的教材已经是够多了,而实践的问题却很少讨论.
前几节文章只讨论到了词法分析和LL文法分析,关键的LR文法分析这里却还没有讲,我们先不要管复杂的LR文法和算法,让我们使用LL算法来实际做一些东西后再说.本文将介绍一个在JAVA上广泛使用的LL算法分析工具Javacc.(这是我唯一能找到的使用LL算法的语法分析器构造工具).这一节的文章并非只针对JAVA开发者,如果你是C/C++开发者,那么也请你来看看这个JAVA下的优秀工具,或许你将来也用得着它.
Lex和yacc这两个工具是经典的词法分析和语法分析工具,但是它们都是基于C语言下面的工具,而使用JAVA的朋友们就用不上了.但是JAVA下已经有了lex和yacc的替代品javacc(
Java Compiler Compiler )
.同时javacc也是使用LL算法的工具,我们也可以实践一下前面学的LL算法.
首先声明我不是一个JAVA专家,我也是刚刚才接触JAVA.Java里面或许有很多类似javacc一样的工具,但是据我所知,javacc还是最广泛,最标准的JAVA下的词法语法分析器.
Javacc的获取
同lex和yacc一样,javacc也是一个免费可以获取的通用工具,它可以在很多JAVA相关的工具下载网站下载,当然,javacc所占的磁盘空间比起lex和yacc更大一些,里面有标准的文档和examples.相对lex和yacc来说,javacc做得更人性化,更容易一些.如果你实在找不到javacc,还是可以联系我,我这里有.现在最新的就是javacc 3.2版本.
Javacc的原理
Javacc可以同时完成对text的词法分析和语法分析的工作,使用起来相当方便.同样,它和lex和yacc一样,先输入一个按照它规定的格式的文件,然后javacc根据你输入的文件来生成相应的词法分析于语法分析程序.同时,新版本的Javacc除了常规的词法分析和语法分析以外,还提供JJTree等工具来帮助我们建立语法树.总之,Javacc在很多地方做得都比lex和yacc要人性化,这个在后面的输入文件格式中也能体现出来.
Javacc的输入文件
Javacc的输入文件格式做得比较简单.每个非终结符产生式对应一个Class中的函数,函数中可以嵌入相应的识别出该终结符文法时候的处理代码(也叫动作).这个与YACC中是一致的.
Javacc的输入文件中,有一系列的系统参数,比如其中lookahead可以设置成大于1的整数,那么就是说,它可以为我们生成LL(k)算法(k>=1),而不是简单的递归下降那样的LL(1)算法了.要知道,LL(2)文法比起前面讨论的LL(1)文法判断每个非终结符时候需要看前面两个记号而不是一个,那么对于文法形式的限制就更少.不过LL(2)的算法当然也比LL(1)算法慢了不少.作为一般的计算机程序设计语言,LL(1)算法已经是足够了.就算不是LL(1)算法,我们也可以通过前面讲的左提公因式把它变成一个LL(1)文法来处理.不过既然javacc都把lookahead选择做出来了,那么在某些特定的情况下,我们可以直接调整一个lookahead的参数就可以,而不必纠正我们的文法.
下面我们来看看Javacc中自带的example中的例子.
例5.1
这个例子可以在javacc-3.2/doc/examples/SimpleExamples/Simple1.jj看到
PARSER_BEGIN(Simple1)
public class Simple1 {
public static void main(String args[]) throws ParseException {
Simple1 parser = new Simple1(System.in);
parser.Input();
}
}
PARSER_END(Simple1)
void Input() :
{}
{
MatchedBraces() ("\n"|"\r")*
}
void MatchedBraces() :
{}
{
"{" [ MatchedBraces() ] "}"
}
设置好javacc的bin目录后,在命令提示符下输入
javacc Simple1.jj
然后
javacc
就会为你生成下面几个
java
源代码文件
Simple1.java
Simple1TokenManager.java
Simple1Constants.java
SimpleCharStream.java
Token.java
TokenMgrError.java
其中Simple1就是你的语法分析器的对象,它的构造函数参数就是要分析的输入流,这里的是System.in.
class Simple1就定义在标记
PARSER_BEGIN(Simple1)
PARSER_END(Simple1)之间.
但是必须清楚的是,PARSER_BEGIN和PARSER_END中的名字必须是词法分析器的名字(这里是Simple1).
PARSER_END下面的定义就是文法非终结符号的定义了.
Simple1的文法基本就是:
Input ->
MatchedBraces ("\n"|"\r")*
MatchedBraces ->
“
{
“
MatchedBraces
“
}
”
从它的定义我们可以看到
,
每个非终结符号对于一个过程
.
比如
Input
的过程
void Input() :
{}
{
MatchedBraces() ("\n"|"\r")*
}
在定义
void Input
后面记住需要加上一个冒号
”:”,
然后接下来是两个块
{}
的定义
.
第一个
{}
中的代码是定义数据
,
初试化数据的代码
.
第二个
{}
中的部分就是真正定义
Input
的产生式了
.
每个产生式之间用
”|”
符号连接
.
注意
:
这里的产生式并非需要严格
BNF
范式文法
,
它的文法既可以是
BNF,
同时还可以是混合了正则表达式中的定义方法
.
比如上面的
Input ->
MatchedBraces ("\n"|"\r")*
中
(“\n”|”\r”)*
就是个正则表达式
,
表示的是
\n
或者
\r
的
0
个到无限个的重复的记号
.
而
是
javacc
系统定义的记号
(TOKEN),
表示文件结束符号
.
除了
,
无论是系统定义的
TOKEN,
还是自定义的
TOKEN,
里面的
TOKEN
都是以
的方式表示
.
每个非终结符号
(Input
和
MatchedBraces)
都会在
javacc
生成的
Simple1.java
中形成
Class Simple1
的成员函数
.
当你在外部调用
Simple1
的
Input,
那么语法分析器就会开始进行语法分析了
.
例
5.2
在
javacc
提供的
example
里面没有
.javacc
提供的
example
里面提供的例子中
SimpleExamples
过于简单
,
而其它例子又过于庞大
.
下面我以我们最常见的数学四则混合运算的文法来构造一个
javacc
的文法识别器
.
这个例子是我自己写的
,
十分简单
,.
其中还包括了文法识别同时嵌入的构建语法树
Parse-Tree
的代码
.
不过由于篇幅的原因
,
我并没有给出全部的代码
,
这里只给了
javacc
输入部分相关的代码
.
而
Parse-tree
就是一个普通的
4
叉树
,3
个
child,1
个
next(
平行结点
),
相信大家在学习数据结构的时候应该都是学过的
.
所以这里就省略过去了
.
在大家看这些输入代码之前
,
我先给出它所使用的文法定义
,
好让大家有个清楚的框架
.
Expression
-> Term{ Addop Term }
Addop -> "+" | "-"
Term->Factor { Mulop Factor }
Mulop -> "*" | "/"
Factor -> ID | NUM | "("Expression")"
这里的文法可能和BNF范式有点不同.{}的意思就是0次到无限次重复,它跟我们在学习正则表达式的时候的”*”符号相同,所以,在Javacc中的文法表示的时候,{…}部分的就是用(…)*来表示.
为了让词法分析做得更简单
,
我们通常都不会在文法分析的时候
,
使用
”(”,”)“
等字符号串来表示终结符号
,
而需要转而使用
LPAREN
,
RPAREN
这样的整型符号来表示
.
PARSER_BEGIN(Grammar)
public class Grammar implements NodeType {
public ParseTreeNode GetParseTree(InputStream in) throws ParseException
{
Grammar parser =new Grammar(in);
return parser.Expression();
}
}
PARSER_END(Grammar)
SKIP :
{
" " | "\t" | "\n" | "\r"
}
TOKEN :
{
< ID: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","0"-"9"] )* >
|< NUM: ( ["0"-"9"] )+ >
| < PLUS:"+" >
| < MINUS:"-" >
| < TIMERS: "*" >
| < OVER:"/" >
| < LPAREN: "(" >
| < RPAREN: ")" >
}
ParseTreeNode Expression() :
{
ParseTreeNode ParseTree = null;
ParseTreeNode node;
}
{
( node=Simple_Expression()
{
if(ParseTree == null)
ParseTree =node;
else
{
ParseTreeNode t;
t= ParseTree;
while(t.next != null)
t=t.next;
t.next = node;
}
}
)*
{ return ParseTree;}
}
ParseTreeNode Simple_Expression() :
{
ParseTreeNode node;
ParseTreeNode t;
int op;
}
{
node=Term(){}
(
op=addop() t=Term()
{
ParseTreeNode newNode = new ParseTreeNode();
newNode.nodetype = op;
newNode.child[0] = node;
newNode.child[1] = t;
switch(op)
更多相关内容 -
PRECISE:PRECISE NLI的Java实现
2021-05-20 08:25:08PRECISE自然语言的Java实现与SQL数据库的接口。 v0.1 安装: 下载在lib /目录中 下载 运行测试GUI: TestGUI path_to_wordnet_database path_to_lexicon 可以在lex /中找到的词典 改善 单词索引 基于: Popescu... -
lex宿主语言可以用java吗
2021-03-01 11:05:26它的基本原理就是使用正则表达式扫描匹配文本,并为每一个匹配模式定义一些操作,当用C语言作宿主语言时,这些操作都由C语言实现。一种匹配的正则表达式可能会包含相关的动作。这一动作可能还包括返回一个标记。当 ...展开全部
Lex是美国Bell实验室用C语言研制的一个词法分析程序自生成工具62616964757a686964616fe78988e69d8331333361303131
。它的基本原理就是使用正则表达式扫描匹配文本,并为每一个匹配模式定义一些操作,当用C语言作宿主语言时,这些操作都由C语言实现。
一种匹配的正则表达式可能会包含相关的动作。这一动作可能还包括返回一个标记。当 Lex 接收到文件或文本形式的输入时,它试图将文本与正则表达式进行匹配。它一次读入一个输入字符,直到找到一个匹配的模式。如果能够找到一个匹配的模式,Lex 就执行相关的动作(可能包括返回一个标记)。另一方面,如果没有可以匹配的正则表达式,将会停止进一步的处理,Lex 将显示一个错误消息。
Lex 和 C 是强耦合的。一个 .l 文件(Lex 文件具有 .l 的扩展名)通过 lex 公用程序来传递,并生成 C 的输出文件。这些文件被编译为词法分析器的可执行版本。
本程序对java源程序进行分析,主要实现以下两个功能:
(1)、清除注释。java源程序有三种注释方法:1、单行注释,以//开头直到行结束;2、多行注释,以/*为开始,*/为结束,可以注释多行;3、java文档注释,这也是一种多行注释,但它可以通过java文档生成工具写入java程序文档中。它以/**为开始,*/为结束。
(2)、通过程序行数计算工作量。
(3)、计算程序中类的个数,并判断有没有两个public类,如果存在则报错:There is an error:One java file cannot includes two public class。
单行注释的清除。由于单行注释以//开头直到行结束,首先要匹配的就是//,然后清除从匹配处到行结束的所有字符。具体实现如下:
"//" {
int c;
while ( (c = input()) != '/n' &&
c != EOF )
{
;
}
code = add(code,'/n');
}
多行注释的清除。多行注释有两种,一种是普通多行注释,另一种是java文档注释。这两种注释都以*/结束,普通多行注释以/*开始,java文档注释以/**开始。可以先匹配/*,然后向后搜索*/。要区别这两种注释就要看/*后面是否紧跟一个*字符,如果不是则为普通多行注释;如果是还要看下一字符是否为/字符,如果是也为普通注释,如果不是则为java文档注释。具体lex程序实现如下:
"/*" {
int c,ct=0;
char * javadoc = "/*there is a Java Doc Comment*/";
for ( ; ; )
{
while ( (c = input()) != '*' &&
c != EOF )
{
ct++;
}
if ( c == '*' )
{
c = input();
if ( c == '/' )
{
ct = 0;
break; /* found the end */
}
else
{
if(ct==0)
code = strcat(code,javadoc);
}
}
if ( c == EOF )
{
printf( "EOF in comment" );
break;
}
}
}
my.l即为lex程序。输入一段带有注释的java源程序,然后打入结束标志$号,回车就可以看到在输出的程序中所有注释都已经删除,在含有java文档注释的地方加上了一句注释:/*there is a Java Doc Comment*/。
经过仔细研究发现,上面的实现过程还是过分依赖C语言,没有真正发挥Lex模式匹配的强大功能。单行注释、普通多行注释、Java文档注释可分别由下列模式匹配:
.*/n
///*[^/*//]*/*//
///*/*[^/*//]*/*//
本程序还提供了识别类定义的功能,匹配模式如下:
public[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/]}*/}
(public|protected|private)[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/]}*/}
[ /n/t]*class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/]}*/}
经完善后的lex程序如下所示:
%{
#include
char * code = "";
int codelines = 0;
int classnum = 0;
int pubclass = 0;
char * classes[4]={"","","",""};
/*add a char c to the string code*/
char * add(char * code,char c)
{
char * temp;
if(code==NULL)
return "";
temp = (char*)malloc(sizeof(char)*2);
temp[0] = c;
temp[1] = '/0';
temp = strcat(code,temp);
return temp;
}
%}
%%
///*[^/*//]*/*// code = add(code,'');
///*/*[^/*//]*/*// code = strcat(code,"/*there is a Java Doc Comment*//n");
.*/n code = add(code,'/n');
public[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/}]*/} {
classes[classnum] = (char*)malloc(100);
classes[classnum] = strcpy(classes[classnum],yytext);
classnum++;
code = strcat(code,yytext);
pubclass++;
}
(public|protected|private)[ /n/t]+class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/}]*/} {
classes[classnum] = (char*)malloc(100);
classes[classnum] = strcpy(classes[classnum],yytext);
classnum++;
code = strcat(code,yytext);
}
[ /n/t]*class[ /n/t]+[a-zA-Z][_a-zA-z0-9]*/[ /n/t]*/{[^/}]*/} {
classes[classnum] = (char*)malloc(100);
classes[classnum] = strcpy(classes[classnum],yytext);
classnum++;
code = strcat(code,yytext);
}
/n code = add(code,'/n');
. {
if(yytext[0] == ';')
codelines++;
code = add(code,yytext[0]);
}
%%
yywrap()
{
int i=0;
printf("/nBelow is the code without comment:/n/n");
printf(code);
printf("/n/nConclude:/nThis code weights %d lines/n",codelines);
printf("This code includes %d classes/n",classnum);
printf("classes:/n");
for(i=0;i
{
printf(classes[i]);
printf("/n");
}
if(pubclass>1)
printf("/nThere is an error: a java file cannot have two public class/n");
code = (char*)malloc(1);
code[0]='/0';
}
main()
{
yylex();
system("pause");
return 1;
}
这个程序还有另外两个其他功能:1、根据程序的行数来确定程序工作量,行数等于分号的个数。在结果的末尾将会显示行数;2、我们知道一个java文件中不能存在两个public类,本程序可以检查一个文件中存在几个类,并判断是否存在两个或两个以上的public类,如果存在就报错。
注:my.l文件是改进前的lex程序,改进后的程序保存在my1.l文件中,Java.txt内含有一个java源程序可以用来测试。运行lexyy.exe,复制java.txt里面的内容粘贴到程序里,加上输入结束符Ctrl+z,然后回车即可看到结果;或者在dos下把java.txt作为lexyy.exe的参数运行lexyy.exe也可。
已赞过
已踩过<
你对这个回答的评价是?
评论
收起
-
lex_实验-编译原理词法分析器实现
2018-05-02 23:51:16这是一个编译原理lex工具,具备词法分析器的功能,方便了解编译原理词法分析器的功能 -
Lexer-Parser:编译原理,java实现的词法分析器&语法分析器,有运行窗口,并附带使用说明
2021-03-20 23:00:11用java实现的词法分析和语法分析的小程序 :carp_streamer: 个人博客网站: : :heart_with_ribbon: :television:结果图 词法分析 语法分析 判断是否是LL(1)文法 概述 词法分析:根据输入的字符序列,将字符序列... -
编译原理-词法分析器1(lex实现)-附件资源
2021-03-02 15:08:24编译原理-词法分析器1(lex实现)-附件资源 -
编译原理-词法分析器1(lex实现)
2018-11-05 16:10:26编译原理课实验一是词法分析器,但是在网上查了很多资料,发现用lex实现还要用Linux,Windows可以用对应的flex实现,但是网上的资料很零散,所以整理了一下从安装到配置,到实现一个词法分析器的过程 一、 安装 ...编译原理课实验一是词法分析器,但是在网上查了很多资料,发现用lex实现还要用Linux,Windows可以用对应的flex实现,但是网上的资料很零散,所以整理了一下从安装到配置,到实现一个词法分析器的过程
一、 安装
- 下载flex和bison
UnxUtils: http://pan.baidu.com/s/1o6NY1E6
Updates:http://pan.baidu.com/s/1o6NY1E6
保存在E:\lex\cywin - 使用的flex和bison都是GNU的工具,GCC既采用C/C++的编译器也采用GNU的编译器,Windows平台的GCC主要是MinGW编译器,下载地址:
https://sourceforge.net/projects/mingw/files/Installer/mingw-get/catalogue/msys-package-list.xml.lzma/download
或者
http://www.mingw-w64.org/doku.php
保存在E:\lex\GnuWin32 - 下载Parser Generator
http://www.bumblebeesoftware.com/downloads.htm
二、 配置环境变量
下载UnxUtils以及UnxUpdates之后,解压到自己的文件夹,把/usr/local/wbin文件夹的绝对地址加到
我的电脑(右键)->属性->高级->环境变量->系统变量->path值
三、 安装MinGW - 安装过程简图
点击mingw-get-setup.exe
- 打开安装好的软件,选Basic Setup,在右侧选择mingw32-gcc-g++,鼠标右键点击Mark for Installation,然后点击左上角的Installation按钮,选择Apply Changes,弹出对话框点击Apply,安装对应的编辑器
- 设置环境变量
进入MinGW的安装路径E:\lex\GnuWin32,将bin的绝对路径添加到path中(E:\lex\GnuWin32\bin)
我的电脑(右键)->属性->高级->环境变量->系统变量->path值 - 检查安装完成
打开cmd,输入gcc -v,显示如下即为安装成功
四、 Parser Generator的配置 - 打开Parser Generator
- 菜单project->LibBuilder选第一个
3. 配置好后点击built
4. 建立一个project
<1>project->ParserWizard
<2>工程设定(语言可选C/C++/JAVA)
<3>工程设定(是否带main函数的YACC文件或LEX文件)
<4>YACC文件设定
<5>LEX文件设定
<6>编辑好代码后project->Rebuild All
上图:
五、 配置VC6.0
- 打开cmd,一直到1.l存在的文件夹的目录下
- Flex 1.l,此时再打开1.l的文件夹会发现出现lex.yy.c文件
- 那么现在开始配置VC,首先导入Parser Generator的库文件和源文件
Tools->Options,设置Iuclude files,library files,Source files
4. project->Settings
Win32 Debug
C/C++ -> preprocessor definitions添加宏定义,YYDEBUG
Link -> Object/Library Modules 加yld,lib
Win32 Release
Link -> Object/Library Modules 加yld,lib
- 将lex.yy.c添加到Source Files
将1.h添加到Header Files
- 编译运行就OK啦
- 或者在DOS下运行
- 下载flex和bison
-
利用Java实现简单的词法分析器实例代码
2021-02-12 14:05:55首先看下我们要分析的代码段如下:输出结果如下:输出结果(a).PNG输出结果(b).PNG输出结果(c).PNG括号里是一个二元...import java.io.*;/** 主程序*/public class Main {public static void main(String[] args) th...首先看下我们要分析的代码段如下:
输出结果如下:
输出结果(a).PNG
输出结果(b).PNG
输出结果(c).PNG
括号里是一个二元式:(单词类别编码,单词位置编号)
代码如下:
package Yue.LexicalAnalyzer;
import java.io.*;
/*
* 主程序
*/
public class Main {
public static void main(String[] args) throws IOException {
Lexer lexer = new Lexer();
lexer.printToken();
lexer.printSymbolsTable();
}
}
package Yue.LexicalAnalyzer;
import java.io.*;
import java.util.*;
/*
* 词法分析并输出
*/
public class Lexer {
/*记录行号*/
public static int line = 1;
/*存放最新读入的字符*/
char character = ' ';
/*保留字*/
Hashtable keywords = new Hashtable();
/*token序列*/
private ArrayList tokens = new ArrayList();
/*符号表*/
private ArrayList symtable = new ArrayList();
/*读取文件变量*/
BufferedReader reader = null;
/*保存当前是否读取到了文件的结尾*/
private Boolean isEnd = false;
/* 是否读取到文件的结尾 */
public Boolean getReaderState() {
return this.isEnd;
}
/*打印tokens序列*/
public void printToken() throws IOException {
FileWriter writer = new FileWriter("E:\\lex.txt");
System.out.println("词法分析结果如下:");
System.out.print("杜悦-2015220201031\r\n\n");
writer.write("杜悦-2015220201031\r\n\r\n");
while (getReaderState() == false) {
Token tok = scan();
String str = "line " + tok.line + "\t(" + tok.tag + "," + tok.pos + ")\t\t"
+ tok.name + ": " + tok.toString() + "\r\n";
writer.write(str);
System.out.print(str);
}
writer.flush();
}
/*打印符号表*/
public void printSymbolsTable() throws IOException {
FileWriter writer = new FileWriter("E:\\symtab1.txt");
System.out.print("\r\n\r\n符号表\r\n");
System.out.print("编号\t行号\t名称\r\n");
writer.write("符号表\r\n");
writer.write("编号 " + "\t行号 " + "\t名称 \r\n");
Iterator e = symtable.iterator();
while (e.hasNext()) {
Symbol symbol = e.next();
String desc = symbol.pos + "\t" + symbol.line + "\t" + symbol.toString();
System.out.print(desc + "\r\n");
writer.write(desc + "\r\n");
}
writer.flush();
}
/*打印错误*/
public void printError(Token tok) throws IOException{
FileWriter writer = new FileWriter("E:\\error.txt");
System.out.print("\r\n\r\n错误词法如下:\r\n");
writer.write("错误词法如下:\r\n");
String str = "line " + tok.line + "\t(" + tok.tag + "," + tok.pos + ")\t\t"
+ tok.name + ": " + tok.toString() + "\r\n";
writer.write(str);
}
/*添加保留字*/
void reserve(KeyWord w) {
keywords.put(w.lexme, w);
}
public Lexer() {
/*初始化读取文件变量*/
try {
reader = new BufferedReader(new FileReader("E:\\输入.txt"));
} catch (IOException e) {
System.out.print(e);
}
/*添加保留字*/
this.reserve(KeyWord.begin);
this.reserve(KeyWord.end);
this.reserve(KeyWord.integer);
this.reserve(KeyWord.function);
this.reserve(KeyWord.read);
this.reserve(KeyWord.write);
this.reserve(KeyWord.aIf);
this.reserve(KeyWord.aThen);
this.reserve(KeyWord.aElse);
}
/*按字符读*/
public void readch() throws IOException {
character = (char) reader.read();
if ((int) character == 0xffff) {
this.isEnd = true;
}
}
/*判断是否匹配*/
public Boolean readch(char ch) throws IOException {
readch();
if (this.character != ch) {
return false;
}
this.character = ' ';
return true;
}
/*数字的识别*/
public Boolean isDigit() throws IOException {
if (Character.isDigit(character)) {
int value = 0;
while (Character.isDigit(character)) {
value = 10 * value + Character.digit(character, 10);
readch();
}
Num n = new Num(value);
n.line = line;
tokens.add(n);
return true;
} else
return false;
}
/*保留字、标识符的识别*/
public Boolean isLetter() throws IOException {
if (Character.isLetter(character)) {
StringBuffer sb = new StringBuffer();
/*首先得到整个的一个分割*/
while (Character.isLetterOrDigit(character)) {
sb.append(character);
readch();
}
/*判断是保留字还是标识符*/
String s = sb.toString();
KeyWord w = keywords.get(s);
/*如果是保留字的话,w不应该是空的*/
if (w != null) {
w.line = line;
tokens.add(w);
} else {
/*否则就是标识符,此处多出记录标识符编号的语句*/
Symbol sy = new Symbol(s);
Symbol mark = sy; //用于标记已存在标识符
Boolean isRepeat = false;
sy.line = line;
for (Symbol i : symtable) {
if (sy.toString().equals(i.toString())) {
mark = i;
isRepeat = true;
}
}
if (!isRepeat) {
sy.pos = symtable.size() + 1;
symtable.add(sy);
} else if (isRepeat) {
sy.pos = mark.pos;
}
tokens.add(sy);
}
return true;
} else
return false;
}
/*符号的识别*/
public Boolean isSign() throws IOException {
switch (character) {
case '#':
readch();
AllEnd.allEnd.line = line;
tokens.add(AllEnd.allEnd);
return true;
case '\r':
if (readch('\n')) {
readch();
LineEnd.lineEnd.line = line;
tokens.add(LineEnd.lineEnd);
line++;
return true;
}
case '(':
readch();
Delimiter.lpar.line = line;
tokens.add(Delimiter.lpar);
return true;
case ')':
readch();
Delimiter.rpar.line = line;
tokens.add(Delimiter.rpar);
return true;
case ';':
readch();
Delimiter.sem.line = line;
tokens.add(Delimiter.sem);
return true;
case '+':
readch();
CalcWord.add.line = line;
tokens.add(CalcWord.add);
return true;
case '-':
readch();
CalcWord.sub.line = line;
tokens.add(CalcWord.sub);
return true;
case '*':
readch();
CalcWord.mul.line = line;
tokens.add(CalcWord.mul);
return true;
case '/':
readch();
CalcWord.div.line = line;
tokens.add(CalcWord.div);
return true;
case ':':
if (readch('=')) {
readch();
CalcWord.assign.line = line;
tokens.add(CalcWord.assign);
return true;
}
break;
case '>':
if (readch('=')) {
readch();
CalcWord.ge.line = line;
tokens.add(CalcWord.ge);
return true;
}
break;
case '
if (readch('=')) {
readch();
CalcWord.le.line = line;
tokens.add(CalcWord.le);
return true;
}
break;
case '!':
if (readch('=')) {
readch();
CalcWord.ne.line = line;
tokens.add(CalcWord.ne);
return true;
}
break;
}
return false;
}
/*下面开始分割关键字,标识符等信息*/
public Token scan() throws IOException {
Token tok;
while (character == ' ')
readch();
if (isDigit() || isSign() || isLetter()) {
tok = tokens.get(tokens.size() - 1);
} else {
tok = new Token(character);
printError(tok);
}
return tok;
}
}
package Yue.LexicalAnalyzer;
/*
* Token父类
*/
public class Token {
public final int tag;
public int line = 1;
public String name = "";
public int pos = 0;
public Token(int t) {
this.tag = t;
}
public String toString() {
return "" + (char) tag;
}
}
package Yue.LexicalAnalyzer;
/*
* 单词类别赋值
*/
public class Tag {
public final static int
BEGIN = 1, //保留字
END = 2, //保留字
INTEGER = 3, //保留字
FUNCTION = 4, //保留字
READ = 5, //保留字
WRITE = 6, //保留字
IF = 7, //保留字
THEN = 8, //保留字
ELSE = 9, //保留字
SYMBOL = 11, //标识符
CONSTANT = 12, //常数
ADD = 13, //运算符 "+"
SUB = 14, //运算符 "-"
MUL = 15, //运算符 "*"
DIV = 16, //运算符 "/"
LE = 18, //运算符 "<="
GE = 19, //运算符 ">="
NE = 20, //运算符 "!="
ASSIGN = 23, //运算符 ":="
LPAR = 24, //界符 "("
RPAR = 25, //界符 ")"
SEM = 26, //界符 ";"
LINE_END = 27, //行尾符
ALL_END = 28; //结尾符 "#"
}
package Yue.LexicalAnalyzer;
/**
* 保留字
*/
public class KeyWord extends Token {
public String lexme = "";
public KeyWord(String s, int t) {
super(t);
this.lexme = s;
this.name = "保留字";
}
public String toString() {
return this.lexme;
}
public static final KeyWord
begin = new KeyWord("begin", Tag.BEGIN),
end = new KeyWord("end", Tag.END),
integer = new KeyWord("integer", Tag.INTEGER),
function = new KeyWord("function", Tag.FUNCTION),
read = new KeyWord("read", Tag.READ),
write = new KeyWord("write", Tag.WRITE),
aIf = new KeyWord("if", Tag.IF),
aThen = new KeyWord("then", Tag.THEN),
aElse = new KeyWord("else", Tag.ELSE);
}
package Yue.LexicalAnalyzer;
/*
* 标识符
*/
public class Symbol extends Token {
public String lexme = "";
public Symbol(String s) {
super(Tag.SYMBOL);
this.lexme = s;
this.name = "标识符";
}
public String toString() {
return this.lexme;
}
}
package Yue.LexicalAnalyzer;
/**
* 运算符
*/
public class CalcWord extends Token {
public String lexme = "";
public CalcWord(String s, int t) {
super(t);
this.lexme = s;
this.name = "运算符";
}
public String toString() {
return this.lexme;
}
public static final CalcWord
add = new CalcWord("+", Tag.ADD),
sub = new CalcWord("-", Tag.SUB),
mul = new CalcWord("*", Tag.MUL),
div = new CalcWord("/", Tag.DIV),
le = new CalcWord("<=", Tag.LE),
ge = new CalcWord(">=", Tag.GE),
ne = new CalcWord("!=", Tag.NE),
assign = new CalcWord(":=", Tag.ASSIGN);
}
package Yue.LexicalAnalyzer;
/**
* 界符
*/
public class Delimiter extends Token {
public String lexme = "";
public Delimiter(String s, int t) {
super(t);
this.lexme = s;
this.name = "界符";
}
public String toString() {
return this.lexme;
}
public static final Delimiter
lpar = new Delimiter("(", Tag.LPAR),
rpar = new Delimiter(")", Tag.RPAR),
sem = new Delimiter(";", Tag.SEM);
}
package Yue.LexicalAnalyzer;
/*
* 常数
*/
public class Num extends Token {
public final int value;
public Num(int v) {
super(Tag.CONSTANT);
this.value = v;
this.name = "常数";
}
public String toString() {
return "" + value;
}
}
package Yue.LexicalAnalyzer;
/**
* 行尾符
*/
public class LineEnd extends Token {
public String lexme = "";
public LineEnd(String s) {
super(Tag.LINE_END);
this.lexme = s;
this.name = "行尾符";
}
public String toString() {
return this.lexme;
}
public static final LineEnd lineEnd = new LineEnd("\r\n");
}
package Yue.LexicalAnalyzer;
/**
* 结尾符
*/
public class AllEnd extends Token {
public String lexme = "";
public AllEnd(String s) {
super(Tag.ALL_END);
this.lexme = s;
this.name = "结尾符";
}
public String toString() {
return this.lexme;
}
public static final AllEnd allEnd = new AllEnd("#");
}
总结
以上就睡这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
您可能感兴趣的文章:
-
使用flexmark在Java中将Markdown格式文本转换成HTML格式文本
2020-12-21 12:56:27最近做的项目有将Markdown语法转换成HTML语法的这么一个...然鹅 自己写个Utils太麻烦了 为此找到了个很实用的转换工具包:flexmark 第一步:引入flexmark的依赖: ... flexmark-all 0.50.42 第二步:直接使用即可: ... -
如何读取照片的GPS信息?—最好的语言Java实现起来就这么简单【手把手教程+完整代码】
2021-07-04 20:40:23通过一张照片来进行GPS定位,java实现起来就这么简单 -
用Lex编写的简易版C语言词法分析器(编译原理大作业1)
2019-11-16 20:09:43主要针对的是编译原理程序设计这门课的大作业所做的小项目,里边有完整的代码,以及使用说明,包括bnf范式。 -
Java实现Json解析器
2019-02-17 12:59:09在开始正文之前,先啰嗦几...虽然他是用C语言写的,哈哈~就这样我跟着大牛的文档和代码写了一边C语言的JSON后,对JSON的语法以及实现有了一个初级的了解,然后,我就开始着手写Java版的Json,emmm…在写之前,为了看... -
jlex:一个用Python编写的Java源代码的词法分析器-java source code
2021-03-25 07:17:59一个用Python编写的Java源代码的词法分析器 例子 代码 from jlex . lexer import lex_source_file from jlex . type import Type tokens = lex_source_file ( 'Example.java' ) for token in tokens : if ... -
中文分词之Java实现使用IK Analyzer实现
2017-11-03 19:25:19IK Analyzer是基于lucene实现的分词开源框架,下载路径:http://code.google.com/p/ik-analyzer/downloads/list 需要在项目中引入: IKAnalyzer.cfg.xml IKAnalyzer2012.jar lucene-core-3.6.0.jar ... -
compiler Java实现的编译器前端(一直到中间代码生成)其中使用了JFLEX及CUP做Lex program /解释器 238万源...
2021-03-09 04:11:33文件名称: compiler下载 收藏√ [5 4 3 2 1]开发工具: Java文件大小: 596 KB上传时间: 2013-05-04下载次数: 0详细说明:Java实现的编译器前端(一直到中间代码生成)其中使用了JFLEX及CUP做Lex-Compiler by Java文件... -
Java这些东西
2021-03-09 23:10:15FOP, Kindle, Velocity, Clojure, JNDI, Clover,Hadoop, JSF, Jackrabbit, Livescribe pen, Commons, Hibernate, EJB, Tobago, IntelliJ, Jersey,Scalaz, HornetQ, JAX-RS, Lift, Derby, JUnit, Freemarker, JavaME,... -
lex-main.lex
2013-09-15 19:06:40分词算法源文件(lex-main,java中的用于写分词算法的词库) -
lexer and parser 的java简单实现
2019-03-11 19:45:14java实现一个词法分析器,参考link可以识别加法与乘法中的token。 token 种别码 EOI 0 SEMI(;) 1 PLUS(+) 2 TIMES(*) 3 LP( ( ) 4 RP( ) ) 5 NUM 6 ... -
如何做好Flex与Java交互
2021-01-20 03:35:51三种flex4与Java顺利通信的方式是: ... *第一种 功能描述:该类用来实现flex与普通java类中的方法通信 * @author Administrator */ //以上是打头的功能描述,可以不写。 <!–flex 与普通java类通信– -
编译原理动手实操,用java实现一个简易编译器1-词法解析入门
2016-02-17 09:53:41本文本着动手实操(念第一声)的原则,用java实现一个简单的编译器,让读者朋友能一感编译原理的实质,我秉持一个原则,没有代码可实践的计算机理论,都是耍流氓。 编译器作用就是将一种计算机无法理解的文本,转译成... -
使用IK Analyzer实现中文分词(JAVA)
2021-03-06 13:14:28IK Analyzer是基于lucene实现的分词开源框架;需要在项目中引入:IKAnalyzer.cfg.xml 、IKAnalyzer2012.jar 、lucene-core-3.6.0.jar 、stopword.dicIK Analyzer示例代码如下:package com.haha.test;import java.io... -
从lex&yacc说到编译器(4.文法识别(一))
2021-03-12 21:13:48本来我的计划是简单把lex和yacc介绍完后就直接进入编译器的构造的技术细节问题讨论,但是最近看了一些国外经典教材后,发现文法的识别问题在编译原理和技术中是个绝不能忽视的问题.即使现在有了yacc工具来帮助我来识别... -
atitit.java解析sql语言解析器解释器的实现
2021-02-12 09:55:59atitit.java解析sql语言解析器解释器的实现1.解析sql的本质:实现一个4gldsl编程语言的编译器Sql走十一个4gldsl,..SQL解析器基本上走十一个编译器实现2.解析sql的基本的流程,词法分析,而后进行语法分析,语义分析,... -
Java应用中表达式解析器(Java Cup/JFlex)生成器的介绍及示例
2021-02-27 18:57:42在基于Java的软件系统的构建过程中,开发人员经常会遇到词法解析、语法解析等问题,比如:在报表系统的中一般需要支持单元格计算公式(类似于Excel的公式),在某些系统的数据转换过程要实现自定义的转换规则脚本。... -
初级java笔试题-nand2tetris:创建一个通用计算机系统,从与非门一直到俄罗斯方块的实现
2021-06-03 01:34:28初级java笔试题nand2tetris 从第一性原理构建计算机; 从与非门开始,一直到构建可以玩俄罗斯方块的通用计算机。 硬件 该部分项目已完成。...Lex 和 Yacc 这样的工具来简化这个过程。 操作系统 我计划为通用计算机编 -
中文分词之Java实现使用IK Analyzer实现 | 学步园
2021-03-21 08:08:02需要在项目中引入:IKAnalyzer.cfg.xmlIKAnalyzer2012.jarlucene-core-3.6.0.jarstopword.dic什么都不用改示例代码如下(使用IK ...import java.io.IOException;import java.io.StringReader;import org.apache... -
matlab代码解释器-rockstar-java:Rockstar语言的Java解释器
2021-05-23 11:43:54Matlab代码解释器请参阅有关此出色语言的更多信息。 是的,我知道您在想什么。...另外,我不能保证会不断更新该实现,但是,如果您真的想要,只要您问得好并且我有空闲时间(根据一年中的时间,这可能会很困难 -
squirrel_language:使用 Lex 和 Yacc 开发语言的项目
2021-06-02 08:20:19为了开发 Squirrel,我们分别使用了和 ,分别是 YACC 和 Lex 的实现。 “手工”编译 要生成编译器可执行文件,可以直接使用 flex 和 bison(以及一些 C 编译器)。 然而,推荐的方法是使用 waf(见下文)。 生成...