大家好,欢迎来到IT知识分享网。
想了解更多内容,移步至编译原理专栏
==========================2021.12.22 更新===================================
整理了一下代码,同步到了gitee
https://gitee.com/godelgnis/lrparserhttps://gitee.com/godelgnis/lrparser
————————————————–分割线———————————————————————
这学期选修了编译原理,用的是電子工业出版社出版的《编译原理(第4版)》
最近做了第一次实验词法分析器,是教材后面的附录c.1的内容,根据下面的图创建词法分析器
课本给出了C语言版本的词法分析器,但是看着挺蛋疼的,感觉C语言的指针很烦,于是做了一个Java版本的,说简单也挺简单的(其实大部分是把C语言版本的代码直接复制过来)哈哈。
老师的实验要求是
所以对书上的代码输入输出的代码进行修改,另外,书上代码中的种别码与上面的表格不是对应的,所以也要进行修改
废话不多说,直接上代码
package codescanner;
public class Word {
private int typenum; //种别码
private String word; //扫描得到的词
public int getTypenum() {
return typenum;
}
public void setTypenum(int typenum) {
this.typenum = typenum;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
}
public class CodeScanner {
private static String _KEY_WORD_END = "end string of string";
private int charNum = 0;
private Word word;
private char[] input = new char[255];
private char[] token = new char[255];
private int p_input=0;
private int p_token=0;
private char ch;
private String[] rwtab = {"begin","if","then","while","do","end","",_KEY_WORD_END};
public CodeScanner(char[] input) {
this.input = input;
}
/**
* 取下一个字符
* @return
*/
public char m_getch() {
if(p_input < input.length) {
ch = input[p_input];
p_input++;
}
return ch;
}
/**
* 如果是标识符或者空白符就取下一个字符
*/
public void getbc() {
while((ch == ' ' || ch == '\t') && p_input < input.length) {
ch=input[p_input];
p_input++;
}
}
/**
* 把当前字符和原有字符串连接
*/
public void concat() {
token[p_token] = ch;
p_token++;
token[p_token] = '\0';
}
public boolean letter() {
if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')
return true;
else
return false;
}
public boolean digit() {
if(ch>='0'&&ch<='9')
return true;
else
return false;
}
/**
* 回退一个字符
*/
public void retract() {
p_input--;
}
/**
* 将token中的数字串转换成二进制值表示
* @return
*/
public String dtb() {
int num = token[0] - 48;
for(int i = 1; i < p_token; i++) {
num = num * 10 + token[i] - 48;
}
StringBuilder result = new StringBuilder();
while(num>0) {
int r = num % 2;
int s = num / 2;
result.append(r);
num = s;
}
return result.reverse().toString();
}
/**
* 查看token中的字符串是否是关键字,是的话返回关键字种别编码,否则返回10
* @return
*/
public int reserve() {
int i=0;
while(rwtab[i].compareTo(_KEY_WORD_END)!=0) {
if(rwtab[i].compareTo(new String(token).trim()) == 0) {
return i+1;
}
i++;
}
return 10;
}
/**
* 能够识别换行,单行注释和多行注释的
* 换行的种别码设置成30
* 多行注释的种别码设置成31
* @return
*/
public Word scan() {
token = new char[255];
Word myWord = new Word();
myWord.setTypenum(10);
myWord.setWord("");
p_token=0;
m_getch();
getbc();
if(letter()) {
while(letter()||digit()) {
concat();
m_getch();
}
retract();
myWord.setTypenum(reserve());
myWord.setWord(new String(token).trim());
return myWord;
}else if(digit()) {
while(digit()) {
concat();
m_getch();
}
retract();
myWord.setTypenum(11);
myWord.setWord(new String(token).trim()); //输出token中的数字串字符形式
// myWord.setWord(dtb()); //输出token中的数字串10进制值的二进制字符串形式
return myWord;
}
else
switch (ch) {
case '=':
myWord.setTypenum(25);
myWord.setWord("=");
return myWord;
case '+':
myWord.setTypenum(13);
myWord.setWord("+");
return myWord;
case '-':
myWord.setTypenum(14);
myWord.setWord("-");
return myWord;
case '*':
myWord.setTypenum(15);
myWord.setWord("*");
return myWord;
case '/':
m_getch();
//识别单行注释
if (ch == '/') {
while(m_getch() != '\n');
myWord.setTypenum(30);
myWord.setWord("\\n");
return myWord;
}
//识别多行注释
if(ch=='*') {
String string = "";
while(true) {
if (ch == '*') {
if (m_getch() == '/') {
myWord.setTypenum(31);
myWord.setWord(string);
return myWord;
}
retract();
}
if (m_getch() == '\n') {
string += "\\n";
}
}
}
retract();
myWord.setTypenum(16);
myWord.setWord("/");
return myWord;
case ':':
m_getch();
if(ch=='=') {
myWord.setTypenum(18);
myWord.setWord(":=");
return myWord;
}
retract();
myWord.setTypenum(17);
myWord.setWord(":");
return myWord;
case '<':
m_getch();
if(ch=='=') {
myWord.setTypenum(22);
myWord.setWord("<=");
return myWord;
}else if (ch == '>') {
myWord.setTypenum(21);
myWord.setWord("<>");
return myWord;
}
retract();
myWord.setTypenum(20);
myWord.setWord("<");
return myWord;
case '>':
m_getch();
if(ch=='=') {
myWord.setTypenum(24);
myWord.setWord(">=");
return myWord;
}
retract();
myWord.setTypenum(23);
myWord.setWord(">");
return myWord;
case ';':
myWord.setTypenum(26);
myWord.setWord(";");
return myWord;
case '(':
myWord.setTypenum(27);
myWord.setWord("(");
return myWord;
case ')':
myWord.setTypenum(28);
myWord.setWord(")");
return myWord;
case '\n':
myWord.setTypenum(30);
myWord.setWord("\\n");
return myWord;
case '#':
myWord.setTypenum(0);
myWord.setWord("#");
return myWord;
default:
concat();
myWord.setTypenum(-1);
myWord.setWord("ERROR INFO: WORD = \"" + new String(token).trim() + "\"");
return myWord;
}
}
}
package codescanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Scanner;
public class Analyzer {
private File inputFile;
private File outputFile;
private String fileContent;
private ArrayList<Word> list = new ArrayList<>();
public Analyzer(String input,String output) {
inputFile = new File(input);
outputFile = new File(output);
}
/**
* 从指定的文件中读取源程序文件内容
* @return
*/
public String getContent() {
StringBuilder stringBuilder = new StringBuilder();
try(Scanner reader = new Scanner(inputFile)) {
while (reader.hasNextLine()) {
String line = reader.nextLine();
stringBuilder.append(line + "\n");
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return fileContent = stringBuilder.toString();
}
/**
* 先将源程序中的注释和换行替换成空串
* 然后扫描程序,在程序结束前将扫描到的词添加到list中
* 最后把扫描结果保存到指定的文件中
*/
public void analyze(String fileContent) {
int over = 1;
Word word = new Word();
//fileContent = fileContent.replaceAll("//.*\n", "") // 去除字符串fileContent中所有的单行注释 与 换行
// .replaceAll("/\\*{1,2}[\\s\\S]*?\\*/", ""); // 去除字符串fileContent中所有的多行注释
//现在不用正则表达式也可以识别换行和单行注释和多行注释了
CodeScanner scanner = new CodeScanner(fileContent.toCharArray());
while (over != 0) {
word = scanner.scan();
// System.out.println("(" + word.getTypenum() + " ," + word.getWord() + ")"); //在控制台输出结果
list.add(word);
over = word.getTypenum();
}
saveResult();
}
/**
* 将结果写入到到指定文件中
* 如果文件不存在,则创建一个新的文件
* 用一个foreach循环将list中的项变成字符串写入到文件中
*/
public void saveResult() {
if (!outputFile.exists())
try {
outputFile.createNewFile();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try(Writer writer = new FileWriter(outputFile)){
for (Word word : list) {
writer.write("(" + word.getTypenum() + " ," + word.getWord() + ")\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Analyzer analyzer = new Analyzer("input.txt","output.txt");//输入输出可自己修改,文件放在当前文件夹下,刷新项目就可以看到了
analyzer.analyze(analyzer.getContent());
}
}
测试数据
begin
x:=9; //while there is a annotation
if
/**
*编译原理大法好!
*/
x>0
then
x:=2*x+1/3;
end
#
结果
觉得写得还可以得话,可以给我点个赞呀:)
===========================2020/12/1更新==================================
补充一下,项目结构,input.txt 和 output.txt 是和 src 目录同等级的,而且这个目录是可以自己改的,在main函数里自己改下路径,想放哪放哪,我当时这么写就是为了图方便而已。。。。。
input.txt 需要自己手动创建,内容需要自己输入,output.txt可以不创建(文件不存在会自动创建),它是用来保存分析结果的。
不要再私信我input或者output文件的问题了,代码不难,你稍微有点耐心看下就能看懂了(基本流程的注释都写了。。。。),要是看不懂,那么你可能需要加强一下Java基础了。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/12777.html