编译原理实验1——词法分析器的Java实现

news2024/11/24 4:02:42

一、  实验目的

设计并实现一个PL/0语言(或其它语言的子集,如C语言的子集)的词法分析程序,加深对词法分析原理的理解。

二、实验原理

词法分析是从左向右扫描每行源程序的符号,拼成单词,换成统一的机内表示形式——TOKEN字,送给语法分析程序。

TOKEN字是一个二元式:(单词种别码,自身值)。PL/0语言单词的种别码用整数表示,可参考教材或自行设定;单词自身值按如下规则给出:

1  标识符的自身值是它在符号表的入口地址。

  1. 常数的自身值是常数本身。
  2. 关键字和界限符的自身值为本身。

三、  实验步骤与要求

1、要求根据状态图,设计实现词法分析器。

  2、编制程序,此程序应具有如下功能:

  1. 输入:字符串(待进行词法分析的源程序),可从文件读或入从键盘直接输入

输出:由(种别码,自身值)所组成的二元组序列,二元组序列可保存到一个文件中,也可直接屏幕显示。

单词的种别码是语法分析需要的信息,可用整数编码表示,例如:标识符的种别码为1,常数为2,保留字为3,运算符为4,界符为5。

单词的自身值是编译其它阶段需要的信息,标识符的自身值是标识符在符号表入口,其他类型单词的自身值是其本身。  

可以参考下面的示例:

输入字符串if  i>=15 then  x := y;

输出: 

(3,‘if’)// i的符号表入口为0
(4,‘>=’)
(2,‘15’)
(3,‘then’)
(1,1) // x的符号表的入口为1
(4,‘:=’)
(1,2)   // y的符号表的入口为2
(5,‘;’)
  1. 功能:
  1. 滤空格
  2. 识别保留字:if then else while do  等
  3. 识别标识符:<字母>(<字母>|<数字>)*
  4. 识别整数数:0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*
  5. 识别典型的运算符和分隔符,例如 + - * / >  >= <= ( )
  1. 具有一定的错误处理功能。例如,能检查出程序语言的字符集以外的非法字符。

3、可以使用开发工具,自行设计界面,可以自行确定一些附加功能。

4、请指导教师检查程序和运行结果,评定成绩。

评定级别分优秀、良好、合格、不合格。第一次验收中如果存在问题,老师指出后允许改进,改进后再重新验收。成绩以最后一次的验收为准。

5、撰写并上交实验报告。

   必须提交实验报告,通过此环节训练实验总结与分析的能力。最后参考实验报告给出实验成绩。

四、上机报告内容

设计思路:

本次实验采用 Java 语言编写,词法分析器采用了模块化的设计思路,通过多个正则表达式模式进行词法元素匹配。程序首先定义了一系列正则表达式模式,包括匹配数字、字母和数字、运算符、界符等。此外,还定义了一个关键字集合,用于识别C语言关键字。

词法分析器的主要功能是通过lex方法对输入的源代码字符串进行分析。这个方法采用了迭代的方式,不断从输入字符串中匹配并提取词法元素。为了避免死循环,设置了一个限制循环次数的条件,当匹配次数超过100次仍未找到匹配项时,将其识别为非法字符。分析过程中,根据匹配到的词法元素类型将其封装为Token对象并添加到结果列表中。

程序最后还包括了一个displayTokens方法,用于输出分析结果。这个方法遍历词法元素列表,根据不同类型的Token对象进行格式化输出。

实验截图:

测试案例:

if i>=15 then  x := y;

代码:

仅供参考~

