编程实战:类C语法的编译型脚本解释器(二)

系列入口:编程实战:类C语法的编译型脚本解释器(系列)-CSDN博客

        现在开始解释所有的设计思想和与源代码。先从外围入手,最后会进入到一个巨大的解析语法的类。

        本文介绍TOKEN和变量。

目录

一、TOKEN

1.1 定义Token类型

1.2 将脚本拆解为Token

1.3 TryGetKeyword识别关键字

1.4 TryGetNumber识别数值

1.5 其余TryGetXXXX略 

二、变量


一、TOKEN

1.1 定义Token类型

        token是编程语言的基本单元,是最小单位,包括分隔符、标识符、操作符、关键字、数字、字符串字面值,不包括空白。在C和类C语法中,空白字符包括换行都会被忽略(但预处理程序并非如此,所以预处理是额外的东西)。

        所有编译程序首先都会把源代码分解成一系列token,本代码也是如此。token的相关定义如下:

	enum { TOKEN_BUF_LEN = 128 };//仅用于预定义的关键字、运算符,其它标识符任意长度//语法标记,去除空白之后的每个元素struct Token{enum types { DELIMITER = 0, OPERATOR, IDENTIFIER, NUMBER, KEYWORD, STRING };types type;//类型string text;//文本size_t pos;//在源代码中的位置Token(types _type, char const* _text, size_t _pos) :type(_type), text(_text), pos(_pos) {}string ToString()const{STATIC_C const char typestr[][TOKEN_BUF_LEN] = { "DELIMITER","OPERATOR","IDENTIFIER","NUMBER","KEYWORD","STRING" };//必须与types对应char buf[TOKEN_BUF_LEN * 2];string ret;sprintf(buf, "%03ld %-12s ", pos, typestr[type]);ret = buf;ret += text.c_str();return ret;}};

        每个Token包含类型和文本(对应源代码中的表现形式),同时为了调试需要,增加了pos记录在脚本中的位置。

1.2 将脚本拆解为Token

        作为编译的第一步,显然是将源代码分解为token。

        这一步由一个类来实现:

class CTokens
{public:vector<Token > m_tokens;//解析出的语法元素bool ToTokens(string& source){string::size_type pos = 0;while (GetToken(source, pos));return true;}
};

        解析出的token保存在m_tokens中,而函数ToTokens()仅仅是循环调用GetToken()解析出一个一个token而已。 

        GetToken()是关键的主控函数:

			bool GetToken(string& source, string::size_type& pos){Token::types type;string token;char c;bool isInComment = false;while (pos < source.size()){c = source[pos];if (isInComment){if ('\n' == c){isInComment = false;}++pos;continue;}if ('/' == c && pos + 1 < source.size() && '/' == source[pos + 1]){isInComment = true;pos += 2;continue;}if (!IsBlank(c))break;++pos;}if (source.size() == pos)return false;if (TryGetKeyword(source.c_str(), pos, token)){type = Token::KEYWORD;}else if (TryGetNumber(source.c_str(), pos, token)){type = Token::NUMBER;}else if (TryGetString(source.c_str(), pos, token)){type = Token::STRING;}else if (TryGetDelimiter(source.c_str(), pos, token)){type = Token::DELIMITER;}else if (TryGetOperator(source.c_str(), pos, token)){type = Token::OPERATOR;}else if (TryGetIdentifier(source.c_str(), pos, token)){type = Token::IDENTIFIER;}else{CmyException::Throw(__FILE__, __LINE__, source.c_str(), pos, "无法识别的符号");return false;}m_tokens.push_back(Token(type, token.c_str(), pos - token.size()));return true;}

        这个函数的流程不复杂,先跳过注释(仅支持单行注释),然后依次尝试每种token,每种尝试如果成功会修改当前位置pos(通过引用参数),如果失败则不会修改pos。

        TryGetXXXX这一组函数每个都不复杂,不过调用顺序有名堂,关键字是最优先的,这就保证关键字不可能被用作变量名。

