编译原理 实验一 java语言实现对C语言词法分析

Posted _DiMinisH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译原理 实验一 java语言实现对C语言词法分析相关的知识,希望对你有一定的参考价值。

实验一 C语言词法分析器 (预习报告) 

一、实验目的

  1. 理解词法分析在编译程序中的作用;
  2. 掌握词法分析程序的实现方法和技术

二、实验原理

  输入源程序,扫描分解字符串,通过状态转换图,识别出对应的标识符。

三、实验要求

  1. 单词分类
      明确所分析的代码片段包含的单词种别,以及有限单词的具体内容。比如保留字集合、运算符集合等。
  2. 待分析的源程序的输入形式和识别后单词的输出形式
      明确输入以文件输入,输出二元组中单词种别码的表述形式。
  3. 单词状态转换图
      给出各类单词识别的状态转换图。
  4. 算法设计
      path是文件路径,isShow表示用不用,返回值是一个结果
    public static Map<String, Pair<String, String>> analyseToAll (String path, boolean isShow)
      map是结果,函数用来输出全部结果
    public static void printAll (Map<String, Pair<String, String>> map)

实验一 C语言词法分析器 (实验报告) 

一、概述

  实验可以实现对关键字、特殊符号、运算符、常量的分析,常量可以识别出字符常量、字符串常量,同时可以删除程序中的注释。
  实验亮点:对注释删除,识别字符和字符串常量,可以用于分析其他源程序,只需要改动符号链表即可

二、实验方案

1. 词法分析器的结构

  词法分析器以循环结构为主,每次读取c语言源代码文件中的数据进行判断,实现对c语言源程序的词法分析

Delimiter 界符
KeyWord 关键字
Operator 运算符
SpecialIdentifier 特殊符号

2. 词法分析器的状态转换图

3. 词法分析器的程序流程图

三、实验过程及结果分析

1.测试过程
  • 测试数据
#include <stdio.h>
/*
    书籍结构体
*/
struct Books

   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
 book = "C 语言", "RUNOOB", "编程语言", 123456;

int main()

    printf("title : %s\\nauthor: %s\\nsubject: %s\\nbook_id: %d\\n", book.title, book.author, book.subject, book.book_id);

  • 测试结果
特殊符 : #
关键字 : include
界符 : <
标识符 : stdio
运算符 : .
标识符 : h
界符 : >
关键字 : struct
标识符 : Books
界符 : 
关键字 : char
标识符 : title
界符 : [
常量 : 50
界符 : ]
特殊符 : ;
关键字 : char
标识符 : author
界符 : [
常量 : 50
界符 : ]
特殊符 : ;
关键字 : char
标识符 : subject
界符 : [
常量 : 100
界符 : ]
特殊符 : ;
关键字 : int
标识符 : book_id
特殊符 : ;
界符 : 
标识符 : book
运算符 : =
界符 : 
界符 : "
字符串 : C 语言
界符 : "
运算符 : ,
界符 : "
字符串 : RUNOOB
界符 : "
运算符 : ,
界符 : "
字符串 : 编程语言
界符 : "
运算符 : ,
常量 : 123456
界符 : 
特殊符 : ;
关键字 : int
标识符 : main
界符 : (
界符 : )
界符 : 
标识符 : printf
界符 : (
界符 : "
字符串 : title : %s\\nauthor: %s\\nsubject: %s\\nbook_id: %d\\n
界符 : "
运算符 : ,
标识符 : book
运算符 : .
标识符 : title
运算符 : ,
标识符 : book
运算符 : .
标识符 : author
运算符 : ,
标识符 : book
运算符 : .
标识符 : subject
运算符 : ,
标识符 : book
运算符 : .
标识符 : book_id
界符 : )
特殊符 : ;
界符 : 

Process finished with exit code 0

2.调试分析
  1. 如何区分大于小于号是运算符还是界符(<studio.h>)
    分析:当前面出现#号,表示当前行的大于小于号是界符,如果当前行没有#号,则表示大于小于号一定是运算符。
    解决:增加boolean类型变量 isTheSameLine,指出当前行有没有#号,如果有,不进行运算符判断操作,如果没有不进行界符判断操作。
  2. 多行注释识别
    分析:多行注释以/开头/结尾,可以使用滞后输出的方式,即当前读取的字符先不着急作出判断,先入栈,等到下一次读取到字符,统一进行操作,当判断出出现了/,则接下来读取的字符都不进行关键字、标识符等判断操作,只有读到/再进行判断操作。
    解决:增加三个boolean类型变量:isText表示接下来读取到的都是注释里面的内容,不进行判断;isStar表示接下来的注释是多行注释,而不是//这种单行注释,lastIsStar表示上一个读取到的是*,用来判断是否多行注释结束,在对c语言源程序读入分析时,当读取到/后,接下来只用判断是否读入了,如果读入,则lastIsStar = true,继续读取,如果读取到下一个是/,则结束注释,如果不是/,则lastIsStar 又变为false。
