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程序都不用修改,只用修改上面的文本文件就能够了,很是的方便,适合于结构已经固定,可是能够随意修改功能的场合。