java开发C解释器:实现解释器内部模块间的通讯

本文介绍了解决C语言编译器中结构体指针未分配内存直接访问问题的方法,通过引入观察者模式实现在不同解释器模块间的通讯,确保在执行赋值等操作前后能通知相关模块。

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

我们完成结构体的解析执行后,当前的解释器已经相对完善了,如果不考虑性能因素,当前解释器能够解释执行很多C语言程序,但当前解释器还有一些问题,我们看下面这段C程序:

void main() {
    struct TAG {
        char c;
        int p;
        int arr[3];
    }tag;

    struct TAG* pTag;
    pTag->p = 400;
    printf("value of p in struct pTag is : %d", pTag->p);
}

上面程序被我们的解释器执行后,printf语句把pTag->p 的值输出为400,这显然是有问题的,因为pTag是个结构体指针,在没有为其分配动态内存的情况下,直接对成员变量进行赋值访问是错误的。接下来的几节,我们将致力于解决
这个问题。

要解决这个问题,我们需要实现的一点是,在解释器内部的各个执行模块间实现相互通讯。我们知道,不同的C语言语句,是由不同的Executor来执行的。这些
Executor之间互不关联,但随着解释器功能的增强,我们需要它们之间实现相互通讯。例如当执行赋值语句的执行器在执行前和后执行后需要发出消息,让其他执行器知道,以便让解释器的其他模块有机会做相应的操作。

我们将使用观察者模式来搭建联通不同解释器之间的桥梁,我们先看看这个“桥梁”的接口定义:

public interface IExecutorBrocaster {
    public void brocastBeforeExecution(ICodeNode node);
    public void brocastAfterExecution(ICodeNode node);
    public void registerReceiverForBeforeExe(IExecutorReceiver receiver);
    public void registerReceiverForAfterExe(IExecutorReceiver receiver);
    public void removeReceiver(IExecutorReceiver receiver);
}

当有某个Executor想通知其他模块相关信息时,例如在执行之前要通知其他模块时,可以调用brocastBeforeExecution 接口,如果在执行之后想通知其他模块,那么就可以调用brocastAfterExecution接口。

如果解释器中某个模块想接收其他模块发送的消息,它可以实现
IExecutorReceiver接口,然后调用接口registerReceiverForBeforeExe 和
registerReceiverForAfterExe , 前者接收executor在执行前的消息,后者接收executor在执行之后的消息。

我们看看它的实现:

package backend;
import java.util.ArrayList;
import java.util.List;


public class ExecutorBrocasterImpl implements IExecutorBrocaster{
    private List<IExecutorReceiver> beforeExecutionReceiver = new ArrayList<IExecutorReceiver>();
    private List<IExecutorReceiver> afterExecutionReceiver = new ArrayList<IExecutorReceiver>();
    private static ExecutorBrocasterImpl instance = null;
    public static IExecutorBrocaster getInstance() {
        if (instance == null) {
            instance = new ExecutorBrocasterImpl();
        }

        return instance;
    }

    private ExecutorBrocasterImpl() {

    }
    @Override
    public void brocastBeforeExecution(ICodeNode node) {
        notifyReceivers(beforeExecutionReceiver, node);
    }

    @Override
    public void brocastAfterExecution(ICodeNode node) {
        notifyReceivers(afterExecutionReceiver, node);
    }

    @Override
    public void registerReceiverForBeforeExe(IExecutorReceiver receiver) {
        if (beforeExecutionReceiver.contains(receiver) == false) {
            beforeExecutionReceiver.add(receiver);
        }
    }

    @Override
    public void registerReceiverForAfterExe(IExecutorReceiver receiver) {
        if (afterExecutionReceiver.contains(receiver) == false) {
            afterExecutionReceiver.add(receiver);
        }
    }

    private void notifyReceivers(List<IExecutorReceiver> receivers, ICodeNode node) {
        for (int i = 0; i < receivers.size(); i++) {
            receivers.get(i).handleExecutorMessage(node);
        }
    }

    @Override
    public void removeReceiver(IExecutorReceiver receiver) {
        if (beforeExecutionReceiver.contains(receiver)) {
            beforeExecutionReceiver.remove(receiver);
        }

        if (afterExecutionReceiver.contains(receiver)) {
            afterExecutionReceiver.remove(receiver);
        }

    }
}