3. 程序运行界面

四、总结与体会

  本次试验总体评价优,与预期结果相符合,本实验主要用到的就是Java文件操作,不算复杂,但是主要的逻辑在于如何分析每一个词,试验过程是一个解决问题的过程,从本次试验中我学到了词法分析器的原理、如何用程序实现词法分析器,但还是有些遗憾,没有实现对浮点数的分析,总体来说很不错,加油。

五、代码

1. 词法分析
package main;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Comparator;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;

import static main.Constants.*;

/**
 * @author Diminish
 * @date 2022/4/9 7:46
 */
public class WordAnalysis 

    /**
     * 读取到数字或字母后所暂存的字符串
     * */
    private static String input = "";

    /**
     * 记录读取到的关键字个数, 可以改为当前行数
     * */
    private static int index = 1;

    /**
     * 记录引号个数
     * */
    private static int number = 0;

    /**
     * 记录单引号个数
     * */
    private static int single = 0;

    /**
     * 是否当前行有#号
     * */
    private static boolean isTheSameLine = false;

    /**
     * 接下来读入的都是注释里的内容
     * */
    private static boolean isText = false;

    /**
     * 当前注释是多行注释
     * */
    private static boolean isStar = false;

    /**
     * 上一个符号是*
     * */
    private static boolean lastIsStar = false;

    /**
     * @param path C文件路径
     * @param isShow 是否显示输入的源码
     * @return 单词种别二元组
     * */
    public static Map<String, Pair<String, String>> analyseToAll (String path, boolean isShow) 

        Map<String, Pair<String, String>> result = new TreeMap<>(Comparator.comparingInt(Integer::parseInt));

        try (Reader fileReader = new FileReader(path)) 

            StringBuilder stringBuilder = new StringBuilder();

            // 读取到的字符
            int c;
            while ((c = fileReader.read()) != -1) 
                stringBuilder.append((char) c);

                String ch = (char) c + "";
                // 判断注释
                if (isText) 
                    // 开启了注释
                    switch (ch) 
                        case "\\n":
                            if (!isStar) 
                                isText = false;
                            
                            break;
                        case "*":
                            lastIsStar = true;
                            break;
                        case "/":
                            if (lastIsStar && isStar) 
                                isText = false;
                            
                            break;
                        default:
                            lastIsStar = false;
                            break;
                    
                    continue;
                
                // 判断是数字或者字母
                else if (ch.matches(NUMBER_LETTER_REGULAR_EXPRESSION)) 
                    // 数字或字母加入到待处理的字符串input中
                    input += ch;
                    if (!OPERATOR_STACK.empty()) 
                        String operator = OPERATOR_STACK.pop();
                        result.put(index++ + "", new Pair<>(OPERATOR, operator));
                    
                    continue;
                 else if (number % 2 != 0 && !"\\"".equals(ch)) 
                    input += ch;
                    continue;
                 else if (number % 2 != 0) 
                    result.put(index++ + "", new Pair<>(STRING, input));
                    input = "";
                 else if (single % 2 != 0 && !"'".equals(ch)) 
                    input += ch;
                    continue;
                 else if (single % 2 != 0) 
                    result.put(index++ + "", new Pair<>(CHAR, input));
                    input = "";
                

                // 判断是不是空格
                if (" ".equals(ch) || "\\r".equals(ch) || "\\n".equals(ch)) 
                    if (!"".equals(input)) 
                        isKeyWord(result);
                    

                    if (!OPERATOR_STACK.empty()) 
                        String operator = OPERATOR_STACK.pop();
                        result.put(index++ + "", new Pair<>(OPERATOR, operator));
                    

                    if ("\\n".equals(ch)) 
                        isTheSameLine = false;
                    
                
                // 不是字母或者数字就进入
                else 

                    // 判断是不是特殊符号
                    if (SpecialIdentifier.isSpecialIdentifier(ch)) 
                        // 判断是不是关键字
                        isKeyWord(result);
                        if ("#".equals(ch)) 
                            isTheSameLine = true;
                        
                        result.put(index++ + "", new Pair<>(SPECIAL_IDENTIFIER, ch));
                    

                    // 判断是不是运算符
                    if (Operator.isOperator(ch)) 
                        // 判断是不是关键字
                        isKeyWord(result);
                        boolean b = "<".equals(ch) || ">".equals(ch);
                        if (!isTheSameLine) 
                            // 大于小于号是界符
                            if (b) 
                                if (!OPERATOR_STACK.empty()) 
                                    String halfOperator = OPERATOR_STACK.pop();
                                    if (Operator.isOperator(halfOperator + ch)) 
                                        result.put(index++ + "", new Pair<>(OPERATOR, halfOperator + ch));
                                     else if (",".equals(halfOperator)) 
                                        result.put(index++ + "", new Pair<>(OPERATOR, halfOperator));
                                        result.put(index++ + "", new Pair<>(OPERATOR, ch));
                                     else 
                                        error(halfOperator + ch, stringBuilder, "错误的运算符", result);
                                        return result;
                                    
                                 else 
                                    OPERATOR_STACK.push(ch);
                                
                             else 
                                if (!OPERATOR_STACK.empty()) 
                                    String halfOperator = OPERATOR_STACK.pop();
                                    if (Operator.isOperator(halfOperator + ch)) 
                                        result.put(index++ + "", new Pair<>(OPERATOR, halfOperator + ch));
                                     else if (",".equals(halfOperator)) 
                                        result.put(index++ + "", new Pair<>(OPERATOR, halfOperator));
                                        result.put(index++ + "", new Pair<>(OPERATOR, ch));
                                     else if ("//".equals(halfOperator + ch)) 
                                        isText = true;
                                     else if ("/*".equals(halfOperator + ch)) 
                                        isText = true;
                                        isStar = true;
                                     else 
                                        error(halfOperator + ch, stringBuilder, "错误的运算符", result);
                                        return result;
                                    
                                 else 
                                    OPERATOR_STACK.push(ch);
                                
                            
                         else 
                            if (!b) 
                                if (!OPERATOR_STACK.empty()) 
                                    String halfOperator = OPERATOR_STACK.pop();
                                    if (Operator.isOperator(halfOperator + ch)) 
                                        result.put(index++ + "", new Pair<>(OPERATOR, halfOperator + ch));
                                     else if (",".equals(halfOperator)) 
                                        result.put(index++ + "", new Pair<>(OPERATOR, halfOperator));
                                        result.put(index++ + "", new Pair<>(OPERATOR, ch));
                                     else if ("/*".equals(halfOperator + ch) || "//".equals(halfOperator + ch)) 
//                                        "/*".equals(halfOperator + ch) ||
                                        isText = true;
                                     else 
                                        error(halfOperator + ch, stringBuilder, "错误的运算符", result);
                                        return result;
                                    
                                 else 
                                    OPERATOR_STACK.push(ch);
                                
                            
                        
                    
                    // 判断是不是界符
                    if (Delimiter.isDelimiter(ch)) 

                        // 判断是不是关键字
                        isKeyWord(result);

                        // 栈不空
                        if (!IDENTIFIER_STACK.empty()) 
                            // 判断c的类型, 是不是左括号
                            if (Delimiter.isLeftDelimiter(ch)) 
                                // 左界符入栈
                                if ("<".equals(ch) || ">".equals(ch)) 
                                    if (isTheSameLine) 
                                        result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                        IDENTIFIER_STACK.push(ch);
                                    
                                 else 
                                    result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                    IDENTIFIER_STACK.push(ch);
                                
                            
                            // 判断是不是引号
                            else if (Delimiter.isNotOrientationDelimiter(ch)) 
                                if ("\\"".equals(ch)) 
                                    boolean inc = false;
                                    if (number == 0) 
                                        number++;
                                        inc = true;
                                    
                                    // 引号为奇数
                                    if (number % 2 != 0) 
                                        result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                        if (!inc) 以上是关于编译原理 实验一 java语言实现对C语言词法分析的主要内容,如果未能解决你的问题,请参考以下文章

编译原理--词法分析器(python语言实现)

递归下降语法分析实验和词法分析实验报告,是编译原理的,做好直接发我邮箱 516786727@qq.com

编译原理实验:实验一 简单词法分析程序设计(必修)(Python实现)

编译原理课程设计词法分析程序设计

实验一词法分析试验报告

编译原理:实验二 递归下降语法分析