java实现C语言解释器:无参数传递的函数调用的解释和执行
来源:互联网 发布:软件开发包 编辑:程序博客网 时间:2024/04/30 15:39
具体的代码讲解和调试演示过程,请参看视频:
用java开发C语言编译器
本节,我们看看,如何实现C语言中的函数调用,举个具体例子,在完成本节的代码后,我们的解释器功能进一步增强,使得它能解释执行下面的C语言代码:
void f() { int a; a = 1;}void main() { f();}
我们先看看函数定义的语法表达式:
EXT_DEF -> OPT_SPECIFIERS FUNCT_DECL COMPOUND_STMTFUNCT_DECL -> UNARY LP RP
对应于函数f的定义 ,其中 OPT_SPECIFIERS 对应的是关键字void, FUNCT_DECL对应的是 f(), 最后COMPOUND_STMT对应的是:
{
int a;
a = 1;
}
根据表达式,解释器会构造如下执行树:
同理,对于main函数,我们也有同样的执行树:
在主函数main 中,实现了对函数f的调用,函数调用对应的语法如下:
UNARY -> UNARY LP RP
NUARY -> NAME
其中NAME 对应的是被掉函数名 f, LP RP 对应左右括号。
我们看看构造执行树的代码对此进行的相应改动,CodeTreeBuilder.java:
public class CodeTreeBuilder {private String functionName; private HashMap<String, ICodeNode> funcMap = new HashMap<String , ICodeNode>(); public ICodeNode buildCodeTree(int production, String text) { ICodeNode node = null; Symbol symbol = null; switch (production) { ... case CGrammarInitializer.NewName_LP_RP_TO_FunctDecl: case CGrammarInitializer.NewName_LP_VarList_RP_TO_FunctDecl: node = ICodeFactory.createICodeNode(CTokenType.FUNCT_DECL); node.addChild(codeNodeStack.pop()); child = node.getChildren().get(0); functionName = (String)child.getAttribute(ICodeKey.TEXT); symbol = assignSymbolToNode(node, functionName); break; case CGrammarInitializer.NewName_TO_VarDecl: //我们暂时不处理变量声明语句 codeNodeStack.pop(); break; case CGrammarInitializer.NAME_TO_NewName: node = ICodeFactory.createICodeNode(CTokenType.NEW_NAME); node.setAttribute(ICodeKey.TEXT, text); break; case CGrammarInitializer.OptSpecifiers_FunctDecl_CompoundStmt_TO_ExtDef: node = ICodeFactory.createICodeNode(CTokenType.EXT_DEF); node.addChild(codeNodeStack.pop()); node.addChild(codeNodeStack.pop()); funcMap.put(functionName, node); break; .... }}
由于我们现在是基于函数调用来解释C语言代码的,因此,当我们将一个函数的执行树构造出来后,需要把在执行树的头节点加入一个哈希表,表的关键字用的就是函数名,当某个函数被调用时,解释器会从该表中,通过被调用函数的名字,找到该函数对应的执行树的头节点,然后再根据该执行树进行解释执行相应节点,这样,我们就能实现函数调用了。
我们再看看函数的执行部分,我们根据执行树,增加了若干个Executor,对应于函数定义执行树的头节点,我们增加了ExtDefExecutor,代码如下:
package backend;import frontend.CGrammarInitializer;public class ExtDefExecutor extends BaseExecutor { @Override public Object Execute(ICodeNode root) { int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION); switch (production) { case CGrammarInitializer.OptSpecifiers_FunctDecl_CompoundStmt_TO_ExtDef: executeChild(root, 0); ICodeNode child = root.getChildren().get(0); String funcName = (String)child.getAttribute(ICodeKey.TEXT); root.setAttribute(ICodeKey.TEXT, funcName); executeChild(root, 1); child = root.getChildren().get(1); Object returnVal = child.getAttribute(ICodeKey.VALUE); if (returnVal != null) { root.setAttribute(ICodeKey.VALUE, returnVal); } break; } return root; }}
它的逻辑简单,由于它先执行FUNCT_DECL节点,然后在执行函数体,也就是大括号包住的部分。
我们再看看FUNCT_DECL节点对应的Executor:
public class FunctDeclExecutor extends BaseExecutor { @Override public Object Execute(ICodeNode root) { int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION); switch (production) { case CGrammarInitializer.NewName_LP_RP_TO_FunctDecl: root.reverseChildren(); copyChild(root, root.getChildren().get(0)); break; } return root; }}
该节点执行时,只是单纯的拷贝子节点信息,后面我们实现由参数传递的函数调用时,该节点将发挥重要作用。
由于函数调用对应的语法是:
UNARY -> UNARY LP RP
因此,UnaryExecutor也要做相应改动,代码如下:
public class UnaryNodeExecutor extends BaseExecutor{ @Override public Object Execute(ICodeNode root) { executeChildren(root); int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION); String text ; Symbol symbol; Object value; ICodeNode child; switch (production) { ... case CGrammarInitializer.Unary_LP_RP_TO_Unary: //先获得函数名 String funcName = (String)root.getChildren().get(0).getAttribute(ICodeKey.TEXT); //找到函数执行树头节点 ICodeNode func = CodeTreeBuilder.getCodeTreeBuilder().getFunctionNodeByName(funcName); if (func != null) { Executor executor = ExecutorFactory.getExecutorFactory().getExecutor(func); executor.Execute(func); } break; ... }
它首先找到要调用的函数名,利用函数名在函数哈希表中找到对应的执行树的头节点,根据头结点构造函数的执行节点ExtDefExecutor,然后调用该Executor的execute接口,于是ExtDefExecutor便开始变量函数f的执行树,在对应节点执行相应操作,从而实现函数f被调用的效果。
具体的代码讲解和调试演示请参看视频。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
- java实现C语言解释器:无参数传递的函数调用的解释和执行
- java实现C语言解释器:while 和 do while 循环的解释和执行
- java开发C语言解释器:间套结构体的解释和执行
- 关于C语言中函数调用和参数传递机制的探讨(一 .无参数传递)
- Java编程语言和Java指南中关于Java的参数传递的解释
- 参数传递的解释
- java开发C语言解释器:函数递归调用时的环境保护
- java 函数参数传递解释
- JAVA语言中编译执行和解释执行的区别?
- C语言编程中对于函数参数的一些解释?
- java开发C语言编译器: return 语句的解释和执行
- c中main函数的参数解释
- JAVA的解释执行
- C语言函数调用传递参数时的类型退化
- c/c++/java,函数调用的参数的传递方法
- 指针函数 的解释c语言
- 关于C语言中函数调用和参数传递机制的探讨(二 .传递一个参数)
- 关于C语言中函数调用和参数传递机制的探讨(三 .传递多个参数等)
- 位置服务
- bootstrap框架学习笔记五(其他部件)
- Linux系统的启动级别
- c语言文件读写
- hibernate主键自动生成及配置
- java实现C语言解释器:无参数传递的函数调用的解释和执行
- Pycharm开发spark程序
- Linux环境变量
- 米斯特白帽培训讲义 漏洞篇 逻辑漏洞
- Windows异常代码查询
- https加密通信过程图解
- jsp里做分页示例
- 底层存储变量的写时复制机制(copy on write)
- Python 第一天之配置cmd命令框环境