一、 实验目的
设计并实现一个PL/0语言(或其它语言的子集,如C语言的子集)的词法分析程序,加深对词法分析原理的理解。
二、实验原理:
词法分析是从左向右扫描每行源程序的符号,拼成单词,换成统一的机内表示形式——TOKEN字,送给语法分析程序。
TOKEN字是一个二元式:(单词种别码,自身值)。PL/0语言单词的种别码用整数表示,可参考教材或自行设定;单词自身值按如下规则给出:
1 标识符的自身值是它在符号表的入口地址。
2 常数的自身值是常数本身。
3 关键字和界限符的自身值为本身。
三、 实验步骤与要求
1、要求根据状态图,设计实现词法分析器。
2、编制程序,此程序应具有如下功能:
1) 输入:字符串(待进行词法分析的源程序),可从文件读或入从键盘直接输入
输出:由(种别码,自身值)所组成的二元组序列,二元组序列可保存到一个文件中,也可直接屏幕显示。
单词的种别码是语法分析需要的信息,可用整数编码表示,例如:标识符的种别码为1,常数为2,保留字为3,运算符为4,界符为5。
单词的自身值是编译其它阶段需要的信息,标识符的自身值是标识符在符号表入口,其他类型单词的自身值是其本身。
可以参考下面的示例:
输入字符串if i>=15 then x := y;
输出:
(3,‘if’)
(1,0) // i的符号表入口为0
(4,‘>=’)
(2,‘15’)
(3,‘then’)
(1,1) // x的符号表的入口为1
(4,‘:=’)
(1,2) // y的符号表的入口为2
(5,‘;’)
2) 功能:
a. 滤空格
b. 识别保留字:if then else while do 等
c. 识别标识符:<字母>(<字母>|<数字>)*
d. 识别整数数:0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*
e. 识别典型的运算符和分隔符,例如 + - * / > >= <= ( ) ;
3) 具有一定的错误处理功能。例如,能检查出程序语言的字符集以外的非法字符。
四、实验代码
package s;
import java.io.*;
public class WordAnalysisTest {
public int ch;
public int code;//保留字状态码
public StringBuffer strToken = new StringBuffer();//存放构成单词符号的字符串
public String[] retainWord = {"int","if","else","return","main","void","while","break","then","do"};//保留字
public String[] operator = {"+", "-", "*", "/", ">", ">=", "<", "<=", "(", ")", "=", ":=",":"};
public static void main(String[] args) {
WordAnalysisTest wordAnalysis = new WordAnalysisTest();
wordAnalysis.scanner();
}
//判断是否是字母
public boolean isLetter(){
if(ch>='a' && ch<='z'){
return true;
}
return false;
}
//判断是否是数字
public boolean isDigit(){
if(ch>='0' && ch <= '9'){
return true;
}
return false;
}
//判断是否是空格
public boolean isBC(int ch){
if(ch == ' '){
return true;
}
return false;
}
//判断是否是分界符
public boolean isOperator() {
for(int i=0; i<operator.length; i++) {
if(Character.toString(ch).equals(operator[i])) {
return true;
}
}
return false;
}
//判断返回值
public int Reserve(){
//判断是否为保留字,是的话返回3
for(int i = 0; i < retainWord.length; i++){
if(strToken.toString().equals(retainWord[i])){
return 3;
}
}
//判断是否为分界符,是的话返回4
for(int j = 0; j < operator.length; j++) {
if(strToken.toString().equals(operator[j])) {
return 4;
}
}
//判断是否为数字,是的话返回2
if(strToken.length() != 0){
if(strToken.charAt(0)>='0' && strToken.charAt(0)<='9'){
return 2;
}
}
//判断是否为字母(变量),是的话返回1
if(strToken.length() != 0) {
if(strToken.charAt(0)>='a' && strToken.charAt(0)<='z') {
return 1;
}
}
//存在错误信息,打印输出
if(isLetter()==false && isDigit()==false && isBC(ch)==false && isOperator()==false) {
System.out.println("存在错误信息!");
}
return 0;
}
//书写格式
public void Retract(int count){
code = Reserve();
if(code == 1){
System.out.println("(1,"+count+")");
}else if(code == 2){
System.out.println("(2,'"+strToken+"')");
}
else if(code == 3){
System.out.println("(3,'"+strToken+"')");
}else if(code == 4) {
System.out.println("(4,'"+strToken+"')");
}
strToken.delete(0, strToken.length());
}
//读取文件并进行处理输出
public void scanner(){
BufferedReader br;
try {
br = new BufferedReader(new FileReader("C:\\Users\\zxf\\Desktop\\tests.txt"));
int count=0;
while((ch = br.read()) != -1){
if(isBC(ch) == false){
if(isLetter() == true){
if((strToken.length() != 0) && (Reserve() != 1)) {
Retract(count);
strToken.delete(0, strToken.length());
strToken.append((char)ch);
}else {
strToken.append((char)ch);
}
}else if(isDigit() == true){
if((strToken.length() != 0) && (Reserve() != 2)) {
if(Reserve() == 1) {
strToken.append((char)ch);
}else {
Retract(count);
strToken.delete(0, strToken.length());
strToken.append((char)ch);
}
}else {
strToken.append((char)ch);
}
}else if(isOperator() == true){
if((strToken.length() != 0) && (Reserve() != 4)){
if(Reserve() == 1) {
Retract(count);
count++;
}else {
Retract(count);
}
strToken.delete(0, strToken.length());
strToken.append((char)ch);
}else {
strToken.append((char)ch);
}
}else if((char) ch == ';'){
Retract(count);
System.out.println("(5,'"+(char) ch+"')");
}
}else{
if(Reserve() == 1) {
Retract(count);
count++;
}else {
Retract(count);
}
}
}
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
五、测试用例
1.测试数据(在一个txt文件中需要输入的数据):
if i>=15 then x:=y;
代码中读入文件的代码如下:
br = new BufferedReader(new FileReader("C:\\Users\\zxf\\Desktop\\tests.txt"));
2.求解所得结果输出:
六、最新版代码实现,欢迎交流
package indi.zxf.pro.standard.note.provider.model;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* 词法分析器
*
* @author zhouxiaofa
* @date 2024/4/19 13:08
**/
public class WordAnalyzer {
/**
* 保留字
*/
private final Set<String> retainWords = new HashSet<>(Arrays.asList("int", "if", "else", "return", "main", "void", "do", "while", "break", "then"));
/**
* 分界符
*/
private final Set<String> operators = new HashSet<>(Arrays.asList("+", "-", "*", "/", ">", ">=", "<", "<=", "=", "(", ")", ":=", ":"));
/**
* 结果
*/
private final StringBuffer result = new StringBuffer();
/**
* 结尾符
*/
private final char END_OPERATOR = ';';
/**
* 参数统计量
*/
private int paramCount;
/**
* 文件路径
*/
private final String FILE_PATH = "C:\\\\Users\\\\zxf\\\\Desktop\\\\test.txt";
/**
* 按照指定格式构建输出结果
*/
private void description(int code, int count) {
StringBuilder builder = new StringBuilder();
// 按照不同返回值拼接字符
switch (code) {
case 1:
builder.append("(1,").append(count).append(")");
break;
case 2:
builder.append("(2,'").append(result).append("')");
break;
case 3:
builder.append("(3,'").append(result).append("')");
break;
case 4:
builder.append("(4,'").append(result).append("')");
break;
case 5:
builder.append("(5,';')");
break;
}
// 打印输出
System.out.println(builder);
// 清空
result.delete(0, result.length());
}
/**
* 根据结果集进行前置输出
*/
private void printLetterOrDigitOrEnd() {
// 字母
if (Character.isAlphabetic(result.charAt(0))) {
description(1, paramCount++);
}
// 数字
else if (Character.isDigit(result.charAt(0))) {
description(2, paramCount);
}
// 结尾符
else if (END_OPERATOR == result.charAt(0)) {
description(5, paramCount);
}
}
/**
* 执行词法解析
*/
private void execute(String filePath) {
try {
// 获取文件
BufferedReader reader = new BufferedReader(new FileReader(filePath));
// 获取并分割待解析文本
String[] contents = reader.readLine().split(" ");
for (String content : contents) {
// 判断是否为保留字
if (retainWords.contains(content)) {
result.append(content);
description(3, paramCount);
}
else {
for (int index = 0; index < content.length(); index++) {
// 获取单个字符
char c = content.charAt(index);
// 字母
if (Character.isAlphabetic(c)) {
// 判断前置是否有分界符
if (operators.contains(result.toString())) {
description(4, paramCount);
}
}
// 分界符
else if (operators.contains(String.valueOf(c))) {
// 前置为非分界符
if (!operators.contains(result.toString())) {
printLetterOrDigitOrEnd();
}
}
// 数字
else if (Character.isDigit(c)) {
// 前置为分界符,输出并清空
if (operators.contains(result.toString())) {
description(4, paramCount);
}
}
// 结尾符
else if (END_OPERATOR == c) {
printLetterOrDigitOrEnd();
}
// 统一添加
result.append(c);
}
if (result.length() != 0) {
printLetterOrDigitOrEnd();
}
}
}
}
catch (Exception e) {
throw new RuntimeException("Exception occurred while expressing the file!");
}
}
public static void main(String[] args) {
WordAnalyzer wordAnalyzer = new WordAnalyzer();
wordAnalyzer.execute(wordAnalyzer.FILE_PATH);
}
}