浅谈Interpreter解释器模式

一、前言

    这是我们23个设计模式中最后一个设计模式了,大家或许也没想到吧,竟然是编译原理上的编译器,这样说可能不对,因为编译器分为几个部分组成呢,比如词法分析器、语法分析器、语义分析器、中间代码优化器以及最终的最终代码生成器。而这个解释器其实就是完成了对语法的解析,将一个个的词组解释成了一个个语法范畴,之后拿来使用而已。

   为什么会有这个解释器模式呢,我想这是从编译原理中受到启发的,使用了这样的一个解释器可以在Java语言之上在定义一层语言,这种语言通过Java编写的解释器可以放到Java环境中去执行,这样如果用户的需求发生变化,比如打算做其他事情的时候,只用在自己定义的新的语言上进行修改,对于Java编写的代码不需要进行任何的修改就能在Java环境中运行,这是非常有用的。这就好像,虽然不管怎么编译,最终由中间代码生成最终代码(机器码)是依赖于相应的机器的。但是编译器却能理解高级语言和低级语言,无论高级语言的程序是怎么样编写的编译器的代码是不用修改的而解释器模式就是想做一个建立在Java和我们自定义语言之间的编译器

1157683-20180702153558850-2058301263.png

二、代码

1157683-20180702172926311-373866212.png

本程序使用自顶向下文法来解析源程序:

   首先是文法的定义:

<program> -> program <Command List>

<Command List> -> <Command>*   end

<Command> -> <Repeat Command> | <Primitive Command>

<Repeat Command> -> repeat <number> <Command List>

<Primitive Command> -> go | right | left

由此可以生成一颗语法树。

    然后使用自顶向下文法生成这样的语法树,自顶向下文法从根节点开始,不断的向下解析,遇到一个语法范畴就尝试着自己的定义去解析,直至解析到相应的程序,这里要注意二义性问题,不能尝试两种解析方式都能对源程序解析成功;在实现的时候将一个语法范畴定义为一个类,然后不断地递归的去解析,直至到了叶子节点,将所有的单词解析完毕。

 Node抽象类:

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

 ProgramNode:起始节点  <program> -> program <Command List>

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();
        }
        
    }

}

运行结果:

  源程序:

1157683-20180702170543210-1792664828.png

在这里我专门写错了一个源程序:

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

1157683-20180702170703244-1113700527.png

可以看到编译器检测到了语法错误,对于语法正确的,也形式化的生成了自己的分析结果,使用[ ]括起来的就是语法范畴了,形成层次递归嵌套结构。

三、总结

    最后的设计模式是解释器模式,在Java这种高级语言之上再次定义一种语言的编译器,然后在不改动这个编译器的条件下,也就是不改变Java代码就能够随意的书写更高级的代码,然后执行。在这种模式之下java程序都不用修改,只用修改上面的文本文件就可以了,非常的方便,适合于结构已经固定,但是可以随意修改功能的场合。

 

浅谈设计模式<最通俗易懂的讲解>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
解释器模式Interpreter Pattern)是一种行为型设计模式,它定义了一种语言文法的表示,并定义了一个解释器,用于解释语言中的句子。它将一个问题分成两个部分:一部分是语言的文法规则,另一部分是解释器,用来解释规则中的句子。解释器模式可以用于处理一些简单的语言,如数学表达式、正则表达式等。 实现方式: 1. 定义抽象表达式类(AbstractExpression),它是所有表达式类的父类,声明了抽象的解释方法。 2. 定义终结符表达式类(TerminalExpression),它实现了抽象表达式类中的解释方法,用于解释语言中的终结符。 3. 定义非终结符表达式类(NonterminalExpression),它也实现了抽象表达式类中的解释方法,用于解释语言中的非终结符。 4. 定义上下文类(Context),它包含了解释器需要的一些全局信息。 5. 客户端使用时,先创建一个上下文对象,然后将需要解释的语言句子作为参数传入解释器对象中,解释器对象将句子解释成相应的结果。 优点: 1. 可扩展性好,增加新的文法规则只需要添加相应的非终结符表达式类即可。 2. 易于实现语法分析。 缺点: 1. 对于复杂的文法规则,解释器模式的类数量可能会很大,增加程序的复杂性。 2. 执行效率较低,因为需要递归调用解释器对象。 适用场景: 1. 可以用于处理一些简单的语言,如数学表达式、正则表达式等。 2. 当语言的文法规则比较复杂时,可以使用解释器模式进行语法分析。 3. 当需要对语言进行增强时,可以使用解释器模式添加新的文法规则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值