java开发C语言编译器:实现库函数调用

更详细的讲解和代码调试演示过程,请参看视频
用java开发C语言编译器vue


咱们第一次使用C语言开发程序时,每每是在控制台上打印一句”Hello World”,实现打印语句功能的函数是printf, 这个函数是有C语言的连接库提供的,开发者能够直接调用,相似于这种无需本身实现,直接能够调用的函数,咱们都称为库函数,或是API, 本节,咱们要为当前构建的虚拟机提供C语言库函数,咱们要给解释器提供一种函数调用机制,这些函数无需程序本身实现,而是由咱们的解释器提供的,C语言程序直接调用便可,这节,咱们要实现的一个库函数就是printf. 完成本节代码后,咱们的解释器能解释执行下面程序:java

void main() {
printf("a is %d:",1);
}

printf函数是咱们解释器提供给代码的,有了库函数,程序的开发便能高效不少。api

库函数机制的实现由新构造的类ClibCall,咱们先看看它的实现代码:微信

package backend;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class ClibCall {
    private Set<String> apiSet;

    private ClibCall() {
        apiSet = new HashSet<String>();
        apiSet.add("printf");
    }
    private static ClibCall instance = null;

    public static ClibCall getInstance() {

        if (instance == null) {
            instance = new ClibCall();
        }

        return instance;
    }

    public boolean isAPICall(String funcName) {
        return apiSet.contains(funcName);
    }

    public Object invokeAPI(String funcName) {
        switch (funcName) {

        case "printf":
            return handlePrintfCall();

        default:
            return null;
        }
    }

    private Object handlePrintfCall() {
        ArrayList<Object> argsList = FunctionArgumentList.getFunctionArgumentList().getFuncArgList(false);
        String argStr = (String)argsList.get(0);
        String formatStr = "";

        int i = 0;
        int argCount = 1;
        while (i < argStr.length()) {
            if (argStr.charAt(i) == '%' && i+1 < argStr.length() && 
                    argStr.charAt(i+1) == 'd') {
                i += 2;
                formatStr += argsList.get(argCount);
                argCount++;
            } else {
                formatStr += argStr.charAt(i);
                i++;
            }
        }

        System.out.println(formatStr);

        return null;
    }
}

ClibCall 包含了一个集合类叫apiSet, 其用于存储库函数的函数名,当代码中有函数调用时,解释器拿到被调函数的名字,提交给ClibCall, 该类会在apiSet中查找是否含有与给定名字相同的字符串,若是有,那意味着该函数属于库函数。markdown

因为目前咱们只实现了一个库函数printf, 所以初始化时,咱们把字符串”printf”加入到该集合中。 isAPICall 传入的是函数名,若是函数名包含在apiSet里面,那么返回true, 代表他是库函数调用。架构

invokeAPICall 用来执行给定的库函数,传入参数是库函数的名称。在里面,解释器根据不一样的库函数名称,去实现不一样的库函数。handlePrintfCall用于实现printf调用,首先,它得到输入参数,第一个输入参数是要显示到控制台上的字符串,在字符串中,可能会含有格式化字符,当前咱们实现的printf可接受的格式化字符是%d. 在printf的模拟实现中,咱们遍历每个字符,当遇到%d时,咱们从参数列表中得到对应的数值,而后把数值替换%d格式符,最后经过println把格式化后的字符串打印到控制台上。app

有了库函数调用后,每当解释器解析到函数调用是,须要肯定当前调用的函数是代码本身实现的,仍是库函数提供的,这个机制的实如今UnaryExecutor中,代码以下:ide

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_ARGS_RP_TO_Unary:
            //先得到函数名
            String funcName = (String)root.getChildren().get(0).getAttribute(ICodeKey.TEXT);
            if (production == CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary) {
                ICodeNode argsNode = root.getChildren().get(1);
                ArrayList<Object> argList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.VALUE);
                FunctionArgumentList.getFunctionArgumentList().setFuncArgList(argList);    
            }

            //找到函数执行树头节点
            ICodeNode func = CodeTreeBuilder.getCodeTreeBuilder().getFunctionNodeByName(funcName);
            if (func != null) {
                Executor executor = ExecutorFactory.getExecutorFactory().getExecutor(func);
                executor.Execute(func);
                Object returnVal = func.getAttribute(ICodeKey.VALUE);
                if (returnVal != null) {
                    System.out.println("function call with name " + funcName + " has return value that is " + returnVal.toString());
                    root.setAttribute(ICodeKey.VALUE, returnVal);
                }
            } else {
                ClibCall libCall = ClibCall.getInstance();
                if (libCall.isAPICall(funcName)) {
                    Object obj = libCall.invokeAPI(funcName);
                    root.setAttribute(ICodeKey.VALUE, obj);
                } 
            }
        ....
        }

当解释器解析函数调用时,它如今函数调用表中,查找给定函数的语法执行树头结点,若是找不到的话,那么解释器知道,这个函数是库函数,因而调用ClibCall来处理,它先经过isAPICall来判断,给定函数是不是库函数,若是是的话,则调用invokeAPI来执行库函数提供的功能。函数

有了ClibCall, 之后咱们想要添加新的库函数时,只要修改ClibCall的实现便可,基于如今的架构基础上,咱们从此能够快速的实现更多库函数,从而让咱们的解释器愈来愈强大!ui

更加具体的代码解释和调试过程请参看视频。


本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索