Pascal的变量是相对比较简单的,仅支持全局变量和局部变量两类。Pascal不允许用户声明块内变量(例如,C语言中可以将变量声明置于某一对大括号内,变量的生存期则仅限于大括号所示的程序块内)。虽然块内变量的优越性显而易见,但是标准Pascal并不支持,因此可以忽略。
有了先前的基础,变量声明部分的语义处理将是比较简单的。从变量信息表的结构来看,不难发现,变量信息中需要填写的属性并不多(一共只有6个)。实际上,对于用户声明的变量而言,m_eBank、m_bRef两个属性的取值是固定不变的,总是“VAR”和“false”,因为这两个属性主要适用于一些形参变量或临时变量。而m_iMemoryAlloc也是存储分配相关的属性,因此也不必考虑。故需要补充填写的属性只有3个。下面先介绍变量声明部分相关的文法及其主要的语义动作。
【文法4-13】
根据前面的经验,很容易得到以下结论:变量声明部分的主要语义动作必定存在于非终结符“标识符列表”相关的产生式中,而semantic013、semantic040的主要工作就是标志栈的管理。下面先来看看semantic013、semantic040的源代码。
程序4-33 semantic.cpp
第3行:将“6”压入iListFlag栈,便于推导“标识符列表”时进行相关语义判断。
程序4-34 semantic.cpp
第4、5行:设置临时类型信息表项的m_szName属性。实际上,变量声明中的类型分为两种情况:非匿名类型与匿名类型。所谓非匿名类型,就是指变量的类型是一个用户自定义类型,例如:
【声明4-21】
AA并不是Pascal的标准类型,而是一个用户自定义类型的名字。除了非匿名类型之外,其他的形式都可以看作匿名类型。当变量声明中的“类型”是一个匿名类型时,其语义动作非常简单,只需建立一个类型结点供后续“类型”相关的语义子程序填写信息即可。对于非匿名类型而言,其语义动作就稍有不同。根据符号表及类型链的设计,比较好的处理方式是直接将变量信息表项的m_iTypeLink指向该用户自定义类型的类型链的首结点。由于这两种处理方式存在一定的差异,可能给语义子程序的设计带来小小的不便,为了便于设计与编码,笔者将两者的处理方式进行了统一,即都采用前者的处理方案。这样的改变也是有代价的,就是会造成非匿名类型的变量描述存在一个冗余的类型结点,不过,这丝毫不会给编译器分析源程序带来任何障碍。当然,通过修改设计方案完全可以避免此事的发生,从而使之比较完美,但是程序设计的复杂度却可能有所增加,效率也会降低。(www.daowen.com)
第7行:m_iLink属性初始化为1,而其实际值将由类型声明相关的语义子程序来填写。
第9行:设置iTypePos栈,即当前类型结点,以便后续“类型”相关的语义子程序处理。
下面,再来看看semantic012是如何生成变量信息表项的。
程序4-35 semantic.cpp
第7行:设置m_eRank属性。这里,只需考虑用户变量声明的情况,故统一将m_eRank属性设为VAR即可。
第9行:设置iTypeLink属性。由于无论是匿名类型还是非匿名类型,semantic040都会在表尾追加一个类型信息表项,因此这里只需将iTypeLink指向表尾信息项即可。
第10行:获取变量名字。
第11~25行:检查变量名字是否有效。根据实际情况,依次检索符号表即可。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。