编程实战:类C语法的编译型脚本解释器(七)语句
初级代码游戏的专栏介绍与文章目录-CSDN博客
我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
系列入口:
编程实战:类C语法的编译型脚本解释器(系列)-CSDN博客
本文介绍语句,主要是控制语句,if、else、for之类。
目录
一、语句概览
二、CheckSentence
三、ExecSentence
一、语句概览
//语句//EXPRESSION RETURN:expressions[0]//BLOCK:senetnces//IF:if(expressions[0])sentences[0] else sentences[1]//FOR:for(expressions[0];expressions[1];expressions[2])sentences[0]//DO:do sentences[0] while(expressions[0]);//WHILE:while(expressions[0])sentences[0]struct Sentence{enum types { NULLSENTENCE = 0, DECLARE, EXPRESSION, BLOCK, RETURN, IF, FOR, BREAK, CONTINUE, DO, WHILE };types type;size_t source_start;//起始位置size_t source_end;//结束位置vector<Expression > expressions;vector<Sentence > sentences;Sentence() { clear(); }void clear(){type = NULLSENTENCE;source_start = 0;source_end = 0;expressions.clear();sentences.clear();}bool isLoop()const { return FOR == type || DO == type || WHILE == type; }//inloop=true表示处于循环体内,可能从上层带入bool CheckSentence(string& source, T_VARIABLE_S& vars, bool _inloop);//若为return语句则设置bFinishbool ExecSentence(CZBScript const& script, T_VARIABLE_S& vars, bool& bFinish, bool& bBreak, bool& bContinue, Variable& ret, void* pe)const;string ToString(string const& source, T_VARIABLE_S const& vars, long level = 0)const;
};
语句比表达式简单多了。语句的类型更容易理解,大部分语句类型都是控制语句,有特殊的关键字对应,其余几个需要特别理解:
- 声明语句 声明并定义变量
- 表达式语句 整个语句就是一个表达式
- 块语句 就是用大括号包裹的一组语句
从表达式和语句的分析可以看出来,语言结构就是一系列递归。
语句对象除了类型和辅助调试的开始位置、结束位置外,只有子语句和表达式两个成员变量。块语句包含一组语句,for语句则包含三个表达式和一个语句:
//for语句的逻辑结构
for(表达式1;表达式2;表达式3)语句
for语句的第二个表达式的结果必须是逻辑值。
if语句则包含一个逻辑表达式和一个或两个子语句:
if(逻辑表达式)语句;
或者:
if(逻辑表达式)语句1;else 语句2;
单语句和大括号困扰了很多人,C语法中所有控制结构都是基于“语句”的,也就是单语句,为了多塞几个语句,就要用大括号括起来,变成块语句、复合语句或者别的叫法,总之就是可以当成一个“语句”的东西。
二、CheckSentence
编译后检查语法:
//inloop=true表示处于循环体内,可能从上层带入bool CheckSentence(string& source, T_VARIABLE_S& vars, bool _inloop){bool inloop = (_inloop || isLoop());size_t i;if (FOR == type){vars.PushLevel();//只有for语句的表达式可以定义变量if (sentences.size() != 2)CException::Throw(__FILE__, __LINE__, source, source_start, "for语句子语句必须有2个");if (!sentences[0].CheckSentence(source, vars, inloop))return false;}for (i = 0; i < expressions.size(); ++i){set<string > setpp;if (!expressions[i].CheckExpression(source, vars, 0, setpp))return false;}switch (type){case NULLSENTENCE:if (expressions.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "空语句不能有表达式");if (sentences.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "空语句不能有子语句");break;case DECLARE:if (expressions.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "变量定义语句有且只能有一个表达式");if (sentences.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "变量定义语句语句不能有子语句");if (expressions[0].type != Expression::DEFINE)CException::Throw(__FILE__, __LINE__, source, source_start, "变量定义语句表达式必须是DEFINE类型");if (0 == expressions[0].VariableName.size())CException::Throw(__FILE__, __LINE__, source, source_start, "变量定义语句表达式必须有变量名");break;case EXPRESSION:if (expressions.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "简单语句表达式只能有一个");if (sentences.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "简单语句不能有子语句");break;case BLOCK:if (expressions.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "块语句不能有表达式");break;case RETURN:if (expressions.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "return语句表达式只能有一个");if (sentences.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "return语句不能有子语句");break;case IF:if (expressions.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "if语句表达式只能有一个");if (sentences.size() != 1 && sentences.size() != 2)CException::Throw(__FILE__, __LINE__, source, source_start, "if语句子语句只能有1或2个");if (expressions[0].result_type != Variable::LONG && expressions[0].result_type != Variable::DOUBLE)CException::Throw(__FILE__, __LINE__, source, source_start, "if语句判断表达式必须是数值类型");break;case DO:if (expressions.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "do语句表达式只能有一个");if (sentences.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "do语句子语句只能有1个");if (expressions[0].result_type != Variable::LONG && expressions[0].result_type != Variable::DOUBLE)CException::Throw(__FILE__, __LINE__, source, source_start, "do语句判断表达式必须是数值类型");break;case WHILE:if (expressions.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "while语句表达式只能有一个");if (sentences.size() != 1)CException::Throw(__FILE__, __LINE__, source, source_start, "while语句子语句只能有1个");if (expressions[0].result_type != Variable::LONG && expressions[0].result_type != Variable::DOUBLE)CSException::Throw(__FILE__, __LINE__, source, source_start, "while语句判断表达式必须是数值类型");break;case FOR:if (expressions.size() != 2)CException::Throw(__FILE__, __LINE__, source, source_start, "for语句表达式必须有2个");if (sentences.size() != 2)CException::Throw(__FILE__, __LINE__, source, source_start, "for语句子语句必须有2个");if (expressions[1].result_type != Variable::LONG && expressions[1].result_type != Variable::DOUBLE)CException::Throw(__FILE__, __LINE__, source, source_start, "for语句判断表达式必须是数值类型");break;case BREAK:if (expressions.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "break语句不能有表达式");if (sentences.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "break语句不能有子语句");break;case CONTINUE:if (expressions.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "break语句不能有表达式");if (sentences.size() != 0)CException::Throw(__FILE__, __LINE__, source, source_start, "break语句不能有子语句");break;default:CException::Throw(__FILE__, __LINE__, source, source_start, "暂不支持");return false;break;}if (BLOCK == type)vars.PushLevel();for (i = 0; i < sentences.size(); ++i){if (BLOCK != type)vars.PushLevel();//每个子语句都是一个层次,if有1或2个子语句,do\while\for有1个子语句(for的第一个语句已经提前检查)if ((FOR == type && 0 != i) || FOR != type)if (!sentences[i].CheckSentence(source, vars, inloop))return false;if (!inloop){if (BREAK == sentences[i].type)CException::Throw(__FILE__, __LINE__, source, source_start, "非期待的break语句");if (CONTINUE == sentences[i].type)CException::Throw(__FILE__, __LINE__, source, source_start, "非期待的continue语句");}if (BLOCK != type)vars.PopLevel();}if (BLOCK == type)vars.PopLevel();if (FOR == type)vars.PopLevel();return true;}
基本上就是针对每种类型检查特定的要求,然后递归对每个表达式和子语句做检查。检查每种类型对应的表达式和语句是否存在,检查break和continue是否出现在了非循环结构中。
部分语句类型导致新的变量层级,因此要在开始时调用vars.PushLevel()并在最后调用vars.PopLevel()。 PopLevel()将丢弃变量表的最后一级。
for循环的整体是一个层级,循环体又是一个层级。块语句整体是一个层级。
客观地讲,语句比表达式简单多了。
三、ExecSentence
执行语句:
//若为return语句则设置bFinishbool ExecSentence(CScript const& script, T_VARIABLE_S& vars, bool& bFinish, bool& bBreak, bool& bContinue, Variable& ret, void* pe)const{size_t i;if (bFinish)return true;if (bBreak)return true;if (bContinue)return true;ret.type = Variable::NULLVARIABLE;switch (type){case NULLSENTENCE:ret.type = Variable::NULLVARIABLE;break;case DECLARE:expressions[0].ExecExpression(script, vars, ret, pe);break;case EXPRESSION:expressions[0].ExecExpression(script, vars, ret, pe);break;case BLOCK:vars.PushLevel();for (i = 0; i < sentences.size(); ++i){sentences[i].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);if (bFinish)break;if (bBreak)break;if (bContinue)break;}vars.PopLevel();break;case RETURN:expressions[0].ExecExpression(script, vars, ret, pe);bFinish = true;break;case IF:expressions[0].ExecExpression(script, vars, ret, pe);vars.PushLevel();if (ret.GetBool()){sentences[0].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);}else{if (sentences.size() >= 2)sentences[1].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);else ret.type = Variable::NULLVARIABLE;}vars.PopLevel();break;case FOR:vars.PushLevel();sentences[0].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);while (true){expressions[0].ExecExpression(script, vars, ret, pe);if (!ret.GetBool())break;vars.PushLevel();sentences[1].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);vars.PopLevel();if (bFinish)break;if (bBreak){bBreak = false;break;}if (bContinue)bContinue = false;expressions[1].ExecExpression(script, vars, ret, pe);}vars.PopLevel();//ret.type=Variable::NULLVARIABLE;break;case DO:while (true){vars.PushLevel();sentences[0].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);vars.PopLevel();if (bFinish)break;if (bBreak){bBreak = false;break;}if (bContinue)bContinue = false;expressions[0].ExecExpression(script, vars, ret, pe);if (!ret.GetBool())break;}//ret.type=Variable::NULLVARIABLE;break;case WHILE:while (true){expressions[0].ExecExpression(script, vars, ret, pe);if (!ret.GetBool())break;vars.PushLevel();sentences[0].ExecSentence(script, vars, bFinish, bBreak, bContinue, ret, pe);vars.PopLevel();if (bFinish)break;if (bBreak){bBreak = false;break;}if (bContinue)bContinue = false;}//ret.type=Variable::NULLVARIABLE;break;case BREAK:bBreak = true;break;case CONTINUE:bContinue = true;break;default:script.ThrowN(__FILE__, __LINE__, "暂不支持的语句类型", type);return false;break;}return true;}
第一个参数script仅仅是用来在出错时报错。其余参数提供当前变量表并返回程序流程状态。语句没有返回值,这是语句和表达式的关键区别——尽管一个语句可能仅仅包含一个表达式。
执行和检查以同样的规则处理变量层级(变量生存期)。
(这里是结束,但不是整个系列的结束)
相关文章:
编程实战:类C语法的编译型脚本解释器(七)语句
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 系列入口: 编程实…...
实体-联系图
为了把用户的数据要求清楚、准确地描述出来,系统分析员通常建立一个概念性的数据模型(也称为信息模型)。概念性数据模型是一种面向问题的数据模型,是按照用户的观点对数据建立的模型。它描述了从用户角度看到的数据,它反映了用户的现实环境, 而且与在软件系统中的实现方法无关。…...
ROCm上来自Transformers的双向编码器表示(BERT)
14.8. 来自Transformers的双向编码器表示(BERT) — 动手学深度学习 2.0.0 documentation (d2l.ai) 代码 import torch from torch import nn from d2l import torch as d2l#save def get_tokens_and_segments(tokens_a, tokens_bNone):""&qu…...
期权课程之第一节【用生活的例子解释什么是期权】
1、用生活的例子解释什么是期权 期权的英文名也就叫Option【选择】,实际上期权本质也就是一种选择权。 买入资产的例子 假如你【买家】看上了一套老王的【卖家】房子,现价100W、但是目前手头比较紧、但是你又不想错过这个房子,你可以先给老…...
【YOLOv10训练教程】如何使用YOLOv10训练自己的数据集并且推理使用
《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…...
[windows系统安装/重装系统][step-4][番外篇-2]N卡驱动重装 |解决:开机几小时后电脑卡顿 | 后台自动运行了上千个Rundll32进程问题
现象 开机几小时后,电脑变卡,打开后台管理器都卡,后台管理去转圈圈一小会儿后看到后台进程上千个,好多个Rundll32进程 重启下运行会稍快 重启后运行快,后台管理器反应也快 打开后台管理器不卡(几小时后打…...
Redis开发实战
单机部署安装 服务端下载,安装,启动去官网下载最新的版本:http://redis.io/download ,这里用的是3.0.2解压后,进入解压好的文件夹redis的安装非常简单,因为已经有现成的Makefile文件,所以直接先…...
C++ | Leetcode C++题解之第112题路径总和
题目: 题解: class Solution { public:bool hasPathSum(TreeNode *root, int sum) {if (root nullptr) {return false;}if (root->left nullptr && root->right nullptr) {return sum root->val;}return hasPathSum(root->left…...
leetcode力扣 2024. 考试的最大困扰度
一位老师正在出一场由 n 道判断题构成的考试,每道题的答案为 true (用 ‘T’ 表示)或者 false (用 ‘F’ 表示)。老师想增加学生对自己做出答案的不确定性,方法是最大化有连续相同结果的题数。(…...
lvgl无法显示中文
环境: VS2019、LVGL8.3 问题: VS2019默认编码为GB2312, 解决: VS2022设置编码方式为utf-8的三种方式_vs utf8-CSDN博客 我用的方法2,设置为 utf-8无签名就行。...
读书笔记-Java并发编程的艺术-第1章 并发编程的挑战
文章目录 1.1 上下文切换1.1.1 多线程一定快吗1.1.2 如何减少上下文切换 1.2 死锁1.3 资源限制的挑战 1.1 上下文切换 即时是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给多个线程的时间,因为时间…...
RUST 和 GO 如何管理它们的内存
100编程书屋_孔夫子旧书网 Go 中的内存管理 Go 中的内存不会在缓存键被驱逐时立即释放。 相反,垃圾收集器会经常运行以发现任何没有引用的内存并释放它。 换句话说,内存会一直挂起,直到垃圾收集器可以评估它是否真正不再使用,而…...
对于高速信号完整性,一块聊聊啊(12)
常见的无源电子器件 电子系统中的无源器件可以按照所担当的电路功能分为电路类器件、连接类器件。 A、电路类器件: (1)二极管(diode) (2)电阻器(resistor) …...
C++学习笔记(19)——模板
目录 模板参数与非类型模板参数 模板参数 类型模板参数——传递类型 非类型模板参数——传递数量 C11希望array替代静态数组,但实际上vector包揽了一切 模板总结 优点: 缺点: 模板特化:针对某些类型进行特殊化处理 特化…...
java8新特性——函数式编程详解
目录 一 概述1.1 背景1.2 函数式编程的意义1.3 函数式编程的发展 Lambda表达式1.1 介绍1.2 使用Lambda的好处1.3 Lambda方法1.3.1 Lambda表达式结构1.3.2 Lambda表达式的特征 1.4 Lambda的使用1.4.1 定义函数式接口1.4.2 Lambda表达式实现函数式接口1.4.3 简化Lambda表达式1.4.…...
mybatis-plus小课堂: apply 拼接 in SQL,来查询从表某个范围内的数据
文章目录 引言I mybatis-Plus 之 apply 拼接 in SQL1.1 apply源码实现1.2 apply 拼接 in SQL : 非字符串数组1.3 apply 拼接 in SQL : 字符串数组II 如果in的数量太多,采用子查询。III 常见问题: Cause: comColumn xxx in where clause is ambiguoussee also引言 I mybati…...
民宿推荐系统-手把手调试搭建
民宿推荐系统-手把手调试搭建 民宿推荐系统-手把手调试搭建...
线性回归模型
目录 1.概述 2.线性回归模型的定义 3.线性回归模型的优缺点 4.线性回归模型的应用场景 5.线性回归模型的未来展望 6.小结 1.概述 线性回归是一种广泛应用于统计学和机器学习的技术,用于研究两个或多个变量之间的线性关系。在本文中,我们将深入探讨…...
西门子全球业务调整:数十亿欧元交易额,开启新篇章
导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。 在风起云涌的全球经济舞台上,西门子&am…...
AI遇上遥感,未来会怎样?
随着航空、航天、近地空间等多个遥感平台的不断发展,近年来遥感技术突飞猛进。由此,遥感数据的空间、时间、光谱分辨率不断提高,数据量也大幅增长,使其越来越具有大数据特征。对于相关研究而言,遥感大数据的出现为其提…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
