設計模式:解釋器(Interpreter)模式


設計模式:解釋器(Interpreter)模式

一、前言

    這是我們23個設計模式中最后一個設計模式了,大家或許也沒想到吧,竟然是編譯原理上的編譯器,這樣說可能不對,因為編譯器分為幾個部分組成呢,比如詞法分析器、語法分析器、語義分析器、中間代碼優化器以及最終的最終代碼生成器。而這個解釋器其實就是完成了對語法的解析,將一個個的詞組解釋成了一個個語法范疇,之后拿來使用而已。

   為什么會有這個解釋器模式呢,我想這是從編譯原理中受到啟發的,使用了這樣的一個解釋器可以在Java語言之上在定義一層語言,這種語言通過Java編寫的解釋器可以放到Java環境中去執行,這樣如果用戶的需求發生變化,比如打算做其他事情的時候,只用在自己定義的新的語言上進行修改,對於Java編寫的代碼不需要進行任何的修改就能在Java環境中運行,這是非常有用的。這就好像,雖然不管怎么編譯,最終由中間代碼生成最終代碼(機器碼)是依賴於相應的機器的。但是編譯器卻能理解高級語言和低級語言,無論高級語言的程序是怎么樣編寫的編譯器的代碼是不用修改的而解釋器模式就是想做一個建立在Java和我們自定義語言之間的編譯器

 

二、代碼

   本程序使用自頂向下文法來解析源程序:

   首先是文法的定義:

1 <program> -> program <Command List> 2 3 <Command List> -> <Command>* end 4 5 <Command> -> <Repeat Command> | <Primitive Command> 6 7 <Repeat Command> -> repeat <number> <Command List> 8 9 <Primitive Command> -> go | right | left

    由此可以生成一顆語法樹。

    然后使用自頂向下文法生成這樣的語法樹,自頂向下文法從根節點開始,不斷的向下解析,遇到一個語法范疇就嘗試着自己的定義去解析,直至解析到相應的程序,這里要注意二義性問題,不能嘗試兩種解析方式都能對源程序解析成功;在實現的時候將一個語法范疇定義為一個類,然后不斷地遞歸的去解析,直至到了葉子節點,將所有的單詞解析完畢。

 Node抽象類:

