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

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

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

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

二、实验原理

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

三、实验要求
  1. 单词分类
      明确所分析的代码片段包含的单词种别,以及有限单词的具体内容。

    比如保留字集合、运算符集合等。

  2. 待分析的源程序的输入形式和识别后单词的输出形式
      明确输入以文件输入,输出二元组中单词种别码的表述形式。

  3. 单词状态转换图
      给出各类单词识别的状态转换图。

  4. 算法设计
      path是文件路径,isShow表示用不用,返回值是一个结果
    public static Map> analyseToAll (String path, boolean isShow)
      map是结果,函数用来输出全部结果
    public static void printAll (Map> map)
实验一 C语言词法分析器 (实验报告) 
一、概述

  实验可以实现对关键字、特殊符号、运算符、常量的分析,常量可以识别出字符常量、字符串常量,同时可以删除程序中的注释。


  实验亮点:对注释删除,识别字符和字符串常量,可以用于分析其他源程序,只需要改动符号链表即可

二、实验方案 1. 词法分析器的结构

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

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

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

3. 词法分析器的程序流程图 三、实验过程及结果分析 1.测试过程
  • 测试数据
#include 
/*
    书籍结构体
*/
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. 如何区分大于小于号是运算符还是界符(
    分析:当前面出现#号,表示当前行的大于小于号是界符,如果当前行没有#号,则表示大于小于号一定是运算符。


    解决:增加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) {
                                            number = 0;
                                        }
                                    } else {
                                        error(ch, stringBuilder, "引号不匹配", result);
                                        return result;
                                    }
                                } else {
                                    boolean inc = false;
                                    if (single == 0) {
                                        single++;
                                        inc = true;
                                    }
                                    // 引号为奇数
                                    if (single % 2 != 0) {
                                        result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                        if (!inc) {
                                            single = 0;
                                        }
                                    } else {
                                        error(ch, stringBuilder, "引号不匹配", result);
                                        return result;
                                    }
                                }

                            }
                            // 右界符
                            else {
                                String leftDelimiter = IDENTIFIER_STACK.peek();
                                // 判断是否匹配
                                if ("<".equals(ch) || ">".equals(ch)) {
                                    if (isTheSameLine) {
                                        if (Delimiter.isMatch(leftDelimiter, ch)) {
                                            IDENTIFIER_STACK.pop();
                                            result.put(index++ + "", new Pair<>(DELIMITER, ch + ""));
                                        }
                                    }
                                } else {
                                    if (Delimiter.isMatch(leftDelimiter, ch)) {
                                        IDENTIFIER_STACK.pop();
                                        result.put(index++ + "", new Pair<>(DELIMITER, ch + ""));
                                    }
                                }
                            }
                        }
                        // 空栈
                        else {
                            if ("<".equals(ch) || ">".equals(ch)) {
                                if (isTheSameLine) {
                                    // 判断是不是引号
                                    if (Delimiter.isNotOrientationDelimiter(ch)) {
                                        // 是不是第一次出现引号
                                        if (number == 0) {
                                            result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                            number++;
                                        } else {
                                            error(ch, stringBuilder, "引号不匹配", result);
                                            return result;
                                        }
                                    }
                                    // 判断是不是左界符
                                    else if (!Delimiter.isLeftDelimiter(ch)) {
                                        error(ch, stringBuilder, "括号不匹配", result);
                                        return result;
                                    } else {
                                        // 左界符入栈
                                        if (isTheSameLine) {
                                            result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                            IDENTIFIER_STACK.push(ch);
                                        }
                                    }
                                }
                            } else {
                                // 判断是不是引号
                                if (Delimiter.isNotOrientationDelimiter(ch)) {
                                    if ("'".equals(ch)) {
                                        // 是不是第一次出现引号
                                        if (single == 0) {
                                            result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                            single++;
                                        } else {
                                            if (single % 2 != 0) {
                                                result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                                single = 0;
                                            } else {
                                                error(ch, stringBuilder, "引号不匹配", result);
                                                return result;
                                            }
                                        }
                                    } else {
                                        // 是不是第一次出现引号
                                        if (number == 0) {
                                            result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                            number++;
                                        } else {
                                            if (number % 2 != 0) {
                                                result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                                number = 0;
                                            } else {
                                                error(ch, stringBuilder, "引号不匹配", result);
                                                return result;
                                            }
                                        }
                                    }
                                }
                                // 判断是不是左界符
                                else if (!Delimiter.isLeftDelimiter(ch)) {
                                    error(ch, stringBuilder, "括号不匹配", result);
                                    return result;
                                } else {
                                    // 左界符入栈
                                    result.put(index++ + "", new Pair<>(DELIMITER, ch));
                                    IDENTIFIER_STACK.push(ch);
                                }
                            }

                        }

                    }

                }

            }

            if (!IDENTIFIER_STACK.empty()) {
                error("当前行", stringBuilder, "括号不匹配", result);
                return result;
            }

            if (!"".equals(input)) {
                isKeyWord(result);
            }

            if (isShow) {
                System.out.println(stringBuilder);
            }

            return result;

        } catch (IOException e) {
            System.out.println(e.getMessage());
            return result;
        }
    }

    /**
     * 正则表达式, 判断是不是数字, 字母, 汉字, 汉字的unicode码在 \u4e00 - \u9fa5 之间, 需要转义
     * */
    private static final String NUMBER_LETTER_REGULAR_EXPRESSION = "\d|[a-z]|[A-Z]|[\u4e00-\u9fa5]|_";

    private static final Stack<String> IDENTIFIER_STACK = new Stack<>();

    private static final Stack<String> OPERATOR_STACK = new Stack<>();

    private static final Map<String, Pair<String, String>> MAP = new TreeMap<>(Comparator.comparingInt(Integer::parseInt));

    private static void error (String ch, StringBuilder stringBuilder, String message, Map<String, Pair<String, String>> map) {
        System.out.println("错误: " + ch);
        System.out.println("错误位置: " + stringBuilder);
        System.out.println("错误信息: " + message);
        System.out.print("标识符栈: ");
        IDENTIFIER_STACK.forEach(e -> System.out.print(e + ""));
        System.out.println();
        System.out.print("符号栈: ");
        OPERATOR_STACK.forEach(e -> System.out.print(e + ""));
        printAll(map);
    }

    public static void printAll (Map<String, Pair<String, String>> map) {

        for (var i : map.entrySet()) {
            System.out.println(i.getValue().toString());
        }
    }

    public static void printAllConstants (Map<String, Pair<String, String>> map) {

        for (var i : map.entrySet()) {
            if ("常量".equals(i.getValue().getFirst())) {
                System.out.println(i.getValue().toString());
            }
        }
    }

    /**
     * 判断是不是关键字
     * */
    private static void isKeyWord (Map<String, Pair<String, String>> result) {
        // 判断是不是关键字
        if (KeyWord.isKeyWord(input)) {
            result.put(index++ + "", new Pair<>(KEYWORD, input));
        } else {
            if (input.matches("\d")) {
                result.put(index++ + "", new Pair<>(CONSTANT, input));
            } else if (!"".equals(input)) {
                try {
                    Integer.parseInt(input);
                    result.put(index++ + "", new Pair<>(CONSTANT, input));
                } catch (Exception e) {
                    result.put(index++ + "", new Pair<>(IDENTIFIER, input));
                }
            }
        }
        input = "";
    }

}


