25行代碼實現一個簡單的編譯器

原由


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

java和js語言差別
    

    java相對js這類膠水語言來講仍是相對囉嗦的,一些動態語言的特性在java裏並不具有。《25行JavaScript語句實現一個簡單的編譯器》的做者是個js高手js用得溜溜的,下面說說他用到js裏有而java沒有的功能。java

  1. js 字符串模板
    他在Transpiler中使用ES2015新增的模板字符串功能。node

    `(${ast.expr.map(transpileNode).join(' ' + opMap[ast.val] + ' ')})`;

     

  2. js內置 map和簡單的賦值語法android

    const node = { val: consume(), type: Op, expr: [] };

    其餘膠水語言的話對應的是tuple,java要實現的話還真囉嗦很多。
     git

  3. 模式匹配(實際這是js的map啊啊啊)github

    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功能,但咱們仍是能夠經過各類方法繞一下的。怎麼繞?請看下文!sql

 

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函數

相關文章
相關標籤/搜索