从计算机执行过程角度思考,函数调用的尤为重要,面向过程的语言更是将函数调用作为最基本的要素。面向对象的语言也是基于最基本的函数调用实现的。因此本文就从函数调用开始,由浅入深地思考如何构建我们的编译器。
1. 一个函数调用和其子调用之间的结构图。
一个SuperFunction对象 包含很多SubFunction函数,SubFunction 是SuperFunction的子函数。因此我们使用组合模式将他们组合起来。
2. 父函数SuperFunction与子函数SubFunction的交互图。
3. 执行引擎:
执行引擎负责创建所有的FunctionObject,连接他们之间的父子关系,进行入栈,出栈。假如这个栈向下增长,那么下图就表示了一个栈的动态结构图。
4. 执行引擎算法设计(java 语言描述)
任意一个如下的函数:
Object function(Object[] parameters);
以上函数用结构体进行描述如下
FunctionObject extends CodeBlock{ Object[] parameters; Object result; List<Codeblock> codeBlocks; }
类图如下
则执行引擎的执行算法如下:
stack.push(mainfunction); execute point: do { FunctionObject functionObject = stack.peek(); while(functionObject.codeBlocks.size()>0){ CodeBlock codeBlock = functionObject .codeBlocks.top(); If(subFunctionCall){ create subfunction is needed. stack.push(subfunction); goto executePoint; }else{ codeBlock.execute(); codeBlocks.remove(codeBlock); } } stack.pop(); }while(!stack.isEmpty())
5. 编译器,编译器实际上就是把源代码转换为执行引擎能执行的代码的过程。这个过程, 现代编译器一般情况不会直接转换到目标代码。而是有个中间过程,也就所谓的语法分析和语义分析。
我们的analyse 类似如下函数:
FunctionObject anayliyse(String );
6. 有了语法和语义分析,然后我们就可以很容易的转换到目标代码
String transferLanguage(FunctionObject functionObject);
最终形成目标代码。
完毕。