用java写一个lisp 解释器

起初最早听到lisp这个名字是一个偶然的机会,留下了很牛的印象,时间匆匆五年就过去了,前些日子看sicp,里面又再次提到了这个名字,从网上找了几个入门文档学习了一下基础语法,便又继续看起了sicp;从写下第一行(+ 1 2)代码,日子转眼一个月就过去了,不得不说 lisp的前缀表达式的方式还是很不错的,不知怎得慢慢有了写一个lisp 解释器的想法,然后想到了之前王垠似乎写过一篇文章《怎样写一个解释器》 如果你对如何写一个解释器感兴趣可以看下这篇文章,还是有所启发的。

回来了吗?哈哈哈

我们继续旅途

写一个lisp解释器可以分三步:

1.将lisp表达式的字符串转换成一个树性结构的数组
2.解释这棵树
3.支持变量和方法调用

这里使用的语言是java

构造语法树

首先第一步:如何将lisp表达式的字符串转换成一个树性结构的数组?
我们看一几个lisp表达式 分析一下他的构成

(+ 1 2)
(+ 1 2 (- 3 4))
(+ 1 2 (- 3 4) 5)
(+ 1 2 (- 3 4) 5 (+ 6 7 (+ 8 9)))
(+ 1 2 (- 3 4) (+ 5 6 (+ 7 8)))
(+ 1 2 (- 3 4) (+ 5 6 (+ 7 8)) 9)

可以看到以上表达式里面可以分成两种元素:一个是不可分割的最小元素如 + - 1 2 3 这种 ,还有 (- 3 4)这种复合元素,而复合元素也是由最小的基础元素构成,于是我们得到了第一个规则(复合元素可以被拆分成更小的基础元素和复合元素)。
以(+ 1 2 (- 3 4) 5 (+ 6 7 (+ 8 9)))这个表达式为例,它如果是一棵树长什么样子呢?下面让我们画出它的形态:
用java写一个lisp 解释器_第1张图片
我们有了它的样子,但要如何将一个字符串形式的表达式转换成这样一棵树呢? 这是我们接下来要分析的问题
duang duang duang duang...
让我们回到我们的第一个规则 这里还有什么隐藏信息呢?
1.复合元素可拆分
2.基础元素不可拆分
3.复合元素是被“()”包裹的元素
有了这三项我们就可以在进一步的思考了,树树树,树的元素是什么?
1.节点
2.叶子节点
眉目,眉目,有了眉目
节点对应的是复合元素,基础元素对应的是叶子节点,那如何区分复合元素和基础元素呢?
“3.复合元素是被“()”包裹的元素”,是它,是它,就是它。
复合节点里的第一个元素是“(”后的第一个元素,最后一个元素是“)”前的第一个元素,我们又得到了第二个规则,有了上面两个规则我们开始构建我们的第一棵树:
代码:

node

class ExpNode implements Iterable{

   List data = new ArrayList<>();
   Node parent;

   public static ExpNode newInstance() {
        return new ExpNode();
   }

    public void add(Object o) {
        if (o instanceof ExpNode) {
            ((ExpNode) o).parent = this;
        }
        super.add(o);
    }

    public Iterator iterator() {
        return data.iterator();
    }
}

parse

public class Parse {

    public static ExpNode parseTree(String exp) {
        ExpNode root = ExpNode.newInstance();
        buildNode(trimStr(exp), 0, root);
        return (ExpNode) root.iterator().next();
    }

    private static void buildNode(String exp, int level, Cons node) {
        int lIndex = exp.indexOf("(");
        int rIndex = exp.indexOf(")");
        if (rIndex > -1) {
            if (lIndex < rIndex && lIndex > -1) {
                String subExp = exp.substring(lIndex, rIndex + 1);
                if (isBaseLeaf(exp)) {
                    ExpNode a = parseNode(subExp).orElseThrow(RuntimeException::new);
                    node.add(a);
                } else {
                    Optional nodeOptional = parseNode(subExp);
                    if (nodeOptional.isPresent()) {
                        ExpNode val = nodeOptional.get();
                        node.add(val);
                        node = val;
                    } else {
                        ExpNode objects = ExpNode.newInstance();
                        node.add(objects);
                        node = objects;
                    }
                }
                ++level;
                log.debug("{}{}---{}", createRepeatedStr(level), exp.substring(lIndex), subExp);
                buildNode(exp.substring(lIndex + 1), level, node);
            } else {
                //) b a (+ 8 9) => ) b a ( => b a
                if (lIndex > -1) {
                    String subExp = trimStr(exp.substring(rIndex + 1, lIndex));
                    if (subExp.length() > 0 && !subExp.contains(")")) {
                        String[] values = subExp.split(" ");
                        for (String val : values) {
                            node.parent().add(parseObj(val));
                        }
                    }
                } else {
                    // 所有都是后退
//                    ) b a) => b a) => b a
                    String subExp = exp.substring(rIndex + 1);
                    int r2Index = 1 + subExp.indexOf(")");
                    if (r2Index > 1) {
                        subExp = trimStr(subExp.substring(1, r2Index - 1));
                        if (subExp.length() > 0) {
                            String[] values = subExp.split(" ");
                            for (String val : values) {
                                node.parent().add(parseObj(val));
                            }
                        }
                    }
                }
                --level;
                log.debug("{}{}", createRepeatedStr(level), exp.substring(rIndex));
                buildNode(exp.substring(rIndex + 1), level, node.parent());
            }
        } else {
            log.debug(createRepeatedStr(level));
        }

    }

    private static Optional parseNode(String exp) {
        String subExp = "";
        if (isBaseLeaf(exp)) {
            //(xx [xx])
            subExp = exp.substring(1, exp.length() - 1);
        } else {
            // (xx [xx] (xx xx xx)
            // (xx [xx] (
            // ((
            subExp = exp.substring(1);
            subExp = subExp.substring(0, subExp.indexOf("("));
            if (subExp.trim().isEmpty()) {
                return Optional.empty();
            }
        }
        String[] keys = subExp.split(" ");
        ExpNode node = ExpNode.newInstance();
        for (int i = 0; i < keys.length; i++) {
            node.add(parseObj(keys[i]));
        }
        return Optional.of(node);
    }

    private static Object parseObj(String val) {
        try {
            return Integer.valueOf(val);
        } catch (NumberFormatException e) {
            if (val.equals("true") || val.equals("false")) {
                return Boolean.valueOf(val);
            } else if (val.indexOf("'") == 0 && val.lastIndexOf("'") == val.length() - 1) {
                return val.replaceAll("\"", "\"");
            } else {
                return Var.of(val);
            }
        }
    }

    private static boolean isBaseLeaf(String exp) {
        return count(exp, "\\(") == 1 && count(exp, "\\)") == 1 && exp.matches("^\\(.+?\\)$");
    }

    private static int count(String str, String regx) {
        Matcher matcher = Pattern.compile(regx).matcher(str);
        int i = 0;
        while (matcher.find()) {
            i++;
        }
        return i;
    }

    private static String trimStr(String str) {
        String tempStr = str.replace("  ", " ").trim();
        return tempStr.contains("  ") ? trimStr(tempStr) : tempStr;
    }

    private static String createRepeatedStr(int n) {
        return String.join("", Collections.nCopies(n, "--"));
    }
}

当然还有更简单的方式 哈哈哈,将“(”替换成“[”,将“)”转换成“]”,让后用json 解析器解析即可,哈哈哈哈

解释器

你可能感兴趣的:(lispjava)