C++模板”>>”编译问题与词法消歧设计
在编译理论中,通常将编译过程抽象为5个主要阶段:词法分析(Lexical Analysis),语法分析(Parsing),语义分析(Semantic Analysis),优化(Optimization),代码生成(Code Generation)。这5个阶段类似Unix管道模型,上一个阶段的输出作为下一个阶段的输入。其中,词法分析是根据输入源代码文本流,分割出词,识别类别,产生词法元素(Token)流,如:
|
1
|
int a = 10; |
经过词法分析会得到[(Type, “int”), (Identifier, “a”), (AssignOperator, “=”), (IntLiteral, 10)],在后续的语法分析阶段,就会根据这些词法元素匹配相应的语法规则。在我学习编译原理时,教科书中对于词法分析的介绍主要是基于正则表达式的,言下之意就是普通语言的词法规则是可以通过正则表达式描述的。比如,C语言的变量名规则是“包含字母、数字或下划线,并且以字母或下划线开头”,这就可以用正则表达式[a-zA-Z_][a-zA-Z0-9_]*表达。但是,在实践中我发现不管是主流语言,还是自己设计的DSL都大量存在不能简单通过正则表达式进行词法分析的例子。来看C++98的模版例子:
|
1
|
map<int, vector<int>> |
上面这段代码会被C++98编译器中报语法错误,原因在于它把“>>”识别成了位右移运算符而不是两个模版右括号,在C++98中必须在两个括号中间加空格,写成
|
1
|
map<int, vector<int> > |
除此了C++模版,据我所知,经典的FORTRAN语言的语法规则更是大量存在词法歧义。
我认为从本质上讲,这类问题的根源在于词法分析的依据只是简单的词法规则,并不具备所有的语法信息,而词法歧义必须提升一层在语法规则中消除。所以,在我自己设计一些DSL的时候干脆就把词法分析和语法分析合二为一了,相当于让语法分析在字符层次上去进行,而不是经典的词法元素层次上,这就是所谓的Scannerless Parsing。采用这种方法的例子并不少见,TeX, Wiki, Makefile和Perl 6等语言的语法分析器都属此类。
Scannerless Parsing方法弥补了词法规则无法消歧的问题,但是同时也破坏了词法和语法分析简单清晰的管道结构,总体上增加了实现和理解的复杂度。另外,像C++这样大型的语言,如果开始是有词法分析的,稍微碰到一个歧义就整个转成Scannerless Parsing未免也显得太夸张了。这个问题困扰了我很久,直到最近才找到了一个满意的解决方案。还是以上面”>>”为例,我们知道现在C++11已经允许不加空格了,那么C++11编译器是如何处理这个词法歧义的呢?答案是:词法分析阶段既然分析不好”>>”,干脆就不分析了,直接把”>” “>”交给语法分析器来分析,其他没有词法歧义的照旧。当我知道这个方案的时候不由得感叹:妙!理论上,词法分析是可以什么也不做的,全部把字符一一交给语法分析器也没有问题,所以,干脆让词法分析只做有把握的部分,解决不了的交给语法分析器,这样就既保留了管道结构,又解决了词法歧义。
下面我们再来看看C++11规范关于这个问题的定义:
14.2 Names of template specializations [temp.names] ###
After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator. When parsing a template-argument-list, the first non-nested > is taken as the ending delimiter rather than a greater-than operator. Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens, the first of which is taken as the end of the template-argument-list and completes the template-id. [ Note: The second > token produced by this replacement rule may terminate an enclosing template-id construct or it may be part of a different construct (e.g. a cast).—end note ]
可见,在C++11中,词法分析器是把”>>”直接当成两个”>”传给了语法分析器,然后在语法分析中如果匹配了template-argument-lis语法,第一个”>”符号会被直接认为是模版结束符,而不是大于,也不是位移符号。根据这个定义,我构造了一个例子:
|
1
2
3
4
5
|
template<int N>class Foo {};Foo<3>>1> foo; |
这个例子在C++98中是能正确编译的,”>>”被解释成了位移运算,但是它反而不能在C++11中编译了,因为根据规范第一个”>”被解释成了模版参数结束符。如果要在C++11中编译,需要显式地加上括号:
|
1
|
Foo<(3>>1)> foo; |
C++模板”>>”编译问题与词法消歧设计的更多相关文章
- 基于TF-IDF值的汉语语义消歧算法
RT,学校课题需要233,没了 话说,窝直接做个链接的集合好了,方便以后查找 特征值提取之 -- TF-IDF值的简单介绍 汉语语义消歧之 -- 句子相似度 汉语语义消歧之 -- 词义消歧简介 c++ ...
- C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型
模板与泛型编程 --模板编译模型 引言: 当编译器看到模板定义的时候,它不马上产生代码.仅仅有在用到模板时,假设调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例. 一般而言,当调 ...
- 学习笔记CB008:词义消歧、有监督、无监督、语义角色标注、信息检索、TF-IDF、隐含语义索引模型
词义消歧,句子.篇章语义理解基础,必须解决.语言都有大量多种含义词汇.词义消歧,可通过机器学习方法解决.词义消歧有监督机器学习分类算法,判断词义所属分类.词义消歧无监督机器学习聚类算法,把词义聚成多类 ...
- vue模板编译
Vue 的模板编译是在 $mount 的过程中进行的,在 $mount 的时候执行了 compile 方法来将 template 里的内容转换成真正的 HTML 代码. complie 最终生成 re ...
- Javascript - 预编译与函数词法作用域
预编译与函数词法作用域(Precompiled & Scoped) 预编译 Javascript脚本的宿主在执行代码之前对脚本做了预编译处理,比如浏览器对Js进行了预编译,编译器会扫描所有的声 ...
- 【转】【Html】Vuejs2.0学习之二(Render函数,createElement,vm.$slots,函数化组件,模板编译,JSX)
1.Render函数 所以直接来到Render,本来也想跳过,发现后面的路由貌似跟它还有点关联.先来看看Render 1.1 官网一开始就看的挺懵的,不知道讲的是啥,动手试了一下,一开头讲的是Rend ...
- smarty 模板编译和变量调节器 模板引入
<?php require './smarty/Smarty.class.php'; $sm = new Smarty; //$sm->force_compile = true; $sm- ...
- es6实现简单模板编译
现在有各种框架,其中一个主要模块就是关于template.最火的vue.react等框架,在这一块上也是是下足了功夫.我也想写一个自己的模板编译工具,所以就做了个简单的实现,主要是使用es6的反引号编 ...
- Vuejs2.0学习之二(Render函数,createElement,vm.$slots,函数化组件,模板编译,JSX)
时隔一周多,因为一些别的事情绊住了,下面接着写.中间这段时间也有看官方文档,发现正如他所说90%的基础内容都一样,所以这里直接跳到我比较关注的东东上,要是想看看哪些不一样,可以参考这个http://v ...
随机推荐
- MySQL常用处理方法
1.replace into replace into t(id, update_time) values(1, now());或 replace into t(id, update_time) se ...
- WinForm 之 VS2010发布、打包安装程序
第一步.在vs2010 打开要打包的应用程序解决方案,右键“ 解决方案 ” → “ 添加 ” → “ 新建项目 ” → “ 其他项目类型 ” → “ 安装和部署 ” → “ Visual Studio ...
- Docker镜像保存save、加载load
(1)查看要要保存的镜像的ID [root@localhost docker]# docker images (2)保存镜像 [root@localhost docker]# docker save ...
- Endorsement 业务逻辑介绍
本文主要介绍保单系统中Endorsement功能的基本逻辑和过程,主要参考OIC系统 保单系统 保险公司用来管理保单的信息系统,这里简称为保单系统.主要作用是收集和维护投保人信息和投保信息,计算保费, ...
- 安装了XAMPP,PHP怎么显示中文
1.输出前加文件头.header("Content-type: text/html; charset=utf-8"); 另外注意你的文件编码要和 charset一致. 2. 可能是 ...
- MySQL事物系列:2:事物的实现
1:事物的隔离性由锁来实现.事物的持久性和事物的原子性通过redo log来实现.事物的一致性通过undo log来实现. redo log恢复提交事物修改的页操作 undo log回滚到行记录某个特 ...
- http 请求报文
1.报文 2.http请求方法 restful接口 post:创建 put:更新
- cocos2d-js 和 createjs 性能对比(HTML5)
cocos2d-js除了做native游戏外,还可以用来做HTML5游戏/动画,那么它跟adobe的createjs框架比较会怎么样呢? (背景知识:createjs是adobe支持的HTML5框架, ...
- Leetcode 240 Search a 2D Matrix II (二分法和分治法解决有序二维数组查找)
1.问题描写叙述 写一个高效的算法.从一个m×n的整数矩阵中查找出给定的值,矩阵具有例如以下特点: 每一行从左到右递增. 每一列从上到下递增. 2. 方法与思路 2.1 二分查找法 依据矩阵的特征非常 ...
- 【AIX】用户、组合安全管理
用户介绍 用户的概述 比如:我们在使用AIX系统的时候,同事需要使用我的计算机,但是我不想让他用我的用户登录,我的用户存在一些特殊信息,只能自己查看,此时就可以建立一个普通用户给他使用即可. 1.单用 ...