如何编译JS代码的及理解java 抽象语法树树

 上传我的文档
 下载
 收藏
粉丝量:137
该文档贡献者很忙,什么也没留下。
 下载此文档
规范抽象语法与抽象语法树的直接生成.pdf
下载积分:1000
内容提示:规范抽象语法与抽象语法树的直接生成.pdf
文档格式:PDF|
浏览次数:27|
上传日期: 00:34:15|
文档星级:
全文阅读已结束,如果下载本文需要使用
 1000 积分
下载此文档
该用户还上传了这些文档
规范抽象语法与抽象语法树的直接生成.pdf
关注微信公众号浅析AST抽象语法树及Python代码实现_python
作者:用户
在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition...
在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。
和抽象语法树相对的是具体语法树(concrete syntaxtree),通常称作分析树(parse tree)。一般的,在源代码的翻译和编译过程中,语法分析器创建出分析树。一旦AST被创建出来,在后续的处理过程中,比如语义分析阶段,会添加一些信息。
抽象语法树的结构不依赖于源语言的文法,也就是语法分析阶段所采用的上下文无关文法。因为在Parser工程中,经常会对文法进行等价的转换(消除左递归、回溯、二义性等),这样会给文法引入一些多余的成分,对后续阶段造成不利影响,甚至会使各阶段变得混乱。因此,很多编译器(包括GJC)经常要独立地构造语法分析树,为前、后端建立一个清晰的接口。
Python实现
假设对'a + 3 * b'进行解释,其中a=2,b=5
代码很简单,就不再进行详细的解释了。
Num = lambda env, n: n
Var = lambda env, x: env[x]
Add = lambda env, a, b:_eval(env, a) + _eval(env, b)
Mul = lambda env, a, b:_eval(env, a) * _eval(env, b)
_eval = lambda env, expr:expr[0](env, *expr[1:])
env = {'a':2, 'b':5}
tree = (Add, (Var, 'a'),
(Mul, (Num, 3),
(Var, 'b')))
print _eval(env, tree)
输出结果为17
以上是互联网用户为您的的内容,在阿里云内部有更多的关于浅析AST抽象语法树及Python代码实现_python的内容,欢迎继续使用右上角搜索按钮进行搜索python、数据结构、树、AST、抽象语法树、以便于您获取更多的相关信息。
本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:zixun-group@service.aliyun.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。
若您要投稿,删除文章请联系邮箱:zixun-group@service.aliyun.com
工作人员会在5个工作日内回复
脚本栏目为您免费提供
相关信息,包括
的信息 ,所有
相关内容均不代表阿里云的意见!投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员5个工作日内回复。Vue.js 源码学习七 —— template 解析过程学习 - 简书
Vue.js 源码学习七 —— template 解析过程学习
这次,来学习下Vue是如何解析HTML代码的。
template 解析用在哪
从之前学习 Render 的过程中我们知道,template 的编译在 $mount 方法中出现过。
// src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
return this
const options = this.$options
// resolve template/el and convert to render function
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
// 首字母为#号,看作是ID。
template = idToTemplate(template)
} else if (template.nodeType) {
// 为真实 DOM,直接获取html
template = template.innerHTML
return this
} else if (el) {
// 获取 HTML
template = getOuterHTML(el)
if (template) {
// 进行编译并赋值给 vm.$options
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
// 渲染函数
options.render = render
// 静态渲染方法
options.staticRenderFns = staticRenderFns
return mount.call(this, el, hydrating)
其实以上代码总结起来就4步:
获取el元素。
判断el是否为body或者html。
为$options编译render函数。
执行之前的mount函数。
关键在于第三步,编译 render 函数上。先获取 template,即获取HTML内容,然后执行 compileToFunctions 来编译,最后将 render 和 staticRenderFns 传给 vm.$options 对象。
顺便看看这两个方法都用在哪里?
// src/core/instance/render.js
Vue.prototype._render = function (): VNode {
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
handleError(e, vm, `render`)
return vnode
// src/core/instance/render-helpers/render-static.js
export function renderStatic (
index: number,
isInFor: boolean
): VNode | Array&VNode& {
const cached = this._staticTrees || (this._staticTrees = [])
let tree = cached[index]
if (tree && !isInFor) {
return tree
// otherwise, render a fresh tree.
tree = cached[index] = this.$options.staticRenderFns[index].call(
this._renderProxy,
markStatic(tree, `__static__${index}`, false)
return tree
由此可见,template 编译生成的方法都用在了渲染行为中。
编译 template 的整体逻辑
下面我们顺着编译代码往下找。在 mount 方法中执行了 compileToFunctions 方法。
const { render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
找到方法的所在之处:
// src/platforms/web/compiler/index.js
const { compile, compileToFunctions } = createCompiler(baseOptions)
// src/compiler/index.js
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 将template转为AST语法树对象
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
// 生成渲染代码
const code = generate(ast, options)
render: code.render,
staticRenderFns: code.staticRenderFns
先看里面的 baseCompile 方法,其作用为将 HTML 字符串转为 AST 抽象语法树对象,并进行优化,最后生成渲染代码。返回值中 render 为渲染字符串,staticRenderFns 为渲染字符串数组。
之后再来看看 createCompilerCreator 方法:
// src/compiler/create-compiler.js
export function createCompilerCreator (baseCompile: Function): Function {
return function createCompiler (baseOptions: CompilerOptions) {
function compile (
template: string,
options?: CompilerOptions
): CompiledResult {
const finalOptions = Object.create(baseOptions)
const errors = []
const tips = []
finalOptions.warn = (msg, tip) =& {
(tip ? tips : errors).push(msg)
if (options) {
// merge custom modules
if (options.modules) {
finalOptions.modules =
(baseOptions.modules || []).concat(options.modules)
// merge custom directives
if (options.directives) {
finalOptions.directives = extend(
Object.create(baseOptions.directives || null),
options.directives
// copy other options
for (const key in options) {
if (key !== 'modules' && key !== 'directives') {
finalOptions[key] = options[key]
// 执行传入的编译方法,并返回结果对象
const compiled = baseCompile(template, finalOptions)
if (process.env.NODE_ENV !== 'production') {
errors.push.apply(errors, detectErrors(compiled.ast))
compiled.errors = errors
compiled.tips = tips
return compiled
compileToFunctions: createCompileToFunctionFn(compile)
来看 compile 方法:合并 option 配置参数,然后执行外部传入的 baseCompile 方法,返回方法执行的返回结果。最终返回 { compile, compileToFunctions },
createCompileToFunctionFn 代码如下:
export function createCompileToFunctionFn (compile: Function): Function {
// 定义缓存
const cache = Object.create(null)
return function compileToFunctions (
template: string,
options?: CompilerOptions,
vm?: Component
): CompiledFunctionResult {
options = extend({}, options)
const warn = options.warn || baseWarn
delete options.warn
// 确认缓存,有缓存直接返回
const key = options.delimiters
? String(options.delimiters) + template
: template
if (cache[key]) {
return cache[key]
// compile
const compiled = compile(template, options)
// turn code into functions
const res = {}
const fnGenErrors = []
// 生成 render 和 staticRenderFns 方法
res.render = createFunction(compiled.render, fnGenErrors)
res.staticRenderFns = compiled.staticRenderFns.map(code =& {
return createFunction(code, fnGenErrors)
// 返回方法并缓存
return (cache[key] = res)
这里就找到了我们在 mount 方法中看到的 render 和 staticRenderFns 方法了。createCompileToFunctionFn 方法其实就是将传入的 render 和 staticRenderFns 字符串转为真实方法。
至此,捋一下思路:
template的编译用于render渲染行为中,所以template最后生成渲染函数。
template 的解析过程中
通过 baseCompile 方法进行编译;
通过 createCompilerCreator 中的 compile 方法合并配置参数并返回 baseCompile 方法执行结果;
createCompilerCreator 返回 compile 方法和 compileToFunctions 方法;
compileToFunctions 方法用于将方法字符串生成真实方法。
其实 const { compile, compileToFunctions } = createCompiler(baseOptions) 就是 createCompilerCreator 的返回结果。所以,在 mount 中使用的 compileToFunctions 方法就是 createCompileToFunctionFn 方法生成的。
baseCompile
整体思路滤清了,来看看关键的 baseCompile 方法。该方法进行了三步操作:
parse 将HTML解析为 AST 元素。
optimize 渲染优化。
generate 解析成基本的 render 函数。
先来讲讲AST抽象语法树。维基百科的解释是:
在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。
parse 方法的最终目的就是将 template 解析为 AST 元素对象。在 parse 解析方法中,用到了大量的正则。正则的具体用法之前写过一篇文章:。代码量很多,考虑了各种解析的情况。这里不赘述太多,找一条主线来学习,其他内容我将在中注释。
来看看 parse 方法。
export function parse (
template: string,
options: CompilerOptions
): ASTElement | void {
// 定义了各种参数和方法
parseHTML(template, {
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldDecodeNewlines: options.shouldDecodeNewlines,
shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
shouldKeepComment: options.comments,
start (tag, attrs, unary) {},
chars (text: string) {},
comment (text: string) {}
return root
实际上 parse 就是 parseHTML 的过程,最后返回AST元素对象。其中,传入的 options 配置对象中,start、end、chars、comment方法都会在 parseHTML 方法中用到。其实类似于生命周期钩子,在某个阶段执行。
parseHTML 方法是正则解析HTML的过程,这部分我将在之后的博客中单独说下,也可以看项目的注释,将不定时更新项目注释。
该方法只是做了些标记静态节点的行为,目的是为了在重新渲染时不重复渲染静态节点,以达到性能优化的目的。
export function optimize (root: ?ASTElement, options: CompilerOptions) {
if (!root) return
isStaticKey = genStaticKeysCached(options.staticKeys || '')
isPlatformReservedTag = options.isReservedTag || no
// 标记所有非静态节点
markStatic(root)
// 标记静态根节点
markStaticRoots(root, false)
generate 方法用于将 AST 元素生成 render 渲染字符串。
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state = new CodegenState(options)
const code = ast ? genElement(ast, state) : '_c("div")'
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
最后生成如下这样的渲染字符串:
with(this){return _c('div',{attrs:{"id":"app"}},[_c('button',{on:{"click":hey}},[_v(_s(message))])])}
其中的 _c _v _s 等方法在哪里呢~这个我们之前说起过:
// src/core/instance/render.js
// 创建vnode元素
vm._c = (a, b, c, d) =& createElement(vm, a, b, c, d, false)
// src/core/instance/render-helper/index.js
export function installRenderHelpers (target: any) {
target._o = markOnce
target._n = toNumber
target._s = toString
target._l = renderList
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
target._v = createTextVNode
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
其实template部分真的内容展开超级多,之后会展开细说。原本计划大前天就把博客写出来的,结果看代码看着看着绕进去了。所以,还是那句话,看代码得抓住主线,带着问题去看,不要在意细枝末节。
这也算是我的经验教训了,以后每次看代码,牢记待着明确的问题去看去解决。想一次看懂整个项目的代码是不可行的。
下期预告,parseHTML 细节解析
Vue.js学习系列
鉴于前端知识碎片化严重,我希望能够系统化的整理出一套关于Vue的学习系列博客。
Vue.js学习系列项目地址
本文源码已收入到GitHub中,以供参考,当然能留下一个star更好啦-。
VioletJack,高效学习前端工程师,喜欢研究提高效率的方法,也专注于Vue前端相关知识的学习、整理。
欢迎关注、点赞、评论留言~我将持续产出Vue相关优质内容。
新浪微博:
专注于Vue前端学习和分享
博客主页:https://violetjack.github.io/
Github:https://github.com/violetjack
写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些产出也会对同样想要学习Vue.js源码的小伙伴有所帮助。之前这篇文章同样在...
写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出。 文章的原地址:https://github.com/answershuto/learnVue。 在学习过程中,为Vue加上了中文...
--- type: api --- ## 全局配置 `Vue.config` 是一个对象,包含 Vue 的全局配置。可以在启动应用之前修改下列属性: ### silent - **类型**:`boolean` - **默认值**:`false` - **用法**:
今天我们来学习下Vue的渲染 Render 源码~ 还是从初始化方法开始找代码,在 src/core/instance/index.js 中,先执行了 renderMixin 方法,然后在Vue实例化的时候执行了 vm._init 方法,在这个 vm._init 方法中执行...
这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于 Vue 1.0 ,可以参考我的另一篇文章 Vue.js 1.0 Official Guide Notes。 Vue 实例 属性与方法 不要在实例属性或者回调函...
在APP的使用的场景中,我们可能会有这样的需求,让APP停留在主页不退出。应用场景一般在导购屏中,让导购屏停留在首页,这种需求主要是为了避免用户在退出APP之后无法再次找到我们的APP。做法是在清单文件的入口activity的中加如下代码:默认返回键的处理: @Overri...
作为众多“宋太太”大军中的一员,过去的两个月里晚上都是看着wuli仲基入睡的,甜笑着幻想自己是女主一般,仲基将自己揽入怀中,可是早上被枕边人吓出一身冷汗,一下拉回到了残酷的现实世界(先让我哭会儿),额,这种症状是种病,心病,专业心理学术语叫妄想症(呵呵,其实有点夸张,还没那...
我的亲戚朋友们家里大都是两个孩子,有兄弟俩的,姐妹俩的,兄妹俩的,姐弟俩的,我们聚到一起讨论最多的就是俩孩子的表现。 分开了就想念的不得了,在一起久了就打闹,小的故意找事,大的不知礼让,最后都气呼呼地觉得自己受了很大的委屈,眼泪啪啪地直掉,做家长的哄完大的哄小的,有时候气的...
堕胎给父母带来的运气无限,下面让Yi朝阳师傅为我们解释不同宗教如何弥补堕胎。 佛陀说有天赋插上五条戒律,”已经提到,如果堕胎法谋杀。如果胎儿不遗憾。如果孩子不是罪的死亡。如果死者的母亲是遗憾的。打胎有报应吗所有的死者都不遗憾。是堕胎法的名称。堕胎是一个大罪。如果胎儿死亡或母...
为什么刚开始决定提升自己时,再完美的计划也是无效的? 我的建议是:你没必要8~10点都用来提升,可以只用一个小时、甚至半个小时的时间,坚持去做一件「你能坚持下来」的事情。 对于绝大多数人来说,无论多么完美的计划都是无效的。因为这些计划对于他们本人而言根本不具备可执行性。 如... (转载请表明出处 &) 
& 这是flxe的文件,文件名称为tiger.l
#include &string.h&
#include "util.h"
#include "tokens.h"
#include "errormsg.h"
#include "iostream"
#include "tiger.tab.h"
int charPos=1;
int count =
#ifdef __cplusplus
extern "C" int yywrap (void )
extern int yywrap (void )
charPos = 1 ;
return 1 ;
void adjust(void)
EM_tokPos=charP
CONST_STRING
&INITIAL&[\"]
{adjust();
BEGIN CONST_STRING;}
&INITIAL&" "
{adjust();
continue;}
&INITIAL&\n
{adjust(); lineNum++; continue;}
&INITIAL&\t
{adjust(); continue;}
&INITIAL&","
{adjust(); std::cout && "COMMA" && std:: return COMMA;}
&INITIAL&:
{adjust(); std::cout && "COLON" && std:: return COLON;}
&INITIAL&;
{adjust(); std::cout && "SEMICOLON" && std:: return SEMICOLON;}
&INITIAL&"("
{adjust(); std::cout && "LPAREN" && std:: return LPAREN;}
&INITIAL&")"
{adjust(); std::cout && "RPAREN" && std:: return RPAREN;}
&INITIAL&"["
{adjust(); std::cout && "LBRACK" && std:: return LBRACK;}
&INITIAL&"]"
{adjust(); std::cout && "RBRACK" && std:: return RBRACK;}
&INITIAL&"{"
{adjust(); std::cout && "LBRACE" && std:: return LBRACE;}
&INITIAL&"}"
{adjust(); std::cout && "RBRACE" && std:: return RBRACE;}
&INITIAL&"."
{adjust(); std::cout && "DOT" && std:: return DOT;}
&INITIAL&"+"
{adjust(); std::cout && "PLUS" && std:: return PLUS;}
&INITIAL&"-"
{adjust(); std::cout && "MINUS" && std:: return MINUS;}
&INITIAL&"*"
{adjust(); std::cout && "TIMES" && std:: return TIMES;}
&INITIAL&"/"
{adjust(); std::cout && "DIVIDE" && std:: return DIVIDE;}
&INITIAL&"="
{adjust(); std::cout && "EQ" && std:: return EQ;}
&INITIAL&"&&" {adjust(); std::cout && "NEQ" && std::
return NEQ;}
&INITIAL&"&"
{adjust(); std::cout && "LT" && std:: return LT;}
&INITIAL&"&=" {adjust(); std::cout && "LE" && std:: return LE;}
&INITIAL&"&"
{adjust(); std::cout && "GT" && std:: return GT;}
&INITIAL&"&=" {adjust(); std::cout && "GE" && std:: return GE;}
&INITIAL&"&"
{adjust(); std::cout && "AND" && std:: return AND;}
&INITIAL&"|"
{adjust(); std::cout && "OR" && std:: return OR;}
&INITIAL&:=
{adjust(); std::cout && "ASSIGN" && std:: return ASSIGN;}
&INITIAL&for
{adjust(); std::cout && "FOR" && std:: return FOR;}
&INITIAL&array {adjust();std::cout && "ARRAY" && std::
return ARRAY;}
&INITIAL&if
{adjust(); std::cout && "IF" && std:: return IF;}
&INITIAL&then {adjust(); std::cout && "THEN" && std:: return THEN;}
&INITIAL&else {adjust(); std::cout && "ELSE" && std:: return ELSE;}
&INITIAL&while {adjust(); std::cout && "WHILE" && std:: return WHILE;}
&INITIAL&to
{adjust(); std::cout && "TO" && std:: return TO;}
&INITIAL&do
{adjust();std::cout && "DO" && std::
return DO;}
&INITIAL&let
{adjust();std::cout && "LET" && std::
return LET;}
&INITIAL&in
{adjust();std::cout && "IN" && std::
return IN;}
&INITIAL&end
{adjust();std::cout && "END" && std::
return END;}
&INITIAL&of
{adjust(); std::cout && "OF" && std:: return OF;}
&INITIAL&break {adjust(); std::cout && "BREAK" && std:: return BREAK;}
&INITIAL&nil
{adjust(); std::cout && "NIL" && std:: return NIL;}
&INITIAL&function {adjust();std::cout && "FUNCTION" && std::
return FUNCTION;}
&INITIAL&var
{adjust(); std::cout && "VAR" && std:: return VAR;}
&INITIAL&type {adjust();std::cout && "TYPE" && std::
return TYPE;}
&INITIAL&[0-9]+[a-zA-Z]+
{adjust();
/*error!!!!*/ }
&INITIAL&[0-9]+
{adjust(); std::cout && "INT" && std:: yylval.ival=atoi(yytext); return INT;}
&INITIAL&[a-zA-Z][a-zA-Z0-9]*
{adjust(); std::cout && "ID" && std:: yylval.sval=string(yytext);return ID;}
&INITIAL&"/*"
{adjust();count++; BEGIN COMMENT;}
&CONST_STRING&[\"]
{adjust();
BEGIN INITIAL ; }
&CONST_STRING&[^\"]*
{adjust(); yylval.sval = string(yytext);std::cout && "STRING" && std::
return STRING; }
&COMMENT&"*/"
{adjust();count--; if(count == 0) BEGIN INITIAL;}
&COMMENT&. {adjust();}
&COMMENT&\n {adjust();}
  这里需要注意的是,使用了形如:
#ifdef __cplusplus
static int yyinput (void );
static int input (void );
#ifdef __cplusplus
extern "C" int yywrap (void )
extern int yywrap (void )
charPos = 1 ;
return 1 ;
  这样的标示,因为我想使用c++,但是flex生成的是c,所以这里要特别声明一下。
  以上使用flex后得到的.c文件直接改为.cpp,然后找到文件中的#include &unistd.h& ,使用&#include &io.h& &和&#include &process.h&替换就可以了。
  一下是tiger的bison文件,文件名为tiger.y
#include &stdio.h&
#include "util.h"
#include "errormsg.h"
#include "absyn.h"
#include "symbol.h"
#include "iostream"
extern int yylex(void); /* function prototype */
A_exp absyn_
void yyerror(char *s)
EM_error(EM_tokPos, "%s", s);
// &&&&union &&O&&&&O&&&&&OE&?&&&&&$&?&&&&O&O&&O&A&&&&&&&O&E&& &&&&O&&&&&&&&&&&&&&&O&&?&&I&&O&&O &&&& &&&O&&?&&I&&O&&O&&&&&&&&&&&&&&&& &&O&&Y&&&?&&AE&&D&&&&O&&&&&&A
A_var var ;
A_expList expL
A_fundecList fundecL
A_nametyList tydecL
%type &var& lvalue
%type &exp& exp
%type &dec& dec
%type &ty&
%type &decs& decs
%type &expList& expseq
%type &expList& expList
%type &tyfield& tyfield
%type &tyfields& tyfields
%type &fundec&
%type &fundecList& fundecList
%type &tydec& tydec
%type &tydecList& tydecList
%type &fild& fild
%type &fildlist&
%token &sval&
%token &sval&
%token &ival&
COMMA COLON SEMICOLON LPAREN RPAREN LBRACK RBRACK
LBRACE RBRACE DOT
PLUS MINUS TIMES DIVIDE EQ NEQ LT LE GT GE
AND OR ASSIGN
ARRAY IF THEN ELSE WHILE FOR TO DO LET IN END OF
FUNCTION VAR TYPE UMINUS
%start program
%right FUNCTION TYPE
%right DO ELSE THEN
%nonassoc ASSIGN
%nonassoc EQ NEQ LT LE GT GE
%left PLUS MINUS
%left TIMES DIVIDE
%left UMINUS
/* This is a skeleton grammar file, meant to illustrate what kind of
* declarations are necessary above the %% mark.
Students are expected
to replace the two dummy productions below with an actual grammar.
{ absyn_root = $1 ; }
{ $$ = A_DecList($1 , NULL) ; }
| dec decs
{ $$ = A_DecList($1 , $2) ; }
{ $$ = A_TypeDec(pos , $1) ;
| VAR ID ASSIGN exp
{ $$ = A_VarDec(pos , S_Symbol($2) , NULL , $4) ; }
| VAR ID COLON ID
ASSIGN exp
{ $$ = A_VarDec(pos , S_Symbol($2) , S_Symbol($4) , $6) ;
| fundecList
{ $$ = A_FunctionDec(pos , $1) ; }
tydecList :
{ $$ = A_NametyList($1 , NULL); }
| tydec tydecList
{ $$ = A_NametyList($1 , $2) ; }
TYPE ID EQ ty
{ $$ = A_Namety(S_Symbol($2) , $4 ) ;}
LBRACE tyfields RBRACE { $$ = A_RecordTy(pos , $2); }
| LBRACE RBRACE
{ $$ = A_RecordTy(pos , NULL);
| ARRAY OF ID
A_ArrayTy(pos , S_Symbol($3)); }
{ $$ = A_NameTy(pos , S_Symbol($1)) ; }
{ $$ = A_Field(pos, S_Symbol($1) , S_Symbol($3));
{ $$ = A_FieldList($1 , NULL) ; }
| tyfield COMMA tyfields
{ $$ = A_FieldList($1 , $3);
fundecList : fundec
{ $$ = A_FundecList($1 , NULL); }
| fundec fundecList { $$ = A_FundecList($1 , $2);
fundec : FUNCTION ID LPAREN tyfields RPAREN COLON ID
EQ exp { $$ = A_Fundec( pos, S_Symbol($2) , $4 , S_Symbol($7) , $9 ) ;
| FUNCTION ID LPAREN RPAREN COLON ID EQ exp
{ $$ = A_Fundec( pos, S_Symbol($2) , NULL , S_Symbol($6) , $8 ) ;
| FUNCTION ID LPAREN tyfields RPAREN EQ exp
{ $$ = A_Fundec( pos, S_Symbol($2) , $4 , NULL , $7 ) ;
| FUNCTION ID LPAREN RPAREN EQ exp
{ $$ = A_Fundec( pos, S_Symbol($2) , NULL , NULL , $6 ) ; }
{ $$ = A_SimpleVar(pos , S_Symbol($1) ) ;
| lvalue DOT ID
{ $$ = A_FieldVar(pos, $1 ,S_Symbol($3) );
| ID LBRACK exp RBRACK { $$ = A_SubscriptVar(pos ,
A_SimpleVar(pos , S_Symbol($1) ) , $3); }
S_Symbol($1) ,$3 ) ; }
fildlist :
{ $$ = A_EfieldList( $1 , NULL ); }
| fild COMMA fildlist
{ $$ = A_EfieldList( $1 ,$3 ) ; }
//expseq &&O explist &&&?&&&&&&I&& &&I&&O&&O&&&& &&O
&&&&&&&&&A?&&&&&?&&&O&&&&&&&&?&& &&U&OE&&A?&&&&&&&&&&&&&&&O&&?&&&&&&&?
{ $$ = A_ExpList( $1 , NULL ); }
| exp SEMICOLON
{ $$ = A_ExpList( $1 , $3 ) ;
{ $$ = A_ExpList($1 , NULL ) ; }
| exp COMMA expList
{ $$ = A_ExpList($1 , $3) ; }
$$ = A_OpExp( pos , A_oper::A_minusOp , A_IntExp(pos , 0) , $2 ); }%prec UMINUS
{ $$ = A_VarExp(pos , $1 ); }
{ $$ = A_NilExp(pos); }
| LPAREN RPAREN { $$ = A_NilExp(pos); }
//const int
{ $$ = A_IntExp(pos , $1) ; }
//const string
{ $$ = A_StringExp(pos , $1); }
// expression sequence
| LPAREN expseq RPAREN
{ $$ = A_SeqExp( pos , $2 ) ; }
//CALL FUNCTION
|ID LPAREN RPAREN
{ $$ = A_CallExp( pos, S_Symbol($1) ,NULL ); }
|ID LPAREN expList RPAREN { $$ = A_CallExp(pos , S_Symbol($1) ,$3) ; }
//OPERATOR
|exp PLUS exp
{ $$ = A_OpExp( pos , A_oper::A_plusOp , $1 , $3 ); }
|exp MINUS exp
{ $$ = A_OpExp( pos , A_oper::A_minusOp , $1 , $3 ); }
|exp TIMES exp
{ $$ = A_OpExp( pos , A_oper::A_timesOp , $1 , $3 ); }
|exp DIVIDE exp
{ $$ = A_OpExp( pos , A_oper::A_divideOp , $1 , $3 ); }
|exp EQ exp
{ $$ = A_OpExp( pos , A_oper::A_eqOp , $1 , $3 ); }
|exp NEQ exp
{ $$ = A_OpExp( pos , A_oper::A_neqOp , $1 , $3 ); }
|exp LT exp
{ $$ = A_OpExp( pos , A_oper::A_ltOp , $1 , $3 ); }
|exp LE exp
{ $$ = A_OpExp( pos , A_oper::A_leOp , $1 , $3 ); }
|exp GT exp
{ $$ = A_OpExp( pos , A_oper::A_gtOp , $1 , $3 ); }
|exp GE exp
{ $$ = A_OpExp( pos , A_oper::A_geOp , $1 , $3 ); }
|exp AND exp
{ $$ = A_IfExp( pos , $1 , $3 ,
A_IntExp(pos ,0)); }
|exp OR exp
{ $$ = A_IfExp( pos , $1 , A_IntExp(pos , 1) ,$3 ); }
|ID LBRACE RBRACE
{ $$ = A_RecordExp(pos, S_Symbol($1) , NULL); }
|ID LBRACE fildlist RBRACE
{ $$ = A_RecordExp(pos,S_Symbol($1) , $3); }
|ID LBRACK exp RBRACK OF exp {
$$ = A_ArrayExp(pos , S_Symbol($1) , $3 ,$6) ; }
|lvalue ASSIGN exp
{ $$ = A_AssignExp(pos , $1 , $3) ; }
//if...then....else
|IF exp THEN exp ELSE exp {
$$ = A_IfExp( pos , $2 , $4 , $6 ) ;}
|IF exp THEN exp
$$ = A_IfExp( pos , $2 , $4 , NULL) ;}
|WHILE exp DO
exp { $$ = A_WhileExp(pos, $2 , $4 );}
|FOR ID ASSIGN exp TO exp DO exp { $$ =
A_ForExp( pos, S_Symbol($2) , $4 , $6 , $8); }
{ $$ = A_BreakExp( pos ) ; }
| LET decs IN expseq
{ $$ = A_LetExp( pos , $2 , A_SeqExp( pos , $4 ));}
| LET decs IN END
{ $$ = A_LetExp( pos , $2 ,NULL) ;}
  对于这个文件,虎书给的是.grm文件后缀,但是只有使用.y文件后缀才可以生成相应的.c和.h文件。同样,为了使用c++,将文件后缀改为.cpp。
  这里提一些要点:
  1.使用bison生成文件时,输入-b 生成的文件可以进行debug,使用-v可以得到一个.output文件,这个文件里就记录了所有的状态信息,方便查看各种移进规约冲突等的位置。
  2.当出现$$ 未定义标示符的时候,说明你的bison没有加union 和 %type 语句,如果再加上%token 就可以生成.h文件中定义了一个枚举和一个联合,因为在联合中的类型都是自己定义的,所以在生成的.h文件中还要加得到的.h文件还要加上在程序中使用的相应的头文件 如#include "symbol.h" ,#include "absyn.h"。生成的这个.h文件很重要这两个是实现bison生成的语法分析器和flex生成的词法分析器交互的数据结构。
& & &联合有两个作用,第一个定义一个栈,将语法分析过程中的数据都存放到这个栈中,使用这个栈生成最后的抽象语法分析树。第二个作用是定义了一个变量 yyval,这个变量 yylval 的类型是YYSTYPE(这个类型就是联合)。这个变量将词法分析过程中的值记录下来放入栈中,如{adjust(); std::cout && "ID" && std:: yylval.sval=string(yytext);return ID;}中记录ID的实际命名时什么,还有一个作用就是使用它记录了语法分析器中的动作(每条规则后面的c++程序)中的值返回值。找到这个变量的定义,发现上面有一个注释:该变量返回动作中的值,也就是说它里面记录了动作(每条规则后面的c++程序)中的值,并压入栈中。看看bison生成的代码可以知道,yyval也就是$$,他的每个动作都是使用栈顶的内容生成一个新的内容,老的内容被移出去,然后赋值给yylval,最后由这个yylval值在赋值给栈顶。从而完成了抽象语法数的构建。这样可以知道,在整个过程中,词法分析器中使用的内容是由.h文件定义的类型,以及在.c文件中定义的变量来生成的。
  在联合的下面定义了一些终结符和非终结符的具体类型,可以看出都是联合中包含的类型,这样就保证了可以使用一个类型(联合)来创建一个栈,这个栈可以包含多种类型。
& & &词法分析器向语法分析器发送的是一个个的终结符,如 中的return &INITIAL&:= {adjust(); std::cout && "ASSIGN" && std:: return ASSIGN;} 由上面的分析可以知道,这句话中的ASSIGN在语法分析器的.h文件中定义的。(其实在实际编写过程中,应该是先写语法再写词法,书上的顺序是返的,便于大家理解而已。)
阅读(...) 评论()

我要回帖

更多关于 抽象语法树 编译原理 的文章

 

随机推荐