想了解更多内容,移步至编译原理专栏
==========================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;elsereturn false;}public boolean digit() {if(ch>='0'&&ch<='9')return true;elsereturn 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 blocke.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 blocke1.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 annotationif /***编译原理大法好!*/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基础了。

 

















