天天看點

java簡單編譯器源代碼_25行代碼實作一個簡單的編譯器

起因

《25行JavaScript語句實作一個簡單的編譯器》實作的是一個簡單到不能再簡單的玩具的玩具,他的魔法是函數式程式設計簡化了js代碼。java 8提供了函數式程式設計的支援,昨晚腦子抽風突然興趣java也可以實作一個如此簡單的編譯器!

java和js語言差異

java相對js這類膠水語言來說還是相對啰嗦的,一些動态語言的特性在java裡并不具備。《25行JavaScript語句實作一個簡單的編譯器》的作者是個js高手js用得溜溜的,下面說說他用到js裡有而java沒有的功能。

js 字元串模闆

他在Transpiler中使用ES2015新增的模闆字元串功能。 `(${ast.expr.map(transpileNode).join(' ' + opMap[ast.val] + ' ')})`;

js内置 map和簡單的指派文法 const node = { val: consume(), type: Op, expr: [] };

其他膠水語言的話對應的是tuple,java要實作的話還真啰嗦不少。

模式比對(實際這是js的map啊啊啊) const opAcMap = {

'sum': args => args.reduce((a, b) => a + b, 0),

'sub': args => args.reduce((a, b) => a - b),

'div': args => args.reduce((a, b) => a / b),

'mul': args => args.reduce((a, b) => a * b, 1)

};

java還木有模式比對。

沒有這幾個js功能,但我們還是可以通過各種方法繞一下的。怎麼繞?請看下文!

java實作

廢話不啰嗦上代碼,代碼風格學他的也緊促點湊合着看吧!

static final int OP = 0, NUM = 1;

private static List lexer(String input){return Stream.of(input.split(" ")).map(String::trim).filter(s -> s.length() > 0).collect(Collectors.toList());}

private static class Parser {

Iterator lex;

String next=null;

public Parser(List lex) { this.lex=lex.iterator(); }

private Node parseOp(String str) {

Node n = new Node(str, OP);

while (lex.hasNext())n.addLast(parse());

return n;

}

public Node parse() { return (next=lex.next()).matches("\\d+") ? new Node(Integer.parseInt(next), NUM) : parseOp(next); }

}

final static Map opMap = new HashMap(4) {{ put("sum", "+"); put("sub", "-"); put("div", "/"); put("mul", "*");}};

private static String codeGenerator (Node ast) { return ast.type == NUM ? String.valueOf(ast.val) : genOp(ast); }

private static String genOp(Node node) { return "(" + node.stream().map(n -> codeGenerator(n)) .collect(Collectors.joining(" " + opMap.get(node.val) + " ")) + ")"; }

private static class Node extends ArrayDeque{

Object val;

int type;

public Node(Object val, int type) {

super();

this.val = val;

this.type = type;

}

}

private static int eval(Node ast) { return (int) (ast.type == NUM ? ast.val : ast.stream().reduce(evalOps.get(ast.val)).get().val); }

final static Map> evalOps=new HashMap>(4) {{

put("sum", (a, b) -> new Node(eval(a) + eval(b), NUM)); put("sub", (a, b) -> new Node(eval(a) - eval(b), NUM));

put("div", (a, b) -> new Node(eval(a) / eval(b), NUM)); put("mul", (a, b) -> new Node(eval(a) * eval(b), NUM));}};

js實作lex和transpile用了23行代碼。沒有tuple java實作node多花了9行代碼,加起來用了25行。不過他加eval功能的代碼行(33行)比我這(29行)可是多的。代碼行數多少是其次,函數式程式設計寫代碼還真精簡不少,寫的爽看得也不累。

寫在後

最後還是想說這個玩具的玩具。之是以說這個是玩具呢。

首先,他定的文法規則是非常簡單的。

其次,表面是一個乘除加減語言,但是沒有算術優先級。

最後,這跟什麼編譯器沒啥多大的關聯(詞法分析器用空格直接分割也隻能是玩泥沙),如果想寫個簡單解析器之類的可以參考我的《練手寫了個SQLite解析器》和《一個android sqlite CRUD代碼生成小工具》

本文源碼下載下傳移步github《tiny-compiler-java》