1 package zyr.dp.interpreter;
2 
3 public abstract class Node {
4     public abstract void parse(Context context) throws ParseException;
5 }

 ProgramNode:起始節點  <program> -> program <Command List>

 1 package zyr.dp.interpreter;
 2 
 3 public class ProgramNode extends Node {
 4 
 5     private Node commandListNode;
 6     public void parse(Context context) throws ParseException {
 7         context.skipToken("program");
 8         commandListNode=new CommandListNode();
 9         commandListNode.parse(context);
10     }
11     public String toString(){
12         return "[program "+commandListNode+"]";
13     }
14 
15 }

  CommandListNode類: <Command List> -> <Command>* end

 1 package zyr.dp.interpreter;
 2 
 3 import java.util.ArrayList;
 4 
 5 public class CommandListNode extends Node {
 6 
 7     private ArrayList list=new ArrayList();
 8     
 9     public void parse(Context context) throws ParseException {
10         while(true){
11             if(context.getCurrentToken()==null){
12                 throw new ParseException("錯誤!!!"+"當前字符為空");
13             }else if(context.getCurrentToken().equals("end")){
14                 context.skipToken("end");
15                 break;
16             }else{
17                 Node commandNode=new CommandNode();
18                 commandNode.parse(context);
19                 list.add(commandNode);
20             }
21         }
22     }
23     
24     public String toString(){
25         return list.toString();
26     }
27 
28 }

 CommandNode類: <Command> -> <Repeat Command> | <Primitive Command>

 1 package zyr.dp.interpreter;
 2 
 3 public class CommandNode extends Node {
 4 
 5     private Node node;
 6     public void parse(Context context) throws ParseException {
 7         if(context.getCurrentToken().equals("repeat")){
 8             node = new RepeatCommandNode();
 9             node.parse(context);
10         }else{
11             node = new PrimitiveCommandNode();
12             node.parse(context);
13         }
14     }
15     
16     public String toString(){
17         return node.toString();
18     }
19 
20 }
RepeatCommandNode 類:<Repeat Command> -> repeat <number> <Command List>
 1 package zyr.dp.interpreter;
 2 
 3 public class RepeatCommandNode extends Node {
 4 
 5     private int number;
 6     private Node commandListNode;
 7     public void parse(Context context) throws ParseException {
 8         context.skipToken("repeat");
 9         number=context.currentNumber();
10         context.nextToken();
11         commandListNode=new CommandListNode();
12         commandListNode.parse(context);
13     }
14     public String toString(){
15         return "[repeat "+number+"  "+commandListNode+"]";
16     }
17 
18 }

 PrimitiveCommandNode類:<Primitive Command> -> go | right | left

 1 package zyr.dp.interpreter;
 2 
 3 public class PrimitiveCommandNode extends Node {
 4 
 5     String name;
 6     public void parse(Context context) throws ParseException {
 7         name=context.getCurrentToken();
 8         context.skipToken(name);
 9         if(!name.equals("go") && !name.equals("left") && !name.equals("right") ){
10             throw new ParseException("錯誤!!!非法字符:"+name);
11         }
12     }
13 
14     public String toString(){
15         return name;
16     }
17 }

 ParseException類:

 1 package zyr.dp.interpreter;
 2 
 3 public class ParseException extends Exception {
 4 
 5     private static final long serialVersionUID = 3996163326179443976L;
 6 
 7     public ParseException(String word){
 8         super(word);
 9     }
10 
11 }
Context 類,承載了詞法分析的職責,為上面的語法樹提供單詞,遍歷程序,當然沒考慮到程序的注釋等處理信息。
 1 package zyr.dp.interpreter;
 2 
 3 import java.util.StringTokenizer;
 4 
 5 public class Context {
 6 
 7     private StringTokenizer tokenizer ;
 8     private String currentToken;
 9     public Context(String token){
10         tokenizer=new StringTokenizer(token);
11         nextToken();
12     }
13     public String nextToken() {
14         if(tokenizer.hasMoreTokens()){
15             currentToken=tokenizer.nextToken();
16         }else{
17             currentToken=null;
18         }
19         return currentToken;
20     }
21     public String getCurrentToken(){
22         return currentToken;
23     }
24     public void skipToken(String token) throws ParseException{
25         if(!token.equals(currentToken)){
26             throw new ParseException("錯誤!!!"+"期待"+currentToken+"但是卻得到"+token);
27         }
28         nextToken();
29     }
30     public int currentNumber() throws ParseException{
31         int num=0;
32         try{
33             num=Integer.parseInt(currentToken);
34         }catch(NumberFormatException e){
35             throw new ParseException(e.toString());
36         }
37         return num;
38     }
39     
40 }

    Main類,讀取用戶編寫的程序並且執行詞法分析和語法分析。這里的詞法分析就是簡單的遍歷程序,語法分析采用的自頂向下的語法分析,對於上下文無關文法可以檢測到語法錯誤,並且能生成語法范疇,但是這些語法范疇是我們能看到的,不是及其最終可以拿來去處理的,真正要編寫編譯系統,最好使用,自下而上的算符優先文法等方式來分析。

 1 package zyr.dp.text;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileReader;
 6 import java.io.IOException;
 7 
 8 import zyr.dp.interpreter.*;
 9 
10 public class Main {
11 
12     public static void main(String[] args) {
13         
14         try {
15             BufferedReader  reader = new BufferedReader(new FileReader("program.txt"));
16             String line=null;
17             while((line=reader.readLine())!=null){
18                 System.out.println("源程序為:"+line);
19                 System.out.println("自頂向下解析為:");
20                 Node node=new ProgramNode();
21                 node.parse(new Context(line));
22                 System.out.println(node);
23             }
24         } catch (FileNotFoundException e) {
25             e.printStackTrace();
26         } catch (IOException e) {
27             e.printStackTrace();
28         } catch (ParseException e) {
29             e.printStackTrace();
30         }
31         
32     }
33 
34 }

 運行結果:

  源程序:

  在這里我專門寫錯了一個源程序:

1 program end
2 program go end
3 program go right  go right  go right  go right  go right  go right  end
4 program repeat 4 go right end end
5 program repeat 4 repeat 3  go right end go right end end
6 program repeat 4 go right end

    可以看到編譯器檢測到了語法錯誤,對於語法正確的,也形式化的生成了自己的分析結果,使用[ ]括起來的就是語法范疇了,形成層次遞歸嵌套結構。

三、總結

    最后的設計模式是解釋器模式,在Java這種高級語言之上再次定義一種語言的編譯器,然后在不改動這個編譯器的條件下,也就是不改變Java代碼就能夠隨意的書寫更高級的代碼,然后執行。在這種模式之下java程序都不用修改,只用修改上面的文本文件就可以了,非常的方便,適合於結構已經固定,但是可以隨意修改功能的場合。

  程序代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM