1、前言java
這是咱們23個設計模式中最後一個設計模式了,你們或許也沒想到吧,居然是編譯原理上的編譯器,這樣說可能不對,由於編譯器分爲幾個部分組成呢,好比詞法分析器、語法分析器、語義分析器、中間代碼優化器以及最終的最終代碼生成器。而這個解釋器其實就是完成了對語法的解析,將一個個的詞組解釋成了一個個語法範疇,以後拿來使用而已。node
爲何會有這個解釋器模式呢,我想這是從編譯原理中受到啓發的,使用了這樣的一個解釋器能夠在Java語言之上在定義一層語言,這種語言經過Java編寫的解釋器能夠放到Java環境中去執行,這樣若是用戶的需求發生變化,好比打算作其餘事情的時候,只用在本身定義的新的語言上進行修改,對於Java編寫的代碼不須要進行任何的修改就能在Java環境中運行,這是很是有用的。這就好像,雖然無論怎麼編譯,最終由中間代碼生成最終代碼(機器碼)是依賴於相應的機器的。可是編譯器卻能理解高級語言和低級語言,不管高級語言的程序是怎麼樣編寫的,編譯器的代碼是不用修改的,而解釋器模式就是想作一個創建在Java和咱們自定義語言之間的編譯器。設計模式
2、代碼優化
本程序使用自頂向下文法來解析源程序:spa
首先是文法的定義:.net
<program> -> program <Command List> <Command List> -> <Command>* end <Command> -> <Repeat Command> | <Primitive Command> <Repeat Command> -> repeat <number> <Command List> <Primitive Command> -> go | right | left
由此能夠生成一顆語法樹。設計
而後使用自頂向下文法生成這樣的語法樹,自頂向下文法從根節點開始,不斷的向下解析,遇到一個語法範疇就嘗試着本身的定義去解析,直至解析到相應的程序,這裏要注意二義性問題,不能嘗試兩種解析方式都能對源程序解析成功;在實現的時候將一個語法範疇定義爲一個類,而後不斷地遞歸的去解析,直至到了葉子節點,將全部的單詞解析完畢。code
Node抽象類:orm
package zyr.dp.interpreter; public abstract class Node { public abstract void parse(Context context) throws ParseException; }
ProgramNode:起始節點 <program> -> program <Command List>blog
package zyr.dp.interpreter; public class ProgramNode extends Node { private Node commandListNode; public void parse(Context context) throws ParseException { context.skipToken("program"); commandListNode=new CommandListNode(); commandListNode.parse(context); } public String toString(){ return "[program "+commandListNode+"]"; } }
CommandListNode類: <Command List> -> <Command>* end
package zyr.dp.interpreter; import java.util.ArrayList; public class CommandListNode extends Node { private ArrayList list=new ArrayList(); public void parse(Context context) throws ParseException { while(true){ if(context.getCurrentToken()==null){ throw new ParseException("錯誤!!!"+"當前字符爲空"); }else if(context.getCurrentToken().equals("end")){ context.skipToken("end"); break; }else{ Node commandNode=new CommandNode(); commandNode.parse(context); list.add(commandNode); } } } public String toString(){ return list.toString(); } }
CommandNode類: <Command> -> <Repeat Command> | <Primitive Command>
package zyr.dp.interpreter; public class CommandNode extends Node { private Node node; public void parse(Context context) throws ParseException { if(context.getCurrentToken().equals("repeat")){ node = new RepeatCommandNode(); node.parse(context); }else{ node = new PrimitiveCommandNode(); node.parse(context); } } public String toString(){ return node.toString(); } }
ReapeatCommandNode類:
package zyr.dp.interpreter; public class RepeatCommandNode extends Node { private int number; private Node commandListNode; public void parse(Context context) throws ParseException { context.skipToken("repeat"); number=context.currentNumber(); context.nextToken(); commandListNode=new CommandListNode(); commandListNode.parse(context); } public String toString(){ return "[repeat "+number+" "+commandListNode+"]"; } }
PrimitiveCommandNode類:<Primitive Command> -> go | right | left
package zyr.dp.interpreter; public class PrimitiveCommandNode extends Node { String name; public void parse(Context context) throws ParseException { name=context.getCurrentToken(); context.skipToken(name); if(!name.equals("go") && !name.equals("left") && !name.equals("right") ){ throw new ParseException("錯誤!!!非法字符:"+name); } } public String toString(){ return name; } }
ParseException類:
package zyr.dp.interpreter; public class ParseException extends Exception { private static final long serialVersionUID = 3996163326179443976L; public ParseException(String word){ super(word); } }
Context類,承載了詞法分析的職責,爲上面的語法樹提供單詞,遍歷程序。
package zyr.dp.interpreter; import java.util.StringTokenizer; public class Context { private StringTokenizer tokenizer ; private String currentToken; public Context(String token){ tokenizer=new StringTokenizer(token); nextToken(); } public String nextToken() { if(tokenizer.hasMoreTokens()){ currentToken=tokenizer.nextToken(); }else{ currentToken=null; } return currentToken; } public String getCurrentToken(){ return currentToken; } public void skipToken(String token) throws ParseException{ if(!token.equals(currentToken)){ throw new ParseException("錯誤!!!"+"期待"+currentToken+"可是卻獲得"+token); } nextToken(); } public int currentNumber() throws ParseException{ int num=0; try{ num=Integer.parseInt(currentToken); }catch(NumberFormatException e){ throw new ParseException(e.toString()); } return num; } }
Main類,讀取用戶編寫的程序而且執行詞法分析和語法分析。這裏的詞法分析就是簡單的遍歷程序,語法分析採用的自頂向下的語法分析,對於上下文無關文法能夠檢測到語法錯誤,而且能生成語法範疇,可是這些語法範疇是咱們能看到的,不是及其最終能夠拿來去處理的,真正要編寫編譯系統,最好使用,自下而上的算符優先文法等方式來分析。
package zyr.dp.text; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import zyr.dp.interpreter.*; public class Main { public static void main(String[] args) { try { BufferedReader reader = new BufferedReader(new FileReader("program.txt")); String line=null; while((line=reader.readLine())!=null){ System.out.println("源程序爲:"+line); System.out.println("自頂向下解析爲:"); Node node=new ProgramNode(); node.parse(new Context(line)); System.out.println(node); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } } }
運行結果:
源程序:
在這裏我專門寫錯了一個源程序:
program end program go end program go right go right go right go right go right go right end program repeat 4 go right end end program repeat 4 repeat 3 go right end go right end end program repeat 4 go right end
能夠看到編譯器檢測到了語法錯誤,對於語法正確的,也形式化的生成了本身的分析結果,使用[ ]括起來的就是語法範疇了,造成層次遞歸嵌套結構。
3、總結
最後的設計模式是解釋器模式,在Java這種高級語言之上再次定義一種語言的編譯器,而後在不改動這個編譯器的條件下,也就是不改變Java代碼就可以隨意的書寫更高級的代碼,而後執行。在這種模式之下java程序都不用修改,只用修改上面的文本文件就能夠了,很是的方便,適合於結構已經固定,可是能夠隨意修改功能的場合。