上星期写完词法分析器的时候,曾遇上一个无关紧要却X疼的问题。毕竟是第一次完整地写整个语言的编译器(暂且这么叫着吧,解释器更靠谱),由于经验不足,在字符串解析这一块驻足了两天才解决掉,这里记录下来供以后参考。哦对了,之所以想自己手写词法分析器,并不是我不知道有自动工具可以自动生成,而是我不会用,嗯,果然高冷。

词法分析器的作用简而言之就是将语言分割成一个一个独立的词法单元(单词),并赋予一定的类型。(如果不了解其作用,建议参考词法分析)

例如:

a = 3 ;

我们就可以将其分课程一个个有意义的单元,并赋予类型:

<=,NE>

<3,NUM>

<;,SEMI>

和就是分割好的单词序列。在一般情况下,一门语言的词法分析器总会遇上要分析字符串成分的情况,比如表达式

val = "xxx" 或者 val = 'xxx'

我并未参考其他语言是如何分析,仅凭猜测,自认为应该解析成如下形式:

<=,NE>

<'/",SINGLEQUOTE/DOUBLEQUOTE>

当然也许这不是一个好的类型单词分配方法,但起码不会是一个错误方法吧,到现在,语法分析阶段工作的还挺好的,也许会有更好的办法,还需要多参考前辈们的。

这个时候问题就是,当遇上这样的句子时:

a = "xxxxxxx" b="ssssssss"

因为在一个SQL语句中不可能一辈子咱们就出现一个字符串单元,所以这个怎么解析当时很费了点脑筋。如果没注意,就会解析成

<=,NE>

<",DOUBLE_QUOTE>

<",DOUBLE_QUOTE>

<",DOUBLE_QUOTE>

<",DOUBLE_QUOTE>

如果不加以控制,这显然是错的,因为b=在这里显然是两个单词,而不是STRING。于是我开始寻觅各种办法解决这个问题:第一个想到的是用bool类型来控制,判断引号出现的单复数,如果是true则为复数,即收尾的符号,这样这个问题就解决了。但是,脑脑子里思考问题的解决方案永远是奔向理想目标中的其中一条道路,很多岔路是动手的时候出现的,于是真的出现问题了,参看下面伪代码,其中flag1与2分别代表单引号与双引号的判断flag。

  1. get_next_token()
  2. {
  3. while (p != val.size())
  4. {
  5. if (flag)
  6. {
  7. std::string v=get_string();
  8. continue;
  9. }
  10. switch (c)
  11. {
  12. case '\'':
  13. if (flag==true)//the begin quote
  14. {
  15. flag = false;
  16. }
  17. else if (flag==false)
  18. {
  19. flag = true;
  20. }
  21. consume();
  22. break;
  23. default:
  24. consume();
  25. }
  26. }
  27. }

switch语句内的代码逻辑是没有任何问题的,问题出在,取字符串单词的判断上:当下一个单词是字符串时,取出之后便会执行continue,这时flag 是无法被改变状态的,所以当下一次取单词进入函数时又会进入开始的if逻辑,当时我在这种解决方法上进行了很多次的修改,均告失败,问题重重,于是只得另寻方案,每当这种时候都恨自己脑瓜不够机灵,想不到优雅的办法解决这种问题,当然了也许是条件限制,导致自己没法往优雅的解决方法上想,:p,我倾向于后者。后来也试过用用计数器的方式,也是失败了,掰着掰着就醉了,好一个,众人皆醒我独醉,醉完媳妇旁撅着睡。(诶?!我不是在寝室吗?)

好在把各种烂方法使了过后,想到了一个最终解决方法,使用了一个栈,当栈里保存着有引号的时候(当前符号落到引号上时在switch内的每个case压栈,如代码所示。),说明这一轮要取的单词属于字符串,当当前字符又落到引号上时,判断栈里是否有引号,如果有,则说明是收尾引号,这时清空栈。

  1. //由于get_token函数过长,此处仅贴上部分片段
  2. if (!quote_stack.empty())//string_identifier.first stores the quotes
  3. {
  4. if (quote_stack.top() == c)
  5. {
  6. consume();
  7. char temp = quote_stack.top();
  8. quote_stack.pop();
  9. if (temp=='\'')
  10. {
  11. return token(tag::SINGLEQUOTE, "'");
  12. }
  13. return token(tag::DOUBLEQUOTE, "\"");
  14. }
  15. else
  16. {
  17. std::string id = STRINGS_WITH_TERMINATION(quote_stack.top()).c_str();
  18. token tk(tag::STRING, id.c_str());
  19. if (!id.empty())
  20. {
  21. return token(tk);
  22. }
  23. }
  24. }
  25.  
  26. //switch内部:
  27. case '\'':
  28. consume();
  29. quote_stack.push('\'');
  30. return token(tag::SINGLEQUOTE, "'");
  31. case '"':
  32. consume();
  33. quote_stack.push('"');
  34. return token(tag::DOUBLEQUOTE, "\"");