它的实现逻辑比较简单,把接受者存储到相关队列中,如果有Executor发布执行前通知,那么该对象就把beforeExecutionReceiver队列中的对象全部调用IExecutorReceiver 的接口执行一遍。

如果有Executor发布执行后的消息,那么该对象就把afterExecutionReceiver队列中的对象,全部调用IExecutorReceiver的接口执行一遍。

在每个Executor在执行前和执行后,我们都让他们发出消息,以便其他模块及时处理,在BaseExecutor.java中做如下修改:

protected void executeChildren(ICodeNode root) {
        ExecutorFactory factory = ExecutorFactory.getExecutorFactory();
        root.reverseChildren();

        int i = 0;
        while (i < root.getChildren().size()) {

            if (continueExecute != true) {
                break;
            }

            ICodeNode child = root.getChildren().get(i);

            executorBrocaster.brocastBeforeExecution(child);

            Executor executor = factory.getExecutor(child);
            if (executor != null) {
                executor.Execute(child);    
            }
            else {
                System.err.println("Not suitable Executor found, node is: " + child.toString());
            }

            executorBrocaster.brocastAfterExecution(child);

            i++;
        }
    }

我们再看看IExecutorReceiver的实现:

package backend;


public interface IExecutorReceiver {
    public void handleExecutorMessage(ICodeNode code);
}

有了模块间的消息收发机制后,我们做个试验,让UnaryExecutor去接收NoCommandExecutor的相关信息。UnaryExecutor如果想要接收NoCommandExecutor执行后的信息,特别是接收变量赋值后的事件通知,
那么它需要实现IExecutorReceiver接口,然后把自己注册到ExecutorBrocasterImpl对象中。代码如下:

public class UnaryNodeExecutor extends BaseExecutor implements IExecutorReceiver{
....
@Override
    public Object Execute(ICodeNode root) {
        executeChildren(root);
        switch (production) {
        ....
        case CGrammarInitializer.Unary_StructOP_Name_TO_Unary:
            child = root.getChildren().get(0);
            String fieldName = (String)root.getAttribute(ICodeKey.TEXT);
            symbol = (Symbol)child.getAttribute(ICodeKey.SYMBOL);

            Symbol args = symbol.getArgList();
            while (args != null) {
                if (args.getName().equals(fieldName)) {
                    break;
                }

                args = args.getNextSymbol();
            }

            if (args == null) {
                System.err.println("access a filed not in struct object!");
                System.exit(1);
            }

            root.setAttribute(ICodeKey.SYMBOL, args);
            root.setAttribute(ICodeKey.VALUE, args.getValue());

            if (isSymbolStructPointer(symbol) == true) {
                structObjSymbol = symbol;
                monitorSymbol = args;
                ExecutorBrocasterImpl.getInstance().registerReceiverForAfterExe(this);
            }
            break;
        ....
        }
    }
@Override
    public void handleExecutorMessage(ICodeNode code) {
        int productNum = (Integer)code.getAttribute(ICodeKey.PRODUCTION);
        Object object = code.getAttribute(ICodeKey.SYMBOL);
        if(object == null || (object instanceof Symbol) == false) {
            return;
        }

        Symbol symbol = (Symbol)object;
        if (productNum == CGrammarInitializer.NoCommaExpr_Equal_NoCommaExpr_TO_NoCommaExpr
                && symbol == monitorSymbol) {
            System.out.println("UnaryNodeExecutor receive msg for assign execution");

        }
    }
....
}

当解释器解析到有关结构体的访问或赋值时,UnaryNodeExecutor会在ExecutorBrocasterImpl中注册它自己,准备监听赋值语句的执行,一旦赋值语句被NoCommanExecutor执行后,它就会得到通知,于是他的handleExecutorMessage就会被调用。此时UnaryNodeExecutor会检测当前
被赋值的变量是否是结构体成员,如果是,它就打印出一条语句。

有了执行组件间的消息通讯机制后,我们就可以着手解决前面所说的问题了。

更详细的讲解和代码调试演示过程,请参看视频。

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
这里写图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值