为我们的解释器构造一个调试器

作者: 王强儿 | 来源:发表于2016-07-07 07:12 被阅读136次
    为我们的解释器构造一个调试器

    说明

    现在的debugger支持最基本的两个命令,next step和watch variable。

    语言定义

    上一节实现了简单的解释器,语言的定义:

    程序:语句序列
    语句:语句序列|赋值语句(语句;)
    赋值语句:变量=表达式
    表达式:包含变量的四则运算

    源码示例:

    
    {
      number=2;
      a=number;
      b=10*a+10;
      c=a--b
    };
    x=11;
    y=x*10
    

    调试器使用

    package lsbasi;
    
    public class TestDebug {
        public static void main(String[] args) throws Exception {
            Lexer lexer = new Lexer("{number=2;a=number;b=10*a+10;c=a--b};x=11;y=x*10");
            Parser p = new Parser(lexer);
    
            Interpreter i = new Interpreter(p);
            i.debugger();
            System.out.println(i.GLOBAL_SCOPE);
    
        }
    }
    
    

    调试器实现

    package lsbasi;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Scanner;
    
    import lsbasi.ast.StatementAST;
    import lsbasi.visit.NodeVisitor;
    
    public class Debugger {
        Scanner s = new Scanner(System.in);
        private Map<String, Integer> scope;
        private List<StatementAST> list;
        private int index;
    
        public Debugger(List<StatementAST> list, Map<String, Integer> scope) {
            this.list = list;
            this.scope = scope;
            this.index = 0;
        }
    
        public void help() {
            System.out.println("h:help");
            System.out.println("n:next");
            System.out.println("w:watch");
        }
    
        public String waitUser() {
            return s.next();
        }
    
        public void debugger() throws Exception {
            help();
            while (index < list.size() ) {
                switch (waitUser()) {
                case "n":
                    NodeVisitor.visit(list.get(index), scope);
                    index++;
                    break;
                case "h":
                    help();
                    break;
                case "w":
                    System.out.println(scope);
                    break;
                default:
                    break;
                }
            }
    
        }
    
    }
    
    

    完整的代码

    值得改进的地方当然很多了,例如调试器功能不完整,这个要考虑的扩展性的话,需要拆分代码。让每种调试功能成为一个实例,在需要的时候进行注册,组合子对象模式真的好像不错。

    后记

    相信很多人对调试器没有任何兴趣,但是据我所知很多年轻人编程几乎离不开调试器,比如我,这当然是很受高手鄙视的。

    我一直在想调试器的工作原理是什么样的,有什么好处和坏处,怎么可以摆脱调试器束缚,让我也成为高手一下,而不是遇到问题就下断点,以前觉得这是必选项。当我听说,仅仅是听说,有人可以一次将代码写对的时候,我彻底改变了以前的想法,觉得自己的做法太low了。我想要离开调试器的话,我们必须对语言很熟悉,对正在操作的数据很熟悉,有一些辅助日志帮助,把逻辑设计的天衣无缝,一口气全写对。如果有一口气全写对的,请联系我,让我见识一下。

    调试器的哲学是让计算的世界停下来,慢下来,让我们仔细思考我们的代码,看到变量值和当初考虑不完全的地方,有时候这种错误很难捕捉,即使最好的调试器也经不起拙略程序员的莽撞行为。我想另一个方法是我们可以让自己慢下来,来应对程序的部分错误。

    但是很多时候我们接手不熟悉的代码,甚至得在线定位,或者对完整的代码不熟悉,这时候就得用调试器碰碰运气了。当然终极杀招似乎还是类似alert print之类的输出。

    对于初学者调试器是种逆天的功能,可以让人对代码每一步有直观了解,弥补了对细节掌控的不足。但是任何牛皮的工具都不会替猿类的懒惰和不思进取买单,如果你不明白你自己正在干什么,做的再好都是垃圾。

    相关文章

      网友评论

        本文标题:为我们的解释器构造一个调试器

        本文链接:https://www.haomeiwen.com/subject/hdzkjttx.html