Write a lisp interpreter in java

Posted by icd_lx on Mon, 07 Feb 2022 03:08:24 +0100

At first, the first time I heard the name LISP was an accidental opportunity and left a great impression. Five years passed in a hurry. I saw sicp a few days ago and mentioned the name again. I found several introductory documents on the Internet, learned the basic grammar, and then continued to watch sicp; From writing the first line (+ 1, 2) code, the days passed in a month. I have to say that the prefix expression of LISP is still very good. Somehow, I slowly came up with the idea of writing a lisp interpreter, and then I thought that Wang Yin seemed to have written an article before How to write an interpreter If you are interested in how to write an interpreter, you can see this article, which is enlightening.

Are you back? Ha ha ha

We continued our journey

Writing a lisp interpreter can be divided into three steps:

1. Convert the string of lisp expression into an array of tree structure
2. Explain the tree
3. Support variables and method calls

The language used here is java

Construct syntax tree

First step: how to convert the string of lisp expression into an array of tree structure?
Let's take a look at some lisp expressions and analyze their composition

(+ 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)

It can be seen that the above expression can be divided into two kinds of elements: one is the smallest indivisible element, such as + - 1,2,3, and (- 3,4), and the composite element is also composed of the smallest basic element, so we get the first rule (the composite element can be divided into smaller basic elements and composite elements).
Take the expression (+ 1 2 (- 3 4) 5 (+ 6 7 (+ 8 9))) as an example. What does it look like if it is a tree? Now let's draw its form:

We have its appearance, but how to convert an expression in string form into such a tree? This is the problem we will analyze next
duang duang duang duang...
Let's go back to our first rule. What's the hidden information here?
1. Composite elements can be split
2. The basic element cannot be split
3. Composite elements are elements wrapped by "()"
With these three items, we can think further. What are the elements of a tree?
1. Node
2. Leaf node
Eyebrows, eyebrows, eyebrows
Nodes correspond to composite elements and basic elements correspond to leaf nodes. How to distinguish between composite elements and basic elements?
"3. Composite element is the element wrapped by" () ". It is it, it is it, it is it.
The first element in the composite node is the first element after "(", and the last element is the first element before "). We get the second rule. With the above two rules, we start to build our first tree:
code:

node

class ExpNode implements Iterable<Object>{

   List<Object> 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<Object> 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<ExpNode> 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 {
                    // Everything is backwards
//                    ) 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<ExpNode> 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, "--"));
    }
}

Replace "[, haha]" with "[, haha]", of course

interpreter

Topics: Java lisp