begin 2023年04月15日16:49:35
本科软件工程专业有这么一门课叫《编译原理》,课程内容已经忘了七七八八,但尤为清楚的是上机大作业是拷贝的,课程分数92。
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
给定一个语言,定义其语法的表示以及一个用该表示来解释该语言中的句子的解释器。——《设计模式:可复用面向对象软件的基础》
解释器模式是一种行为型设计模式。
Use the Interpreter pattern when there is a language to interpret, and you can represent statements in the language as abstract syntax trees.The Interpreter pattern works best when
当有语言要解释时,请使用解释器模式,您可以将语言中的语句表示为抽象语法树。解释器模式在以下情况下效果最佳:
解释器模式结构图:
抽象表达式(AbstractExpression):
终结符表达式(TerminalExpression):
非终结符表达式(NonterminalExpression):
上下文(Context):
客户端(Client):
实现一个简单函数绘图语言解释器,解释下面代码:
rot is 0;
origin is (0, 0);
scale is (2,20);
for T from 1 to 300 step 1 draw (t,-ln(t));
scale is (20,0.1);
for T from 0 to 8 step 0.1 draw (t,exp(t));
scale is (2,1);
for T from 0 to 300 step 1 draw (t,0);
for T from 0 to 300 step 1 draw (0,t);
for T from 0 to 120 step 1 draw (t,t);
scale is (2,0.1);
for T from 0 to 55 step 1 draw (t,-(t*t));
scale is (10,5);
for T from 0 to 60 step 1 draw (t,sqrt(t));
当然,我们的解释器还需要具备识别注释、出错处理等基本功能,下面是我实现过程中遵循的一些小的原则:
解释器模式常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。
文法是用于描述语言的语法结构的形式规则。如:rot is 0;origin is (0, 0);scale is (2,20);
用文法表示:
<句子> -> <主语><谓语><表语>
<主语> -> <代词> | <名词>
<谓语> -> <动词>
<表语> -> <名词> | <形容词>
<名词> -> rot|origin|scale
<动词> -> is
<形容词> -> 0|(0,0)|(2,20)
注:这里的符号"->"表示“定义为”的意思,用"<"和">"括住的是非终结符,没有括住的是终结符。
句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出。
如,上面的文法可以推导出rot is 0;origin is (0, 0);scale is (2,20);
,所以这些都是句子。
语法树是句子结构的一种树型表示,它代表了句子的推导结果,它有利于理解句子语法结构的层次。
rot is 0;
的语法树如下:
本代码示例代码相对于本文来说过于冗长,下面带有有所省略,有兴趣请参考我的github:interpreter。
代码示例主要流程:
抽象表达式:
// 语法
public interface Grammar {
void interpret(Parser parser);
}
终结符表达式:
// 终结符类型
public enum TokenType {
ROT, IS, ORIGIN, SCALE, FOR, FROM, TO, STEP, DRAW, // 保留字
T, // 参数
SEMICOLON, COMMA, LEFT_BRACKET, RIGHT_BRACKET, // 分隔符
EOF, // 文件结束符
PLUS, MINUS, MUL, DIV, // 运算符
LN, EXP, SQRT, SIN, COS, POWER, // 函数
NUMBER // 数字
}
// 具体终结符
public class Token implements Grammar {
public static final Map<String, Token> TOKEN_MAP;
static {
Map<String, Token> tokenMap = new HashMap<>();
tokenMap.put("ROT", new Token(TokenType.ROT, "ROT"));
tokenMap.put("IS", new Token(TokenType.IS, "IS"));
tokenMap.put("ORIGIN", new Token(TokenType.ORIGIN, "ORIGIN"));
tokenMap.put("SCALE", new Token(TokenType.SCALE, "SCALE"));
tokenMap.put("FOR", new Token(TokenType.FOR, "FOR"));
tokenMap.put("FROM", new Token(TokenType.FROM, "FROM"));
tokenMap.put("TO", new Token(TokenType.TO, "TO"));
tokenMap.put("STEP", new Token(TokenType.STEP, "STEP"));
tokenMap.put("DRAW", new Token(TokenType.DRAW, "DRAW"));
tokenMap.put(";", new Token(TokenType.SEMICOLON, ";"));
tokenMap.put("(", new Token(TokenType.LEFT_BRACKET, "("));
tokenMap.put(")", new Token(TokenType.RIGHT_BRACKET, ")"));
tokenMap.put(",", new Token(TokenType.COMMA, ","));
tokenMap.put("NUMBER", new Token(TokenType.NUMBER, null));
tokenMap.put("EOF", new Token(TokenType.EOF, null));
tokenMap.put("T", new Token(TokenType.T, "T"));
tokenMap.put("+", new Token(TokenType.PLUS, "+"));
tokenMap.put("-", new Token(TokenType.MINUS, "-"));
tokenMap.put("*", new Token(TokenType.MUL, "*"));
tokenMap.put("/", new Token(TokenType.DIV, "/"));
tokenMap.put("LN", new Token(TokenType.LN, null));
tokenMap.put("EXP", new Token(TokenType.EXP, null));
tokenMap.put("SQRT", new Token(TokenType.SQRT, null));
tokenMap.put("SIN", new Token(TokenType.SIN, null));
tokenMap.put("COS", new Token(TokenType.COS, null));
tokenMap.put("^", new Token(TokenType.POWER, null));
TOKEN_MAP = tokenMap;
}
private TokenType type;
private Object value;
public Token(TokenType type, Object value) {
this.type = type;
this.value = value;
}
public TokenType getType() {
return type;
}
public Object getValue() {
return value;
}
public Token setValue(Object value) {
this.value = value;
return this;
}
@Override
public void interpret(Parser parser) {
// do nothing
}
}
非终结符表达式(部分代码省略):
// 非终结符
// 旋转语句
public class RotateStatement implements Grammar {
@Override
public void interpret(Parser parser) {
/**
* 解释ROT IS NUMBER,即旋转角度,将角度存入上下文中,供后续语句使用。
* 语法分析:先识别ROT,然后再识别IS,最后识别NUMBER。
**/
parser.matchToken(TokenType.ROT);
parser.matchToken(TokenType.IS);
parser.setRot((Double) parser.getCurrentToken().getValue());
parser.matchToken(TokenType.NUMBER);
parser.matchToken(TokenType.SEMICOLON);
}
}
上下文(部分代码已省略):
// 解释器
public class Parser {
// 语法规则
private Lexer lexer;
// 当前token
private Token currentToken;
// 输出
private String output;
// 旋转角度
private double rot;
// 原点坐标
private double originX;
private double originY;
// 缩放比例
private double scaleX;
private double scaleY;
private double from;
private double to;
private double step;
private ExprNode xNode;
private ExprNode yNode;
// 语句解释映射
private Map<TokenType, Grammar> statementMap;
public Parser(Lexer lexer) {
this.lexer = lexer;
this.output = "";
this.rot = 0;
this.originX = 0;
this.originY = 0;
this.scaleX = 1;
this.scaleY = 1;
statementMap = new HashMap<>();
statementMap.put(TokenType.ROT, new RotateStatement());
statementMap.put(TokenType.ORIGIN, new OriginStatement());
statementMap.put(TokenType.SCALE, new ScaleStatement());
statementMap.put(TokenType.FOR, new ForStatement());
}
}
//
客户端:
public class Client {
public static void main(String[] args) throws IOException {
Semantic semantic = new Semantic();
semantic.analyze();
}
}
解释器执行结果:
因为写画图代码的时间正值520,所以随手查找了心形坐标函数,执行结果如下,祝天下所有有情人终成眷属。
当你需要解释某种语言,无论这种语句是否通用,也许只有你自己能解释,并且该语言能表示为语法树,有不会太复杂,可以使用解释器模式。
2023年05月21日17:32:23