语法分析实现

实现
  • 首先前面已经说过,语法分析根据每一行的第一个属性字,然后对接下来的进行预测,如果符合预测,就是正确的指令
  • 那么首先语法分析
Func
  • Func不能嵌套定义,首先判断isFuncActive,如果是,则报错,否则继续读取下一个属性字是否是标识符,如果是标识符,那么就在函数表中查找,是否重复定义过。如果没有继续再读取下一个属性字是否是“{”,如果是,就判断函数名字是否是_Main,如果是就将MainHeader标记_Main已经出现过并且记录其指令入口点。 然后就将该函数的名字和函数入口点加入函数表,并且标记当前isFuncActive,还要记录当前函数的索引。函数信息的搜集不是一次完成的。当遇到右“}”关闭括号时候,判断当前函数是否是活跃的,如果是,就将统计的当前LocalSize和ParamCount放置进当前函数表中
  • 【当虚拟机真正运行的时候,他遇到一个函数,首先根据其localSize+ParamCount+1开拓堆栈空间,然后Push的时候将参数放置在空间的底部,然后放置返回地址,然后每遇到一个局部数据,就放置进堆栈中。所以对于局部数据来说,在运行的时候想支持前向引用,你可以先将函数遍历然后将局部数据的定义一次性放入堆栈中。这样前向引用的时候可以在堆栈中获取到这个数据。不过这样没有必要,因为会造成阅读困难】
Param
  • 【作者的思路是,在第一次遍历的时候我们需要首先统该函数计所有局部数据的大小iLoalSize并赋予其堆栈索引因为局部数据是位于栈顶的,并且同时统计其ParamCount,但是没有办法赋予Param堆栈索引,因为此时已经到达函数尾部了。当第二遍遍历的时候,每遇到一个Param,就赋予其堆栈索引-(localSize+2+1+currParamCount)】
  • 因为这个原因,Param不支持前向引用,因为到第二遍遍历的时候,我们才知道其堆栈索引,在第二遍同时也要进行指令解析。不过作者好像没有注意到这个bug
  • 【为什么不在第一次遍历的时候,对于Param和localVarible同时进行堆栈索引统计呢,因为在调用函数的时候,参数是通过Push统一压进去的,因此Param是一个连续的空间】
  • 因此对于Param的执行,就是判断函数是否活跃,如果活跃,就紧接着判断其下一个属性字是否是标识符。如果是就获取其堆栈索引,然后将其加入到符号表中
Var/Var[]
  • 首先判断接下来的属性字否是标识符,如果是标识符,就紧接着进行预读取,判断下一个是否是“【”,因为有可能是数组,如果是数组,就判断接下来是否是数字,如果是数字就将其保存到iSize,再判断接下来是否是“】”,
  • 然后根据其是局部还是全局,获得其堆栈索引,全局堆栈索引为正,局部堆栈索引为负【局部堆栈索引等于—(当前局部数据大小+2)】
  • 无论是全局变量还是局部变量,最后都要放进符号表里面。【通过StackIndex是否大于0来判断是局部还是全局】
  • 其实应该先判断GetSymbol,如果可以获取得到,那么就直接报错。不用继续下去了
if(CurrNode.FuncIndex == FuncIndex | | CurrNode.StackIndex >= 0)
	return CurrNode;
SetStackSize
  • 如果是这个属性字,首先判断以前是否出现过,因为他只能定义一次,然后判断是否函数活跃的,因为声明堆栈大小只能在全局空间中。最后再获取下一个属性字,是否是一个整型变量,如果是,是否大于0,否则继续报错。如果将其转换为整数,保存到MainHeader结构体中,并且标识已经声明过堆栈大小。这个是在第一次遍历的时候就可以处理的指示符。
行标签
  • 判断当前函数是否活跃,因为行标签只可以定义在函数当中
  • 判断接下来的属性字是否是COMMA即冒号,如果是冒号,说明这就是一个行标签
  • 然后将当前指令大小——即下一条指令的索引和该标签加入到标签表当中
指令解析
  • 对于指令解析,首先根据指令字符串查找LookUpTable查找到指令,然后获取其iCount和iOpType
  • 并且在指令流数组中当前索引处存储其iCount,并且给OpList分配空间
  • 根据其iCount进行循环,每循环一次就读取其下一个属性字并且获取其操作数类型,然后将操作数类型和iOpType列表中对应操作数的类型掩码取与,如果不为零,则表明该操作数类型符合语法,并将其保存到OpList当中
  • Op有个字段是iType,然后是value
  • 对于指令来说,其操作数类型有以下几种
  • 整型字面量
  • 字符串型字面量
  • 绝对堆栈索引
  • 相对堆栈索引 【需要先去堆栈获取变量大小然后加上函数基址】
  • 标签
  • 函数
  • 主应用程序API调用
  • 一个寄存器 _RetVal
这个里面的都是测试数据,总共得分5分。从控制台输入,不能从文件中读取。实现了基本功能,加分项目都没有去实现,没有函数数组这些的实现。这是用C++语言写的,新建parser类别要选C++,其他对于VS的配置和C语言一样。for语句用的是枚举所有情况,你可以自行修改。 对预备工作中自然语言描述的简化C编译器的语言特性的语法,设计上下文无关文法进行描述 借助Yacc工具实现语法分析器 考虑语法树的构造: 1.语法树数据结构的设计:节点类型的设定,不同类型节点应保存哪些信息,多叉树的实现方式 2.实现辅助函数,完成节点创建、树创建等功能 3.利用辅助函数,修改上下文无关文法,设计翻译模式 4.修改Yacc程序,实现能构造语法树的分析器 考虑符号表处理的扩充 1.完成语法分析后,符号表项应增加哪些标识符的属性,保存语法分析的结果 2.如何扩充符号表数据结构,Yacc程序如何与Lex程序交互,正确填写符号表项 以一个简单的C源程序验证你的语法分析器,可以文本方式输出语法树结构,以节点编号输出父子关系,来验证分析器的正确性,如下例: main() { int a, b; if (a == 0) a = b + 1; } 可能的输出为: 0 : Type Specifier, integer, Children: 1 : ID Declaration, symbol: a Children: 2 : ID Declaration, symbol: b Children: 3 : Var Declaration, Children: 0 1 2 4 : ID Declaration, symbol: a Children: 5 : Const Declaration, value:0, Children: 6 : Expr, op: ==, Children: 4 5 7 : ID Declaration, symbol: a Children: 8 : ID Declaration, symbol: b Children: 9 : Const Declaration, value:1, Children: 10: Expr, op: +, Children: 8 9 11: Expr, op: =, Children: 7 10 12: if statement, Children: 6 11 13: compound statement, Children: 3 12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值