淺談Interpreter解釋器模式

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程序都不用修改,只用修改上面的文本文件就能夠了,很是的方便,適合於結構已經固定,可是能夠隨意修改功能的場合。

 

淺談設計模式<最通俗易懂的講解>

相關文章
相關標籤/搜索