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》

这个编译器源代码是我原先为了完成编译原理实验课作业而的,所以只具有教学价值,现在发出来和大家共享 ;-)<br/><br/>和网上流传的版本不同,它从文法开始,一直做到了符号表的实现. 想实现自己的编译器的话,只需在把Initializtion.h中的文法修改为自己的即可.<br/><br/>工程结构:<br/>Initializtion.h 初始化文法,便于进一步进分析,它为构造GRAMMAR类提供了信息.其中默认非终极符用<>括上,修改时需要注意.<br/>Grammar.cpp Grammar.h 定义了文法GRAMMAR类,它通过initializtion.h的信息建立文法的内部表示。<br/>LL1_Analyser.cpp LL1_Analyser.h 定义了LL1分析器,即LL1_Analyser类.<br/>LL1_Recognizer.cpp LL1_Recognizer.h 为LL1语法分析驱动器,可以通过文法,TOKEN序列和LL1分析表,判定语法是否正确,同时驱动动作.<br/>Rec_Parse.cpp Rec_Pares.h 实现了递归下降分析器Rec_Parse类, 递归下降的思想和LL1驱动器一样,不过是把压栈改成调用自己,而把弹栈改成返回.<br/>Scanner.cpp Scanner.h 实现了词法分析器,可以将程序变为TOKEN序列. 扫描的源程序文件路径也在这里被定义(默认为.//demo.txt)<br/>Action.cpp Action.h 实现了语义栈的操作,_Action类定义了动作符号所对应的动作.<br/>SymTable.cpp SymTable.h 实现了符号表的建立和输出.<br/><br/>希望大家能通过该程序对STL和编译原理有更深刻的理解,Have Fun and Good Luck!<br/><br/> -- David.Morre
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值