彻底理解所谓的自顶向下分析和自底向上分析到底是啥玩意
首先要明确的是这两种方法有同样的目的,都是读取一段字符流,输出一个等价的AST,也就是抽象语法树,表面上看是通过某种分析手段把源文件“转换”成了一颗树状结构,但是这里有个非常容易让人误解的地方,就是这个”转换“并非正在的”转换“,也就是说它不是一种直接变换,比如,你把一张纸折成了纸飞机,这是一种变换,你在原来东西的基础上做了调整,呈现出了不同的外观。 但是这里看似”转换“的效果,其实是通过”生成“完成的,也就是AST本身之前并不存在,他并不是通过调整源文件结构直接变换得来的,而是通过一些列规则,基于源文件完全”无中生有“生成出来的。 这也是为什么相关语法分析的一些语法被叫做”生成文法“。源文件只是被掰开揉碎后作为”生成“过程中的向导和原材料。 那么我们具体看一下所谓的”自顶向下“,也就是top-down是怎么回事。 这里自动向下其实就是从大到小的意思,加入我们现在有这样的一个对应关系: 源文件--->程序(P), P表示program 那么现在假如我有一个解释器I,interpreter, 我们期望的是当I看到P的时候,就可以完全理解”源文件“里的内容。但是现在P仅仅是一个符号,啥实质性内容也没有,我么可以把P看做一颗树形结构的一个节点,但目前他就是一个孤零零的空节点,一个纯粹的符号。 那么我们如何才能让P长出枝叶,变得有血有肉,这样他才能具有意义,才能传达信息。而这正是自顶向下要做的事情,如何做呢?答案是不断的猜,你没听错,就是猜。 ”猜“这个字听起来不专业啊,我们换个词,叫”预测“。 总结一下,”自顶向下的猜“就变成了听起来更专业的”自顶向下的预测分析“。(啥玩意,能不能好好说人话,互联网黑话真晦涩,嗨,还不是因为这些东西都是人家老外原创的,我们翻译过来又怕翻译的不专业,然后就搞一堆晦涩难懂的词,结果时间长了大家都习惯了也不好改了,不过有些东西其实用大白话翻译更容易理解。) 不废话了,那我们就看看怎么”猜“的。 加入现在有一段代码:var a =b+3*5; 我们当然很容易理解,因为我们大脑可以解析这个结构,这个所谓的文本,非结构化数据,对我们的大脑来说其实是结构化的,所谓的非结构化只是相对于那些无法理解这个结构的计算机程序,在它们看来短代码只是一串没有任何意义的字符序列。但是如果我们的I(解释器)可以理解某种树状结构的数据,那么如果我们能够把这段字符序列转化成一个树状的数据结构,I就可以理解了。现在目标进一步明确了。 说了半天我好像还是不知道怎么转化。。。别急,转化肯定是需要规则的,没有章法乱转化,完全不确定能转化出啥啊,必然无法达成目标。 那么我们需要一套规则,来指导我们如何完成这个转化过程。 而这个所谓的规则就是某种”生成文法“,他指导我们如何通过这个规则生成一个”语法树”,也就是一个树形的数据结构,而这个结构可以和源文件的字符串在形式上等价,注意用词,是“等价”,不是“相同“,这里的等价是说二者可以相互转化,我拿到这个语法树,其实也是可以完全生成一个等价的源文件。如何制定这个规则呢?首先我们用符号S表示源代码,用P表示AST,抽象语法树的根节点。 那么现在得到了:S-->P这么一个关系,现在S和P是不等价的,因为P仅仅是一个空节点,啥内容也没有,而S却代表了整个源代码。 那么我们要想办法把P丰富起来,给它添加上枝叶,而这些枝叶的取材就来自S,我们把S所代表的源代码掰开揉碎了,来拼凑出P的枝叶,这样两边就等价了。恩,思路又进一步明确了,如何操作呢? 如果要是物理实体的话,对于人来说好像拼出来这么一个东西不太难,毕竟我们大脑已经理解了整个上下文和每个符号的意义,所以我们的大脑很容知道怎么拼,可是计算机程序它就比较蠢,他只认识字符。。。 那么我们来分析下源代码,把他拆成更小的组成部分: var,这个东西我们了解,是在说要声明某个变量,而后面所有的东西都是跟这个声明关联的,所以我们就任务这整个字符序列是一个声明语句,记作 那么现在就有了S->->P 继续分析,整个的开头是var,是不是以var开头的一段符号都是一个?有可能,不过我也不知道啊,谁知道代码里别人会写啥,先不管,先这么认为吧,碰到var 后面就是,那我们给var起个有意义的名字吧,叫,因为它好像就是个标记而已,var后面是个空格,好像没啥意义,就是个分隔符号,我们先把它忽略掉,继续往下看,碰到一个字符a,然后我们还发现它后面是个赋值操作,而这个a标记赋值操作左侧的一个符号,因为是个符号,也可能是b,c,d啊什么的,所以我们给它起个通用的名字叫做,同理,后面是个等号,是一个运算符,我们也起个名字叫,等号后面好像是一堆数字,这玩意我们熟悉啊,这不就是个”四则运算表达式“嘛,没错,那么我们给他整体起个名字就叫吧,经过这一顿分析,我们好像有了新的进展: S->->->P 这里两个好像看不出有啥区别啊,不太好吧,并且var好像还是有点意义的,可以作为整个结构的起始标记,姑且这么认为,好吧,那我们改一下,var就叫var吧,它好像比较特别,那就成了: S-> ->var ->P
不写了,费劲,写文章还挺累的