编译原理实验1——词法分析(Java代码实现)

一、 实验目的

设计并实现一个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);
    }
}
语法分析是编译原理中的重要部分,它的作用是将词法分析阶段得到的词法单元序列转换成抽象语法树(AST)或语法分析树(Parse Tree),以便于后续的语义分析、中间代码生成和目标代码生成等环节的进行。在本次实验中,我们将使用Java语言实现一个简单的语法分析器。 实验要求: 1. 实现自顶向下的递归下降分析器。 2. 支持的文法如下: ``` <program> ::= <stmts_list> <stmts_list> ::= <stmt> | <stmts_list> <stmt> <stmt> ::= <if_stmt> | <while_stmt> | <assign_stmt> <if_stmt> ::= if <condition> then <stmts_list> end <while_stmt> ::= while <condition> do <stmts_list> end <assign_stmt> ::= <id> = <expr> <condition> ::= <expr> <relop> <expr> <expr> ::= <term> | <expr> <addop> <term> <term> ::= <factor> | <term> <mulop> <factor> <factor> ::= <id> | <number> | '(' <expr> ')' <relop> ::= '<' | '>' | '=' | '<=' | '>=' | '<>' <addop> ::= '+' | '-' <mulop> ::= '*' | '/' <id> ::= <letter> | <id> <letter> | <id> <digit> <number> ::= <digit> | <number> <digit> <letter> ::= A | B | ... | Z | a | b | ... | z <digit> ::= 0 | 1 | ... | 9 ``` 注意:文法中的关键字 if、then、end、while、do、and 等均为保留字。 3. 实现的语法分析器应具备以下功能: - 能够识别出语法正确的程序,并输出相应的语法分析树或抽象语法树。 - 能够识别出语法错误的程序,并给出相应的错误提示信息。 - 能够处理注释和空格等无意义的字符。 4. 实验提交要求: - 实验报告,包括程序设计和实验结果分析。 - 程序源代码实验设计思路: 1. 根据给定的文法,设计语法分析器的语法规则和对应的产生式。 2. 编写相应的Java代码,将文法转换为递归下降分析器所需要的形式。 3. 实现从输入的源代码中读取词法单元序列的功能。 4. 实现递归下降分析器的核心算法,对输入的词法单元序列进行语法分析,并构建相应的语法分析树或抽象语法树。 5. 在语法分析过程中,需要处理注释和空格等无意义的字符,以便于正确识别语法错误。 6. 在语法分析过程中,需要对输入的源代码进行错误检查,并给出相应的错误提示信息。 7. 输出语法分析树或抽象语法树,以便于后续的语义分析、中间代码生成和目标代码生成等环节的进行。 实验结果分析: 经过实验测试,我们的语法分析器能够正确地识别出合法的程序,并输出相应的语法分析树或抽象语法树。同时,它也能够正确地识别出语法错误的程序,并给出相应的错误提示信息。总的来说,本次实验取得了较好的实验效果。 实验代码: 见下方代码框:
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值