2. 关键字
package main;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;


/**
 * @author Diminish
 * @date 2022/4/9 7:57
 * 

关键字

*/
@Getter @Setter @NoArgsConstructor public class KeyWord { private static final ArrayList<String> KEY_WORDS = generateKeyWord(); public static boolean isKeyWord (String string) { return KEY_WORDS.stream().anyMatch(keyWord -> keyWord.equals(string)); } public static ArrayList<String> generateKeyWord () { ArrayList<String> list = new ArrayList<>(); list.add("auto"); list.add("int"); list.add("double"); list.add("float"); list.add("long"); list.add("char"); list.add("signed"); list.add("unsigned"); list.add("short"); list.add("struct"); list.add("enum"); list.add("static"); list.add("switch"); list.add("case"); list.add("default"); list.add("break"); list.add("continue"); list.add("register"); list.add("const"); list.add("volatile"); list.add("typedef"); list.add("extern"); list.add("return"); list.add("void"); list.add("do"); list.add("while"); list.add("for"); list.add("if"); list.add("else"); list.add("goto"); list.add("sizeof"); list.add("include"); return list; } }
3. 界符
package main;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.lang.reflect.Array;
import java.util.ArrayList;

/**
 * @author Diminish
 * @date 2022/4/9 8:10
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Delimiter {

    /**
     * 单词种别
     * */
    private String type;

    /**
     * 界符
     * */
    private String delimiter;

    private static final ArrayList<String> DELIMITERS = generateDelimiter();

    public static boolean isDelimiter (String c) {
        return DELIMITERS.stream().anyMatch(delimiter -> delimiter.equals(c));
    }

    public static boolean isLeftDelimiter (String c) {
        return DELIMITERS.stream().limit(4).anyMatch(delimiter -> delimiter.equals(c));
    }

    public static boolean isNotOrientationDelimiter (String c) {
        return DELIMITERS.stream().skip(8).anyMatch(delimiter -> delimiter.equals(c));
    }

    public static boolean isMatch (String left , String now) {
        return switch (left) {
            case "(" -> ")".equals(now);
            case "{" -> "}".equals(now);
            case "[" -> "]".equals(now);
            case "<" -> ">".equals(now);
            default -> false;
        };
    }

    public static ArrayList<String> generateDelimiter () {

        ArrayList<String> list = new ArrayList<>();

        list.add("(");
        list.add("[");
        list.add("{");
        list.add("<");
        list.add(")");
        list.add("]");
        list.add("}");
        list.add(">");
        list.add("\"");
        list.add("'");

        return list;
    }
}