public class Lexer {
    // 存储分析结果的列表
    private static List<Token> tokens = new ArrayList<>();
    private static int count = 0; // 标识符记录器,每次自增 1
    // C 语言关键字集合
    private static Set<String> keywords = Set.of("auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "then");
    private static Map<String, Integer> typeCodes = Map.of("标识符", 1, "常数", 2, "保留字", 3, "运算符", 4, "界符", 5, "非法字符", 6);
    // regex 字符串表达式,识别数字
    private static Pattern patternNumber = Pattern.compile("\\d+");
    // regex 字符串表达式,识别字母和数字
    private static Pattern patternLetterNumber = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*");
    // regex 字符串表达式,识别C语言中的所有操作符
    private static Pattern patternSymbol = Pattern.compile("\\{\\}|\\{|\\}|~|==|>=|<=|'|=|\"|&&|\\^|\\(\\)|\\(|\\)|\\|\\||:=|[+\\-*/><]");
    // regex 字符串表达式,识别界符;
    private static Pattern patternDelimiter = Pattern.compile(";");

    public static void main(String[] args) {
        String  input   = "if i>=15 then  x := y;";
        Scanner scanner = new Scanner(input).useDelimiter("\\s+");
        // 拆分空格,对每一项进行词法分析
        while (scanner.hasNext()) {
            String next = scanner.next();
            lex(next);
        }
        displayTokens(); // 输出结果
    }

    /**
     * 词法分析,结果存入 tokens
     *
     * @param msg 输入字符串
     */
    public static void lex(String msg) {
        StringBuilder sb    = new StringBuilder(msg);
        int           times = 0;
        // 当输入字符串非空时,持续运行
        while (sb.length() > 0) {
            matchAndAddToken(sb, patternLetterNumber, typeCodes.get("标识符"));
            matchAndAddToken(sb, patternSymbol, typeCodes.get("运算符"));
            matchAndAddToken(sb, patternNumber, typeCodes.get("常数"));
            matchAndAddToken(sb, patternDelimiter, typeCodes.get("界符"));
            // 100 次循环还没匹配到,说明有非法字符
            if (times++ > 100) {
                String match = sb.substring(0, 1);
                sb.delete(0, 1);
                tokens.add(new Token(typeCodes.get("非法字符"), match));
            }
        }
    }

    /**
     * 尝试匹配给定的正则表达式,如果匹配成功,添加到 tokens 列表并返回 true,否则返回 false
     *
     * @param sb       输入字符串
     * @param pattern  正则表达式
     * @param typeCode 类型码
     * @return 是否匹配成功
     */
    private static boolean matchAndAddToken(StringBuilder sb, Pattern pattern, int typeCode) {
        Matcher matcher = pattern.matcher(sb.toString());
        if (matcher.find()) {
            String match = matcher.group();
            sb.delete(0, match.length());
            // 判断是不是关键字
            if (typeCode == typeCodes.get("标识符") && keywords.contains(match)) {
                tokens.add(new Token(typeCodes.get("保留字"), match));
            } else if (typeCode == typeCodes.get("标识符")) {
                tokens.add(new Token(typeCodes.get("标识符"), count, "//" + match + "符号表的入口为" + count++));
            }else {
                    tokens.add(new Token(typeCode, match));
                }
            return true;
        }
        return false;
    }

    // 输出结果
    public static void displayTokens() {
        for (Token token : tokens) {
            if (token.msg != null) {
                System.out.println("(" + token.typeCode + ", " + token.value + ")  " + token.msg);
            } else {
                System.out.println("(" + token.typeCode + ", '" + token.value + "')");
            }
        }
    }

    /**
     * Token 类
     * typeCode: 类型码
     * value: 值
     */
    static class Token {
        int typeCode;
        Object value;
        String msg;

        public Token(int typeCode, Object value) {
            this.typeCode = typeCode;
            this.value = value;
        }
        public Token(int typeCode, Object value, String msg) {
            this.typeCode = typeCode;
            this.value = value;
            this.msg = msg;
        }
    }
}

、实验总结与收获

本次实验主要目的是设计并实现一个PL/0语言(或其它语言的子集)的词法分析程序,以加深对词法分析原理的理解。实验采用Java语言编写,通过多个正则表达式模式进行词法元素匹配,实现了识别保留字、标识符、常数、运算符、界符等功能,并具有一定的错误处理功能。

通过本次实验,我们不仅深入理解了词法分析原理,还学习了Java语言的正则表达式和模块化设计思路。通过对源程序的扫描和分析,词法分析器可以为后续的语法分析和代码生成提供基础支持,为编译器的整个过程打下坚实的基础。

在实验中,我们发现了一些需要注意的问题,如需要考虑不同语言的差异,合理处理各种异常情况等。通过克服这些问题,我们不仅更加熟悉了词法分析的实现过程,还提高了自己的编程能力和代码质量。

总的来说,本次实验不仅加深了我们对词法分析原理的理解,还提高了我们的编程能力和代码质量。我们将继续努力学习编译原理相关知识,为以后的软件开发工作打下更加坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/457151.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

由浅入深MFC学习摘记--第四部分上

目录 第八章 Document-View结构为什么使用Document-View结构DocumentviewDocumentFrameDocumentTemplateCDocTemplate、CDocument、CView、CFrameWnd 之间的关系 Document - 数据结构设计容器选用范例修改线条与点 View-重绘与编辑代码修改View 的重绘鼠标消息处理类向导 Seria…

手推FlinkML2.2(三)

SQLTransformer&#xff08;SQL转换器&#xff09;是一种数据预处理方法&#xff0c;允许您使用SQL语句对数据进行转换和操作。SQL转换器通常用于数据清洗、特征工程和数据聚合等任务&#xff0c;以提高数据分析和机器学习模型的性能。它可以与各种数据处理和存储系统&#xff…

后端大厂面试总结大全六

目录&#xff1a; 1、Transactional注解控制事务有哪些不生效的场景2、MySQL的优化 1、Transactional注解控制事务有哪些不生效的场景 数据库引擎不支持事务数据源没有配置事务管理器没有被spring管理方法不是public的同一个类中方法调用&#xff0c;导致Transactional失效 举…

2023云数据库技术沙龙MySQL x ClickHouse专场成功举办

4月22日&#xff0c;2023首届云数据库技术沙龙 MySQL x ClickHouse 专场&#xff0c;在杭州市海智中心成功举办。本次沙龙由玖章算术、菜根发展、良仓太炎共创联合主办。围绕“技术进化&#xff0c;让数据更智能”为主题&#xff0c;汇聚字节跳动、阿里云、玖章算术、华为云、腾…

gitee教程

二、安装git 要使用gitee&#xff0c;需要先安装git工具。git工具下载&#xff1a;Git - Downloadshttps://git-scm.com/downloads 下载好后,检查是否真的下载了&#xff1a; 三、登录gitee 我们先在 gitee上注册账号并登录。gitee官网&#xff1a;Gitee - 企业级 DevOps 研…

LINUX SVN 新建项目

从第三方代码创建代码库&#xff1a; 1、通过客户端进入服务端 2、在对应的目录创建新的项目/目录 在对应的目录右击 &#xff1a;creat folder... 例&#xff1a;创建testSvn 3、在客户端checkout(co) testSvn 4、将第三方源码(srcTest)拷贝到客户端下的对应路径 防止L…

Redis缓存更新策略

缓存更新是redis为了节约内存而设计出来的一个东西&#xff0c;主要是因为内存数据宝贵&#xff0c;当我们向redis插入太多数据&#xff0c;此时就可能会导致缓存中的数据过多&#xff0c;所以redis会对部分数据进行更新&#xff0c;或者把他叫为淘汰更合适。 1.缓存更新三种策…

Windows 自带环境变量

目录 Windows自带环境变量说明Windows自带环境变量总结 所谓 Windows 环境变量&#xff0c;指的是 Windows 指定操作系统工作环境的一些设置选项或属性参数&#xff0c;比方说指定系统文件夹或临时文件夹的位置等。与常量相比&#xff0c;一个环境变量往往由变量名称和变量值组…

深入了解Android系统中的音视频编解码器:MediaCodec

Media内核源码 Media内核是Android系统中负责音视频处理的核心模块&#xff0c;包括音视频采集、编解码、传输、播放等功能。Media内核源码位于Android源码树的/frameworks/av目录下&#xff0c;主要包括以下模块&#xff1a; media/libstagefright&#xff1a;包含了Media F…

设计模式——组件协作模式之策略模式

文章目录 前言一、“组件协作” 模式二、Strategy 策略模式1、动机2、模式定义3、代码示例4、结构 要点总结 前言 一、“组件协作” 模式 现代软件专业分工之后的第一个结果是 “框架与应用程序的划分”&#xff0c;“组件协作” 模式通过晚期绑定&#xff0c;来实现框架与应用…

【内网】WebStorm搭建Vue环境

文章目录 安装node.js和npm1、 Node.js和npm简介2、下载Node.js3、安装Node.js4、检查是否安装成功5、环境配置&#xff08;很重要&#xff01;&#xff09;6、配置内网离线库 安装WebStorm并运行Vue项目1、新建项目2、测试 安装node.js和npm 1、 Node.js和npm简介 简单的说 …

阿里云服务器-Linux

说明&#xff1a;自己想玩一下服务器&#xff0c;记录一下服务器安装的环境以及软件。包括JDK&#xff0c;Mysql&#xff0c;tomcat等。 一&#xff1a;JDK篇 服务器上也是需要安装JDK环境的&#xff0c;跑不掉的。阿里云服务器提供了命令安装&#xff0c;固定安装目录自动配…

【Jmeter】Oracle数据迁移,批量插入测试数据~

前言~1.准备事项1.1 梳理脚本 2.动起手来2.1 了解 JDBC Connection Configuration2.2 配置 JDBC请求 3.生成测试链路3.1 获取表主键信息3.2 获取目标表表结构3.3 拼接数据并生成sql3.4 插入数据 4.优化脚本&#xff08;主键信息4.1 添加Beshell后置处理器4.2 优化拼接数据请求&…

C++类与对象——this指针

文章目录 概述this指针的引出this指针的特性将this指针用于多个对象的比较this指针可以为空吗&#xff1f; 概述 本篇博客主要讲解C类和对象模块中的this指针的知识&#xff0c;将会深入探究this指针的使用细节即使用时需要注意的点&#xff0c;并且简单讲解一下底层的实现原理…

Maven(五):Maven的使用——依赖的测试

Maven&#xff08;五&#xff09;&#xff1a;Maven的使用——依赖的测试 前言一、实验六&#xff1a;测试依赖的范围1、依赖范围1.1 compile 和 test 对比1.2 compile 和 provided 对比1.3 结论 二、实验七&#xff1a;测试依赖的传递性1、依赖的传递性1.1 概念1.2 传递的原则…

内网渗透之横向移动rdpwinrmwinrsspnkerberos

0x00 准备 环境&#xff1a;god.org cs上线 win2008web 提权利用ms14-058 抓取hash和明文密码(当获取到其他主机就重复提权和抓取密码) 扫描存活主机&#xff0c;扫描端口 代理转发-转发上线&#xff0c;生成反向连接木马&#xff0c;绑定监听器&#xff0c;上传至web根目录(方…

hardhat 本地连接matemask钱包

Hardhat 安装 https://hardhat.org/hardhat-runner/docs/getting-started#quick-start Running a Local Hardhat Network Hardhat greatly simplifies the process of setting up a local network by having an in-built local blockchain which can be easily run through a…

制作一个高质量的FAQ页面需要考虑哪些因素?

作为现代企业不可缺少的知识库&#xff0c;FAQ页面&#xff08;Frequently Asked Questions&#xff09;是集中呈现重要信息供用户查询和解决问题的权威场所。如果您在网站上提供了不错的产品或服务&#xff0c;很多客户都会通过访问FAQ页面找到他们需要的答案。 在设计和创建…

Linux Ansible安装以及环境搭建

Ansible介绍 Ansible是一种基于Python的自动化配置工具&#xff0c;通过OpenSSH的方式管理被管理节点&#xff0c;实现批量系统配置、批量程序部署、批量运行命令等功能。 当管理节点需要管理被管理节点时&#xff0c;只需要在管理节点安装ansible&#xff0c;无需在被管理节…

23种设计模式-代理模式

代理模式 在代理模式&#xff08;Proxy Pattern&#xff09;中&#xff0c;一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中&#xff0c;我们创建具有现有对象的对象&#xff0c;以便向外界提供功能接口。 介绍 意图&#xff1a;为其他对象提供…