25行代码实现一个简单的编译器
2017-11-06 14:40
489 查看
起因
《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<String> lexer(String input){return Stream.of(input.split(" ")).map(String::trim).filter(s -> s.length() > 0).collect(Collectors.toList());} private static class Parser { Iterator<String> lex; String next=null; public Parser(List<String> 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<String, String> opMap = new HashMap<String, String>(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<Node>{ 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<String,BinaryOperator<Node>> evalOps=new HashMap<String,BinaryOperator<Node>>(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》
相关文章推荐
- 用25行JavaScript语句实现一个简单的编译器
- 用25行JavaScript语句实现一个简单的编译器(转)
- 一个简单的AJAX实现,基于C#的ASP.Net,包括服务器端的程序代码
- 一个非常简单的opencv实现bmp,jpeg格式转化的代码
- 一个简单词法分析器的实现代码
- 一个简单的JavaScript数据缓存系统实现代码
- 一个超简单的JS拖拽实现代码(兼容IE,Firefox)
- 深入浅出编译原理-6-一个完整的编译器前端的代码实现
- 50行代码实现的一个最简单的基于 DirectShow 的视频播放器
- 一个简单的JavaScript数据缓存系统实现代码
- 50行代码实现的一个最简单的基于 DirectShow 的视频播放器
- 分形介绍 && 一个简单的Kotch curve实现代码
- python通过Tkinter库实现的一个简单的文本编辑器代码
- node.js 一个简单的页面输出实现代码
- 简单代码实现C#中运行另外一个程序
- Linux C++ 一个线程池的简单实现(附代码)
- [每天一个demo]用c#实现简单的xml操作代码
- 关于 数据源 导出excel (这是) 通过 画一个html 实现的、最简单、好理解、的代码、
- 一个简单T9输入法的实现(有代码)
- Linux C++ 一个线程池的简单实现(附代码)