1.3 TryGetKeyword识别关键字

        这个复杂一些,由几个函数组合而成:

			//headset最后一个必须是空串bool IsStartWith(char const* str, char const (*headset)[TOKEN_BUF_LEN], string& ret)const{long i = 0;ret = "";while (headset[i][0] != '\0'){size_t keylen = strlen(headset[i]);if (0 == strncmp(headset[i], str, keylen)){if (ret.size() < strlen(headset[i]))ret = headset[i];}++i;}return ret.size() != 0;}bool IsKeyword(char const* str, string& key)const{STATIC_C char const buf[][TOKEN_BUF_LEN] = {"asm","default","float","operator","static_cast","union","auto","delete","for","private","struct","unsigned","bool","do","friend","protected","switch","using","break","double","goto","public","template","virtual","case","dynamic_cast","if","register","this","void","catch","else","inline","reinterpret_cast","throw","volatile","char","enum","int","return","true","wchar_t","class","explicit","long","short","try","while","const","export","mutable","signed","typedef","const_cast","extern","namespace","sizeof","typeid","continue","false","new","static","typename","string",""};//必须以空串结尾return IsStartWith(str, buf, key);}bool TryGetKeyword(char const* source, string::size_type& pos, string& ret){string key;string nextkey;size_t keylen;if (IsKeyword(source + pos, key)){keylen = key.size();if ('\0' == source[pos + keylen] || IsBlank(source[pos + keylen]) || IsDelimiter(source[pos + keylen]) || IsOperator(source + pos + keylen, nextkey)){ret = key;pos += keylen;return true;}}return false;}

         规则其实也很简单:以关键字开头并且其后是{空白、分隔符、操作符}则为一个关键字。

1.4 TryGetNumber识别数值

        数值是由数字或点开始的字母数字小数点的串,同时还需要符合一些规则,代码里分两步进行,第一步识别出串,第二步则将串根据各种规则转换为数值:

			//以数字或点开头的串bool TryGetNumber(char const* source, string::size_type& pos, string& ret){ret = "";char c = source[pos];if (c >= '0' && c <= '9' || c == '.' && source[pos + 1] >= '0' && source[pos + 1] <= '9'){while ((c = source[pos]) != '\0'){if (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || '.' == c || '_' == c){}else{break;}ret += c;++pos;}}return ret.size() != 0;}bool NumberToVariable(char const* source, Variable& var){char* endptr;if (IsCharIn('.', source) || IsCharIn('e', source) || IsCharIn('E', source)){var.type = Variable::DOUBLE;var.dValue = strtod(source, &endptr);}else{var.type = Variable::LONG;long prefix = 0;long radix = 10;if (strlen(source) >= 1 && '0' == source[0]){if (strlen(source) >= 2 && ('x' == source[1] || 'X' == source[1])){radix = 16;prefix = 2;}else{radix = 8;prefix = 1;}}var.lValue = strtol(source + prefix, &endptr, radix);}if (strlen(endptr) != 0){if (Variable::DOUBLE == var.type && (0 == stricmp(endptr, "f") || 0 == stricmp(endptr, "l"))|| Variable::LONG == var.type && (0 == stricmp(endptr, "u") || 0 == stricmp(endptr, "l") || 0 == stricmp(endptr, "i64"))){return true;}string str;str = "数值常量格式错误 ";str += endptr;CException::Throw(__FILE__, __LINE__, source, endptr - source, str.c_str());return false;}return true;}

1.5 其余TryGetXXXX略 