4. 特殊符
package main;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Diminish
 * @date 2022/4/9 8:27
 * 

特殊符号

*/
@Getter @Setter @NoArgsConstructor public class SpecialIdentifier { private static final ArrayList<String> SPECIAL_IDENTIFIERS = generateSpecialIdentifier(); public static boolean isSpecialIdentifier (String c) { return SPECIAL_IDENTIFIERS.stream().anyMatch(delimiter -> delimiter.equals(c)); } public static ArrayList<String> generateSpecialIdentifier () { return new ArrayList<>(List.of( "#", ";", ":" )); } }
5. 运算符
package main;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;

/**
 * @author Diminish
 * @date 2022/4/9 8:16
 * 

运算符

*/
@Getter @Setter @NoArgsConstructor public class Operator { private static final ArrayList<String> OPERATORS = generateOperator(); public static boolean isOperator (String c) { return OPERATORS.stream().anyMatch(delimiter -> delimiter.equals(c)); } public static ArrayList<String> generateOperator () { ArrayList<String> list = new ArrayList<>(); list.add("="); list.add("+"); list.add("-"); list.add("*"); list.add("/"); list.add("%"); list.add("++"); list.add("--"); list.add("+="); list.add("-="); list.add("*="); list.add("/="); list.add("%="); list.add("=="); list.add("!="); list.add("<="); list.add(">="); list.add("<"); list.add(">"); list.add("&&"); list.add("||"); list.add("!"); list.add("&"); list.add("|"); list.add("~"); list.add("."); list.add(","); return list; } }
6. 常量
package main;

/**
 * @author Diminish
 * @date 2022/4/14 0:11
 */
public class Constants {

    public static final String DELIMITER = "界符";

    public static final String KEYWORD = "关键字";

    public static final String OPERATOR = "运算符";

    public static final String SPECIAL_IDENTIFIER = "特殊符";

    public static final String IDENTIFIER = "标识符";

    public static final String CONSTANT = "常量";

    public static final String STRING = "字符串";

    public static final String CHAR = "字符";
}

7. 测试
package main;

import java.util.Map;
import java.util.TreeMap;

/**
 * @author Diminish
 * @date 2022/4/9 9:39
 */
public class Test {

    public static void main(String[] args) {
        Map<String, Pair<String, String>> result;
        String path = "C:\Users\Administrator\Desktop\JavaTest\CompilationPrincipleSrc\resource\main.c";
        result = WordAnalysis.analyseToAll(path, false);
        WordAnalysis.printAll(result);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/662063.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-18
下一篇 2022-04-18

发表评论

登录后才能评论

评论列表(0条)

保存