这个方法目前运行良好,由于任务的特殊性,栈内最多会容纳两个字符,由于stack内部由deque实现(C++ STL),空间上多少浪费了一点,不过这个方法将任务简化,并且也挺好理解,同时相比flag的方法,flag更容易有在其他函数中无意赋值导致全局变量污染问题的风险。当然了,您可以将其替换为一个两个字节的数组,抽象成一个类来解决,我这里暂时先不做优化。

其实这也只是一个权宜之计,我相信一定有优雅且更加高效的设计或者方法,期待可以学到。

jcSQL词法分析器对字符串token的解析的更多相关文章

  1. QT json字符串生成和解析

    1         QT json字符串生成和解析 1.1  QT Json解析流程 (1)  字符串转化为QJsonDocument QJsonParseError json_error; QJso ...

  2. redis之字符串命令源代码解析(二)

    形象化设计模式实战             HELLO!架构                     redis命令源代码解析 在redis之字符串命令源代码解析(一)中讲了get的简单实现,并没有对 ...

  3. Java字符串常量池及字符串判等解析

    一.理解"=="的含义 "=="常用于两个对象的判等操作,在Java中,"=="主要有以下两种用法: 1.基础数据类型:比较的是他们的值是否 ...

  4. jwt认证生成后的token后端解析

    一.首先前端发送token token所在的位置headers {'authorization':token的值',Content-Type':application/json} 在ajax写 //只 ...

  5. Python 生成 JWT(json web token) 及 解析方式

    一.关于 jwt 的原理及概念可以自行在网络上搜索了解一下,这里推荐一篇写的比较好的博客 深入了解Json Web Token之概念篇 另附 JWT 的官方文档: https://jwt.io/int ...

  6. C语言之字符串典型例题解析

    今天又遇见几个好题,和以前的一些凑一块写一篇文章,作为我延迟去自习室的一个借口吧. 首先是第一题 int fun(char* s){ char* t = s; while(*t++); return ...

  7. 各自平台token获取解析及用户信息的获取

    1.auth根据手机号码获取auth平台session_token记统一认证的user_id与pass_id [dwliuchao1@GD-QHD-CNG152TFKX-12.55 logs]$ cd ...

  8. JSON字符串 拼接与解析

    常用方式: json字符串拼接(目前使用过两种方式): 1.运用StringBuilder拼接 StringBuilder json = new StringBuilder(); json.appen ...

  9. 后台给前台传JSON字符串,前台解析并拼接显示

    后台传JSON public class CourseType : IHttpHandler { Epoint.PeiXun.Bizlogic.BLL.CourseLibrary.PX_CourseT ...

随机推荐

  1. XML新手入门 创建构造良好的XML(1)

    XML新手入门 创建构造良好的XML(1) 2009-03-19 09:24 Kay Whatley IBM Developerworks 我要评论(0) 字号:T | T 本文描述了构建良好的XML ...

  2. 今天在研究jquery用ajax提交form表单中得数据时,学习到了一种新的提交方式

    今天在研究jquery用ajax提交form表单中得数据时,学习到了一种新的提交方式 jquery中的serialize() 方法 该方法通过序列化表单值,创建 URL 编码文本字符串 序列化的值可在 ...

  3. ADO.NET帮助类DBHelper

    一. DBHelper帮助类 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  4. opencv菜鸟学习之旅cvNorm

    Norm 计算数组的绝对范数, 绝对差分范数或者相对差分范数 double cvNorm( const CvArr* arr1, const CvArr* arr2=NULL, int norm_ty ...

  5. NDEF-NFC数据交换格式

    为实现NFC标签.NFC设备以及NFC设备之间的交互通信,NFC论坛(NFC FROUM)定义了称为NFC数据交换格式(NDEF)的通用数据格式.NDEF是轻量级的紧凑的二进制格式,可带有URL,vC ...

  6. jquery 单击table行事件和radio的选中事件冲突

    原文地址:http://zhidao.baidu.com/link?url=HER7lu4jqejWUhWQO2nq6LZ6tf7vyhPZRADSL-xaBQSF4P4yftD9vg08Ss8HF- ...

  7. Wikioi 1294 全排列

    先给出链接地址:Wikioi 1294 虽然题目很短,论难度也就是个深搜,算法方面我就不多说了,而且我知道c++有个函数叫next_permutation,谁用谁知道. 代码如下: #include& ...

  8. 把DEDE的在线文本编辑器换成Kindeditor不显示问题

    在织梦论坛下载了[Kindeditor编辑器For DedeCMS],按照操作说明安装后,后台文章编辑的区域却显示空白,有人说不兼容V57版本,有人说不兼容gbk版本,我也纠结了很久,在网上找了很多版 ...

  9. Hadoop 3、Hadoop 分布式存储系统 HDFS

    HDFS是Hadoop Distribute File System 的简称,也就是Hadoop的一个分布式文件系统. 一.HDFS的优缺点 1.HDFS优点: a.高容错性 .数据保存多个副本 .数 ...

  10. CodeForces 132C Logo Turtle (记忆化搜索)

    Description A lot of people associate Logo programming language with turtle graphics. In this case t ...