二、变量

        我非常痛恨无类型变量,比如JavaScript,所以我在这个脚本里面使用强类型。

        变量类型做了简化,分为long、double和string。通过一个类存储所有的变量,代码如下:

	//变量struct Variable{enum types { NULLVARIABLE = 0, LONG, DOUBLE, STRING };types type;bool isconst;long lValue;double dValue;string strValue;Variable() :type(NULLVARIABLE), isconst(false), lValue(0), dValue(0.) {}bool isNull() { return type == NULLVARIABLE; }bool isNumber() { return type == LONG || type == DOUBLE; }bool isString() { return type == STRING; }void clear(){type = NULLVARIABLE;isconst = false;lValue = 0;dValue = 0;strValue = "";}void initvalue(){lValue = 0;dValue = 0;strValue = "";}Variable& operator = (long v){char buf[256];if (NULLVARIABLE == type)type = LONG;switch (type){case LONG:lValue = v; break;case DOUBLE:dValue = v; break;case STRING:sprintf(buf, "%ld", v);strValue = buf;break;default:break;}return *this;}Variable& operator = (double v){char buf[256];if (NULLVARIABLE == type)type = DOUBLE;switch (type){case LONG:lValue = (long)v; break;case DOUBLE:dValue = v; break;case STRING:gcvt(v, 200, buf);strValue = buf;break;default:break;}return *this;}Variable& operator = (string const& v){if (NULLVARIABLE == type)type = STRING;switch (type){case LONG:lValue = atol(v.c_str()); break;case DOUBLE:dValue = atof(v.c_str()); break;case STRING:strValue = v; break;default:break;}return *this;}Variable& operator = (Variable const& v){if (NULLVARIABLE == type)type = v.type;switch (type){case LONG:lValue = v.GetLong(); break;case DOUBLE:dValue = v.GetDouble(); break;case STRING:strValue = v.GetString(); break;default:break;}return *this;}Variable operator-()const{Variable tmp = *this;switch (type){case LONG:tmp.lValue = -lValue; break;case DOUBLE:tmp.dValue = -dValue; break;default:break;}return tmp;}//eva=true则是为赋值提升,结果以左边为准static types typeUpgrade(types a, types b, bool eva = false){if (NULLVARIABLE == a || NULLVARIABLE == b)return NULLVARIABLE;if (LONG == a && LONG == b)return LONG;if (STRING == a && STRING == b)return STRING;if (DOUBLE == a && DOUBLE == b)return DOUBLE;if (!eva){if (DOUBLE == a && LONG == b)return DOUBLE;if (LONG == a && DOUBLE == b)return DOUBLE;}else{if (DOUBLE == a && LONG == b)return DOUBLE;if (LONG == a && DOUBLE == b)return LONG;}return NULLVARIABLE;}long GetLong()const{string tmp;switch (type){case LONG: return lValue;case DOUBLE: return (long)dValue;case STRING: tmp = strValue; return atol(tmp.c_str());default:return 0;}}double GetDouble()const{string tmp;switch (type){case LONG: return lValue;case DOUBLE: return dValue;case STRING: tmp = strValue; return atof(tmp.c_str());default:return 0.;}}bool GetBool()const{switch (type){case LONG: return 0 != lValue;case DOUBLE: return 0 != dValue;default:return false;}}string GetString()const{char buf[256];switch (type){case LONG: sprintf(buf, "%ld", lValue); return buf;case DOUBLE: gcvt(dValue, 200, buf); return buf;case STRING: return strValue;default:return "";}}Variable operator+(Variable const& b)const{Variable tmp = *this;tmp.type = typeUpgrade(type, b.type);switch (tmp.type){case LONG:tmp.lValue = GetLong() + b.GetLong(); break;case DOUBLE:tmp.dValue = GetDouble() + b.GetDouble(); break;case STRING:tmp.strValue = GetString() + b.GetString(); break;default:break;}return tmp;}Variable operator-(Variable const& b)const{Variable tmp = *this;tmp.type = typeUpgrade(type, b.type);switch (tmp.type){case LONG:tmp.lValue = GetLong() - b.GetLong(); break;case DOUBLE:tmp.dValue = GetDouble() - b.GetDouble(); break;default:break;}return tmp;}Variable operator*(Variable const& b)const{Variable tmp = *this;tmp.type = typeUpgrade(type, b.type);switch (tmp.type){case LONG:tmp.lValue = GetLong() * b.GetLong(); break;case DOUBLE:tmp.dValue = GetDouble() * b.GetDouble(); break;default:break;}return tmp;}Variable operator/(Variable const& b)const{Variable tmp = *this;tmp.type = typeUpgrade(type, b.type);switch (tmp.type){case LONG:{if (0 == b.GetLong())throw "div zero";tmp.lValue = GetLong() / b.GetLong(); break;}case DOUBLE:{if (0 == b.GetDouble())throw "div zero";tmp.dValue = GetDouble() / b.GetDouble(); break;}default:break;}return tmp;}Variable operator%(Variable const& b)const{Variable tmp = *this;tmp.type = typeUpgrade(type, b.type);switch (tmp.type){case LONG:{if (0 == b.GetLong())throw "mod zero";tmp.lValue = GetLong() % b.GetLong(); break;}default:break;}return tmp;}Variable operator>(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() > b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() > b.GetDouble(); break;case STRING:tmp.lValue = GetString() > b.GetString(); break;default:break;}return tmp;}Variable operator<(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() < b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() < b.GetDouble(); break;case STRING:tmp.lValue = GetString() < b.GetString(); break;default:break;}return tmp;}Variable operator>=(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() >= b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() >= b.GetDouble(); break;case STRING:tmp.lValue = GetString() >= b.GetString(); break;default:break;}return tmp;}Variable operator<=(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() <= b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() <= b.GetDouble(); break;case STRING:tmp.lValue = GetString() <= b.GetString(); break;default:break;}return tmp;}Variable operator==(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() == b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() == b.GetDouble(); break;case STRING:tmp.lValue = GetString() == b.GetString(); break;default:break;}return tmp;}Variable operator!=(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() != b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() != b.GetDouble(); break;case STRING:tmp.lValue = GetString() != b.GetString(); break;default:break;}return tmp;}Variable operator&&(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() && b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() && b.GetDouble(); break;default:break;}return tmp;}Variable operator||(Variable const& b)const{Variable tmp = *this;tmp.type = LONG;switch (typeUpgrade(type, b.type)){case LONG:tmp.lValue = GetLong() || b.GetLong(); break;case DOUBLE:tmp.lValue = GetDouble() || b.GetDouble(); break;default:break;}return tmp;}static char const* TypeStr(types type){STATIC_C const char typestr[][TOKEN_BUF_LEN] = { "NULLVARIABLE","LONG","DOUBLE","STRING" };//必须与types对应if(type>=0 && type<4)return typestr[type];else{static char buf[256];sprintf(buf, "错误的类型 %d", type);//cout << buf << endl; exit(0);return buf;}}string ToString(long level = 0)const{string ret;char buf[256];string prefix;prefix.assign(level * 4, ' ');switch (type){case LONG:sprintf(buf, "%ld", lValue); break;case DOUBLE:gcvt(dValue, 200, buf); break;case STRING:strcpy(buf, strValue.c_str()); break;default:sprintf(buf, "NULL"); break;}ret = prefix + " ";ret += (isconst ? "常量" : "变量");ret += "类型 ";ret += TypeStr(type);ret += " : ";ret += buf;return ret;}};

        没有使用union,直接用类型和三个变量来存储,空间当然是有浪费的,但是没人知道啊。

        重载了各种类型的相互操作,都很简单,只是繁琐。

        成员变量:

类型变量名功能
enum typestype实际存储的类型
boolisconst是否是常量,常量不允许修改
longlValuetype为LONG时使用
doubledValuetype为DOUBLE时使用
stringstrValuetype为STRING时使用

(待续)

(这里是结束)

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

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

相关文章

opencv学习三:保存图片

文章目录 三、保存图片&#xff08;一&#xff09;imwrite()保存图片&#xff08;二&#xff09;代码 三、保存图片 &#xff08;一&#xff09;imwrite()保存图片 retval cv2.imwrite(filename,img,params)filename的数据类型是const String&&#xff0c;这里要填入的参…

组件化编程

hello&#xff0c;我是小索奇&#xff0c;精心制作的Vue系列持续发放&#xff0c;涵盖大量的经验和示例&#xff0c;如果对您有用&#xff0c;可以点赞收藏哈~ 组件化编程 组件是什么&#xff1f; 一句话概括就是&#xff1a;实现特定功能的模块化代码单元 vm就是大哥&#xff…

flink源码分析之功能组件(四)-slot管理组件II

简介 本系列是flink源码分析的第二个系列&#xff0c;上一个《flink源码分析之集群与资源》分析集群与资源&#xff0c;本系列分析功能组件&#xff0c;kubeclient&#xff0c;rpc&#xff0c;心跳&#xff0c;高可用&#xff0c;slotpool&#xff0c;rest&#xff0c;metrics&…

各种外部排序的总结

多路归并 败者树 置换选择排序 最佳归并树

linux进程优先级_nice

4.1.3.4 进程优先级&#xff1a;nice nice以更改过的优先序来执行程序&#xff0c;如果未指定程序&#xff0c;则会印出目前的排程优先序&#xff0c;内定的 adjustment 为 10&#xff0c;范围为 -20&#xff08;最高优先序&#xff09;到 19&#xff08;最低优先序&#xff0…

认识K线形态,把握买入卖出时机

一、认识K线 1、K线的含义 股票一天之内有4个最关键的价格&#xff0c;开盘价、收盘价、最高价和最低价&#xff0c;把这个价格显示在图上就是K线图。 以金斗云智投电脑版为例&#xff0c;打开软件&#xff0c;任意搜索一支个股&#xff0c;就可以看到这支股票的K线。 股市新…

数据链路层之网桥

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

【MQ核心概念理解】

文章目录 一、MQ介绍1、什么是MQ&#xff1f;为什么要用MQ&#xff1f;中间服务要保证这些系统可以正常工作&#xff0c;应该要有哪些特性呢&#xff1f;2、MQ的优缺点 一、MQ介绍 1、什么是MQ&#xff1f;为什么要用MQ&#xff1f; ChatGPT中对于消息队列的介绍是这样的&…

更改AndroidStudio模拟器位置

C盘何等的珍贵&#xff0c;可是好多工具&#xff0c;软件非得默认安装在C盘。。导致C盘越来越紧张。。 在日常使用过程中&#xff0c;安装任何软件都会将其安装到非系统盘下&#xff0c;Android模拟器也不能例外。保护好C盘也是日常一个良好的习惯。 Android AVD默认路径&…

ES6知识点

ES6 知识点及常考面试题 var、let 及 const 区别 涉及面试题&#xff1a;什么是提升&#xff1f;什么是暂时性死区&#xff1f;var、let 及 const 区别&#xff1f;对于这个问题&#xff0c;我们应该先来了解提升&#xff08;hoisting&#xff09;这个概念。 console.log(a)…

Android 13 - Media框架(24)- MediaCodecList

这一节我们要了解 MediaCodecList 中的信息是如何加载的&#xff0c;以及这些信息是如何使用到的。 // static sp<IMediaCodecList> MediaCodecList::getLocalInstance() {Mutex::Autolock autoLock(sInitMutex);if (sCodecList nullptr) {MediaCodecList *codecList n…

深入理解Servlet(下)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 在这一篇文章里&#x…

hive里如何高效生成唯一ID

常见的方式&#xff1a; hive里最常用的方式生成唯一id&#xff0c;就是直接使用 row_number() 来进行&#xff0c;这个对于小数据量是ok的&#xff0c;但是当数据量大的时候会导致&#xff0c;数据倾斜&#xff0c;因为最后生成全局唯一id的时候&#xff0c;这个任务是放在一个…

工业机器视觉megauging(向光有光)使用说明书(十二,轻量级的visionpro)

关于最后一个工具的介绍&#xff1a;就是这个“相机图像” 我们可以鼠标双击点进去看一看&#xff1a; 在图像上点击&#xff0c;就可以截取一块图像&#xff0c;是可以放大缩小的&#xff0c;这个放大很low&#xff0c;是我以前研究缩放入门时的版本&#xff0c;本想删除&…

SELinux refpolicy详解(8)

接前一篇文章&#xff1a;SELinux refpolicy详解&#xff08;7&#xff09; 三、refpolicy内容详解 上一回&#xff08;上几回&#xff09;讲解完了refpolicy源码根目录下的README文件。本回继续讲解源码根目录下的其它文件。先来讲解在README文件中提到的build.conf文件。 2…

网站优化如何做?SEO怎么做?

网站刚上线肯定没有收录&#xff0c;没有排名&#xff0c;这是一个积累内容的阶段。这个阶段的seo优化该怎么做呢&#xff1f;小马识途营销顾问建议从以下几方面展开。 一、网站代码优化 1、设置Robot.txt文件&#xff0c;告诉搜索引擎哪些目录文件可以抓取&#xff0c;哪些目录…

机器学习笔记 - 异常检测之OneClass SVM算法简述

一、异常检测是什么? 如下图,理想中我们可以找到一个框住大部分正常样本的决策边界,而在边界外部的数据点(蓝点)即视为异常。 但实际情况下数据都没有标签,因此很难定义正常还是不正常。异常检测的主要挑战如下:正常与异常行为之间的界限往往并不明确、不同的应…

数字媒体技术基础之:常见字体类型

字体 Font在数字设计和排版中起着至关重要的作用&#xff0c;不同的字体类型为文本呈现和创意表达提供了丰富多样的可能性。 .fon 字体 .fon 文件是 Windows 早期系统中使用的一种字体文件格式。 特点&#xff1a; 1、基于像素的位图字体。 2、不支持无损缩放&#xff0c;主要用…

web前端之TypeScript

MENU typescript类型别名、限制值的大小typescript使用class关键字定义一个类、static、readonlytypescript中class的constructor(构造函数)typescript中abstractClass(抽象类)、extends、abstracttypescript中的接口、type、interfacetypescript封装属性、public、private、pr…

LeetCode刷题笔记第80题:删除有序数组中的重复项 II

LeetCode刷题笔记第80题&#xff1a;删除有序数组中的重复项 II 题目&#xff1a; 删除升序数组中超过两次的元素后的数组长度 想法&#xff1a; 使用快慢指针的方法完成&#xff0c;使用快指针遍历整个数组&#xff0c;使用慢指针完成相同元素最多保留两个。在快指针遍历到…