-
python语言解释器模式_python设计模式之解释器模式
2021-01-30 03:14:09python设计模式之解释器模式对每个应用来说,至少有以下两种不同的用户分类。[ ] 基本用户:这类用户只希望能够凭直觉使用应用。他们不喜欢花太多时间配置或学习应用的内部。对他们来说,基本的用法就足够了。[ ] ...python设计模式之解释器模式
对每个应用来说,至少有以下两种不同的用户分类。
[ ] 基本用户:这类用户只希望能够凭直觉使用应用。他们不喜欢花太多时间配置或学习应用的内部。对他们来说,基本的用法就足够了。
[ ] 高级用户:这些用户,实际上通常是少数,不介意花费额外的时间学习如何使用应用的高级特性。如果知道学会之后能得到以下好处,他们甚至会去学习一种配置(或脚本)语言。
[ ] 能够更好地控制一个应用
[ ] 以更好的方式表达想法
[ ] 提高生产力
解释器( Interpreter)模式仅能引起应用的高级用户的兴趣。这是因为解释器模式背后的主要思想是让非初级用户和领域专家使用一门简单的语言来表达想法。然而,什么是一种简单的语言?对于我们的需求来说,一种简单的语言就是没编程语言那么复杂的语言。
一般而言,我们想要创建的是一种领域特定语言( Domain Specific Language, DSL)。 DSL是一种针对一个特定领域的有限表达能力的计算机语言。很多不同的事情都使用DSL,比如,战斗模拟、记账、可视化、配置、通信协议等。
内部DSL构建在一种宿主编程语言之上。内部DSL的一个例子是,使用Python解决线性方程组的一种语言。使用内部DSL的优势是我们不必担心创建、编译及解析语法,因为这些已经被宿主语言解决掉了。劣势是会受限于宿主语言的特性。如果宿主语言不具备这些特性,构建一种表达能力强、简洁而且优美的内部DSL是富有挑战性的。
外部DSL不依赖某种宿主语言。 DSL的创建者可以决定语言的方方面面(语法、句法等),但也要负责为其创建一个解析器和编译器。为一种新语言创建解析器和编译器是一个非常复杂、长期而又痛苦的过程。
解释器模式仅与内部DSL相关。因此,我们的目标是使用宿主语言提供的特性构建一种简单但有用的语言,在这里,宿主语言是Python。注意,解释器根本不处理语言解析,它假设我们已经有某种便利形式的解析好的数据,可以是抽象语法树( abstract syntax tree, AST)或任何其他好用的数据结构。
1. 现实生活的例子
音乐演奏者是现实中解释器模式的一个例子。五线谱图形化地表现了声音的音调和持续时间。音乐演奏者能根据五线谱的符号精确地重现声音。在某种意义上,五线谱是音乐的语言,音乐演奏者是这种语言的解释器。
2. 软件的例子
内部DSL在软件方面的例子有很多。 PyT是一个用于生成(X)HTML的Python DSL。 PyT关注性能,并声称能与Jinja2的速度相媲美。当然,我们不能假定在PyT中必须使用解释器模式。然而, PyT是一种内部DSL,非常适合使用解释器模式。
3. 应用案例
在我们希望为领域专家和高级用户提供一种简单语言来解决他们的问题时,可以使用解释器模式。不过要强调的第一件事情是,解释器模式应仅用于实现简单的语言。如果语言具有外部DSL那样的要求,有更好的工具( yacc和lex、 Bison、 ANTLR等)来从头创建一种语言。
我们的目标是为专家提供恰当的编程抽象,使其生产力更高;这些专家通常不是程序员。理想情况下,他们使用我们的DSL并不需要了解高级Python知识,当然了解一点Python基础知识会更好,因为我们最终生成的是Python代码,但不应该要求了解Python高级概念。此外, DSL的性能通常不是一个重要的关注点。重点是提供一种语言,隐藏宿主语言的独特性,并提供人类更易读的语法。诚然, Python已经是一门可读性非常高的语言,与其他编程语言相比,其古怪的语法更少。
4. 实现
我们来创建一种内部DSL控制一个智能屋。这个例子非常契合如今越来越受关注的物联网时代。用户能够使用一种非常简单的事件标记来控制他们的房子。一个事件的形式为command ->receiver -> arguments。参数部分是可选的。并不是所有事件都要求参数。不要求任何参数的事件例子如下所示。
open -> gate
要求参数的事件例子如下所示:
increase -> boiler temperature -> 3 degrees
->符号用于标记事件一个部分的结束,并声明下一个部分的开始。实现一种内部DSL有多种方式。我们可以使用普通的正则表达式、字符串处理、操作符重载的组合以及元编程,或者一个能帮我们完成困难工作的库/工具。虽然在正规情况下,解释器不处理解析,但我觉得一个实战的例子也需要覆盖解析工作。因此,我决定使用一个工具来完成解析工作。 该工具名为Pyparsing,是标准Python3发行版的一部分。 如果你的系统上还没安装Pyparsing,可以使用下面的命令来
安装。
pip3 install pyparsing
在编写代码之前,为我们的语言定义一种简单语法是一个好做法。我们可以使用巴科斯-诺
尔形式( Backus-Naur Form, BNF)表示法来定义语法。
event ::= command token receiver token arguments
command ::= word+
word ::= a collection of one or more alphanumeric characters
token ::= ->
receiver ::= word+
arguments ::= word+
简单来说,这个语法告诉我们的是一个事件具有command -> receiver -> arguments的形式,并且命令、接收者及参数也具有相同的形式,即一个或多个字母数字字符的组合。包含数字部分是为了让我们能够在命令increase -> boiler temperature -> 3 degrees中传递3 degrees这样的参数,所以不必怀疑数字部分的必要性。
既然定义了语法,那么接着将其转变成实际的代码。以下是代码的样子。
word = Word(alphanums)
command = Group(OneOrMore(word))
token = Suppress("->")
device = Group(OneOrMore(word))
argument = Group(OneOrMore(word))
event = command + token + device + Optional(token + argument)
代码和语法定义基本的不同点是,代码需要以自底向上的方式编写。例如,如果不先为word赋一个值,那就不能使用它。 Suppress用于声明我们希望解析结果中省略->符号。
这个例子的完整代码(文件interpreter.py)使用了很多占位类,但为了让你精力集中一点,我会先只展示一个类。书中也包含完整的代码列表,在仔细解说完这个类之后会展示。我们来看一下Boiler类。一个锅炉的默认温度为83摄氏度。类有两个方法来分别提高和降低当前的温度。
class Boiler:
def __init__(self):
self.temperature = 83 # 单位为摄氏度
def __str__(self):
return 'boiler temperature: {}'.format(self.temperature)
def increase_temperature(self, amount):
print("increasing the boiler's temperature by {} degrees".format(amount))
self.temperature += amount
def decrease_temperature(self, amount):
print("decreasing the boiler's temperature by {} degrees".format(amount))
self.temperature -= amount
下一步是添加语法,之前已学习过。我们也创建一个boiler实例,并输出其默认状态。
word = Word(alphanums)
command = Group(OneOrMore(word))
token = Suppress("->")
device = Group(OneOrMore(word))
argument = Group(OneOrMore(word))
event = command + token + device + Optional(token + argument)
boiler = Boiler()
print(boiler)
获取Pyparsing解析结果的最简单方式是使用parseString()方法,该方法返回的结果是一个ParseResults实例,它实际上是一个可视为嵌套列表的解析树。例如,执行print(event.parseString('increase -> boiler temperature -> 3 degrees'))得到的结果如下所示。
[['increase'], ['boiler', 'temperature'], ['3', 'degrees']]
因此,在这里,我们知道第一个子列表是命令(提高),第二个子列表是接收者(锅炉温度),
第三个子列表是参数( 3摄氏度)。实际上我们可以解开ParseResults实例,从而可以直接访问
事件的这三个部分。可直接访问意味着我们可以匹配模式找到应该执行哪个方法。
cmd, dev, arg = event.parseString('increase -> boiler temperature -> 3 degrees')
if 'increase' in ' '.join(cmd):
if 'boiler' in ' '.join(dev):
boiler.increase_temperature(int(arg[0]))
print(boiler)
执行上面的代码片段会得到以下输出。
boiler temperature: 83
increasing the boiler's temperature by 3 degrees
boiler temperature: 86
完整代码如下:
from pyparsing import Word, OneOrMore, Optional, Group, Suppress, alphanums
class Gate:
def __init__(self):
self.is_open = False
def __str__(self):
return 'open' if self.is_open else 'closed'
def open(self):
print('opening the gate')
self.is_open = True
def close(self):
print('closing the gate')
self.is_open = False
class Garage:
def __init__(self):
self.is_open = False
def __str__(self):
return 'open' if self.is_open else 'closed'
def open(self):
print('opening the garage')
self.is_open = True
def close(self):
print('closing the garage')
self.is_open = False
class Aircondition:
def __init__(self):
self.is_on = False
def __str__(self):
return 'on' if self.is_on else 'off'
def turn_on(self):
print('turning on the aircondition')
self.is_on = True
def turn_off(self):
print('turning off the aircondition')
self.is_on = False
class Heating:
def __init__(self):
self.is_on = False
def __str__(self):
return 'on' if self.is_on else 'off'
def turn_on(self):
print('turning on the heating')
self.is_on = True
def turn_off(self):
print('turning off the heating')
self.is_on = False
class Boiler:
def __init__(self):
self.temperature = 83# in celsius
def __str__(self):
return 'boiler temperature: {}'.format(self.temperature)
def increase_temperature(self, amount):
print("increasing the boiler's temperature by {} degrees".format(amount))
self.temperature += amount
def decrease_temperature(self, amount):
print("decreasing the boiler's temperature by {} degrees".format(amount))
self.temperature -= amount
class Fridge:
def __init__(self):
self.temperature = 2 # 单位为摄氏度
def __str__(self):
return 'fridge temperature: {}'.format(self.temperature)
def increase_temperature(self, amount):
print("increasing the fridge's temperature by {} degrees".format(amount))
self.temperature += amount
def decrease_temperature(self, amount):
print("decreasing the fridge's temperature by {} degrees".format(amount))
self.temperature -= amount
def main():
word = Word(alphanums)
command = Group(OneOrMore(word))
token = Suppress("->")
device = Group(OneOrMore(word))
argument = Group(OneOrMore(word))
event = command + token + device + Optional(token + argument)
gate = Gate()
garage = Garage()
airco = Aircondition()
heating = Heating()
boiler = Boiler()
fridge = Fridge()
tests = ('open -> gate',
'close -> garage',
'turn on -> aircondition',
'turn off -> heating',
'increase -> boiler temperature -> 5 degrees',
'decrease -> fridge temperature -> 2 degrees')
open_actions = {'gate':gate.open,
'garage':garage.open,
'aircondition':airco.turn_on,
'heating':heating.turn_on,
'boiler temperature':boiler.increase_temperature,
'fridge temperature':fridge.increase_temperature}
close_actions = {'gate':gate.close,
'garage':garage.close,
'aircondition':airco.turn_off,
'heating':heating.turn_off,
'boiler temperature':boiler.decrease_temperature,
'fridge temperature':fridge.decrease_temperature}
for t in tests:
if len(event.parseString(t)) == 2: # 没有参数
cmd, dev = event.parseString(t)
cmd_str, dev_str = ' '.join(cmd), ' '.join(dev)
if 'open' in cmd_str or 'turn on' in cmd_str:
open_actions[dev_str]()
elif 'close' in cmd_str or 'turn off' in cmd_str:
close_actions[dev_str]()
elif len(event.parseString(t)) == 3: # 有参数
cmd, dev, arg = event.parseString(t)
cmd_str, dev_str, arg_str = ' '.join(cmd), ' '.join(dev), ' '.join(arg)
num_arg = 0
try:
num_arg = int(arg_str.split()[0]) # 抽取数值部分
except ValueError as err:
print("expected number but got: '{}'".format(arg_str[0]))
if 'increase' in cmd_str and num_arg > 0:
open_actions[dev_str](num_arg)
elif 'decrease' in cmd_str and num_arg > 0:
close_actions[dev_str](num_arg)
if __neme__ == '__main__':
main()
输出如下:
opening the gate
closing the garage
turning on the aircondition
turning off the heating
increasing the boiler's temperature by 5 degrees
decreasing the fridge's temperature by 2 degrees
5. 小结
解释器模式用于为高级用户和领域专家提供一个类编程的框架,但没有暴露出编程语言那样的复杂性。这是通过实现一个DSL来达到目的的。
DSL是一种针对特定领域、表达能力有限的计算机语言。 DSL有两类,分别是内部DSL和外部DSL。内部DSL构建在一种宿主编程语言之上,依赖宿主编程语言,外部DSL则是从头实现,不依赖某种已有的编程语言。解释器模式仅与内部DSL相关。
乐谱是一个非软件DSL的例子。音乐演奏者像一个解释器那样,使用乐谱演奏出音乐。从软件的视角来看,许多Python模板引擎都使用了内部DSL。 PyT是一个高性能的生成(X)HTML的Python DSL。我们也看到Chromium的Mesa库是如何使用解释器模式将图形相关的C代码翻译成Python可执行对象的。
-
python语言解释器模式_python-解释器模式
2021-01-14 09:45:44说明:解释器模式在面向对象语言实现的编译器中得到了广泛的应用。但是此模式进适用于建大的文法解释,弊端又多,所以很少有其他方面使用。例如:将“1+2+3-4”等字符串输入到python的console,但是本身python不认识...说明:
解释器模式在面向对象语言实现的编译器中得到了广泛的应用。但是此模式进适用于建大的文法解释,弊端又多,所以很少有其他方面使用。例如:将“1+2+3-4”等字符串输入到python的console,但是本身python不认识这些字符串,就需要定义的一套文法规则来解释这些字符串,也就是设计一个自定义语言。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在解释器模式的定义中所指的“语言”是使用规定格式和语法的代码。
解释器模式的结构
解释器模式包含以下4个角色: AbstractExpression(抽象表达式) TerminalExpression(终结符表达式) NonterminalExpression(非终结符表达式) Context(环境类)
实例:
#要开发一个自动识别谱子的吉他模拟器,达到录入谱即可按照谱发声的效果。#除了发声设备外(假设已完成),最重要的就是读谱和译谱能力了。#分析其需求,整个过程大致上分可以分为两部分:#根据规则翻译谱的内容;根据翻译的内容演奏。#我们用一个解释器模型来完成这个功能。
classPlayContext():
play_text=NoneclassExpression():definterpret(self, context):if len(context.play_text) ==0:return
else:
play_segs=context.play_text.split(" ")for play_seg inplay_segs:
pos=0for ele inplay_seg:if ele.isalpha():#检验字符串是否只由字母组成
pos+=1
continue
breakplay_chord=play_seg[0:pos]
play_value=play_seg[pos:]
self.execute(play_chord,play_value)defexecute(self,play_key,play_value):pass
classNormGuitar(Expression):defexecute(self, key, value):print("Normal Guitar Playing--Chord:%s Play Tune:%s"%(key,value))#PlayContext类为谱的内容,这里仅含一个字段,没有方法。#Expression即表达式,里面仅含两个方法,interpret负责转译谱,execute则负责演奏;NormGuitar类覆写execute,以吉他 的方式演奏。#业务场景如下:
if __name__=="__main__":
context=PlayContext()
context.play_text= "C53231323 Em43231323 F43231323 G63231323"guitar=NormGuitar()
guitar.interpret(context)
打印结果:
Normal Guitar Playing--Chord:C Play Tune:53231323
Normal Guitar Playing--Chord:Em Play Tune:43231323
Normal Guitar Playing--Chord:F Play Tune:43231323
Normal Guitar Playing--Chord:G Play Tune:63231323
模式优点
易于改变和扩展文法 可以方便地实现一个简单的语言 实现文法较为容易(有自动生成工具) 增加新的解释表达式较为方便
模式缺点
对于复杂文法难以维护 执行效率较低
模式适用环境
可以将一个需要解释执行的语言中的句子表示为一棵抽象语法树 一些重复出现的问题可以用一种简单的语言来进行表达 一个语言的文法较为简单 执行效率不是关键问题
-
Y语言v2.0、Python——设计一门更复杂的解释型编程语言并实现解释器
2020-12-01 17:17:09最近又玩了遍《人力资源机器》和《70亿人》(都是编程游戏,用一种类似汇编的简陋语言解决谜题,后者相当于前者的多线程版本),考虑自己设计开发一个编程游戏。游戏玩法大概是玩家管理一个机器人编队(包括近战型、...背景
最近又玩了遍《人力资源机器》和《70亿人》(都是编程游戏,用一种类似汇编的简陋语言解决谜题,后者相当于前者的多线程版本),考虑自己设计开发一个编程游戏。游戏玩法大概是玩家管理一个机器人编队(包括近战型、远程型和支援型)与敌方机器人编队作战,每台机器人包括一个可编程CPU和一些可选模块(比如移动、撞击、射击、炮火打击和雷达模块等等),核心玩法为机器人的改装(模块装配)、编队(队伍构成、站位和对地形的利用)和编程(编写机器人CPU运行的核心代码,从模块中获取战场信息并控制模块行动)。最后打算以Unity开发,Unity学习中…
还是先用Python+pygame写个demo…简介
考虑到游戏内给机器人编程所使用的编程语言应该简单有效,核心操作在于CPU与模块的交互,所以刚好用上了之前设计的Y语言,不过之前的Y语言设计得非常简陋,不支持真正的函数,即使是实现一个简单的功能也需要大量语句,因此为了适应游戏内容做了一些改进些,这是前身:Y语言1.0,相比Y1.0版本主要做了以下改动:
- 真正的函数:新版解释器加入了调用栈,现在的Y语言函数支持了传参和返回值功能,同时可以进行复杂的嵌套调用和递归调用了,函数仍然支持嵌套定义;
- 变量域:新增了变量域的概念,现在变量区分局部变量和全局变量了,在使用CALL语句调用函数和RET语句返回时会有局部变量域的切换发生;
- 特殊变量@:新增了一个特殊的全局变量@,绝大部分运算指令和函数返回值都会在运行结束后存入该变量;
- 更自然的列表:删除了v1.0中反人类的idx、set和len等列表操作指令,现在可以用中括号[]来索引列表中的元素了,创建列表也可以采用[1,2,3]这种方式了,同时还支持了列表嵌套索引、嵌套定义和多维列表;
- 更简洁的if语句:现在if语句支持if 1 < 2这种书写方式了,此外还加入了else语句;
- 循环语句:新增了loop语句,类似高级语言中while的用法,写循环不再需要危险的at和go了;
- 移除字符串:因为编程游戏中不需要字符串的处理工作,所以移除了字符串的支持和相关操作;
一段代码片段示例:
Python:# 函数flatten可以将一个多层嵌套列表list压平成一维列表 def flatten(lst): tmp = [] for item in lst: if isinstance(item, list): for it in flatten(item): tmp.append(it) else: tmp.append(item) return tmp x = [1, [2, [3, [4, [5], 6, [[7, 8], 9]]]], 10, 11, 12] print(x, len(x)) y = flatten(x) print(y, len(y))
翻译成Y语言:
// 函数flatten可以将一个多层嵌套列表list压平成一维列表 def flatten list mov tmp [] mov i 0 // list[] 返回list的长度,对于非列表则返回-1 loop i < list[] if list[i][] == -1 push tmp list[i] else // 激动人心的递归调用 call flatten list[i] mov res mov j 0 loop j < res[] push tmp res[j] inc j elop eif inc i elop ret tmp edef // 定义复杂的嵌套列表x mov x [1,[2,[3,[4,[5],6,[[7,8],9]]]],10,11,12] out x x[] call flatten x // 返回值放入特殊变量@,此时@ = [1,2,3,4,5,6,7,8,9,10,11,12] out @ @[]
详细介绍
1.空格、换行、注释以及缩进
空格是Y语言语句中唯一的分隔符,指令与变量与常量之间均以空格分隔。
语句与语句之间用换行分隔,一行只能有一条语句。
注释以//打头,运行时跳过,空行同理。
缩进对代码运行无影响。2.变量
变量名为非数字打头的下划线数字字母串,变量自身无类型,可存储Y语言支持的三种数据类型:整数,浮点数,列表。
变量分为全局变量和局部变量,取变量时优先搜索局部变量域,即定义局部变量会屏蔽同名全局变量。存变量时除非使用glb指令,否则都存入局部变量域(局部变量不存在时会被创建)。调用函数和函数返回时会发生局部变量域切换。全局变量域始终可见。
另外@为一个特殊的全局变量,用于存储一些指令的结果。
数据类型支持整数、浮点数、列表三种
数字皆以自然方式定义,如:1、0、1.5、-1.5、+6皆为有效定义;
列表以[]括起来的一组元素定义,元素间以逗号分隔,支持嵌套定义列表和包含变量,如:[1,2,3,[1,2,3],var1,var2]。空[]表示空列表。
列表的索引也采用[],list[x]表示列表list的x项,若list是多维列表也支持list[x][y]来多维索引,list[x[y]]的嵌套索引也支持。此外变量名后接空括号[]表示求长度,如array = [1,2,3] 则array[]为3,若array为非列表则array[] = -1。3.行号和指令
Y语言的代码从头开始逐行运行,每行代码都对应一个行号,行号从0开始。每行语句的第一个单词即指令,其后都为指令需要的参数,指令指示解释器的行为,具体见下文。
指令和语法
指令可以分为控制指令、操作指令、逻辑指令和外部指令四类,所有指令运行时均不分大小写。其中外部指令由解释器外挂了哪些模块决定,并非通用的指令。
1.控制指令
- at:定位指令,不推荐使用,接受一个变量名,将当前行号存入该变量。如 at var。
- go:跳转指令,不推荐使用,接受一个参数,可以是整数常量或变量,将当前运行行跳转到数字对应行的下一行。如 go target。
- mov:赋值指令,接受一个变量名和一个可选参数,将第二个常量或变量的值存入第一个变量,第一个变量(除@外)在局部变量域不存在时会被创建,若无第二个参数则默认为@。如 mov list [1,2,3]或 mov @ 1.234,此外mov x 等效于 mov x @。
- glb:全局赋值指令,用法与mov相同,但对第一个参数的操作变量域为全局变量域。
- if:条件指令,接受0、1或3个参数,接受1个参数时,若该参数为0则跳转到匹配的else(如果有)或eif,否则继续运行,如:if flag。不接受参数时 if 相当于 if @。接受三个参数时需要满足if a op b 的形式,其中a、b皆为数字量,op为<、<=、>、>=、==、!=中的一种,a op b表达成立时继续运行否则跳转,如 if var <= 999。
- else:否则条件指令,不接受参数,与if配对使用,当if条件不满足时会跳转到该指令(如果有),若if条件满足遇到该指令则会跳转到匹配的eif。
- eif:结束条件指令,不接受参数,与if配对使用,用于if和else的跳转定位,eif会匹配之前最近一个未被匹配的if。
- def:函数定义指令,接受一个变量名和若干可选变量名,将当前行号存入该变量(定义于最外层的函数会被存入全局变量),可选变量名将作为函数调用时的局部变量被传入参数创建,随后跳转到对应edef语句,用于定义函数。如 def fun a b。
- edef:结束定义指令,不接受参数,与def配对使用,用于结束def的定义范围。
- call:调用指令,接受一个变量名和该变量对应函数对应的参数个数,储存当前位置和局部变量进入调用栈,以传入参数创建新的局部变量域并跳转至函数定义行的下一行,如 call fun 1 2。
- ret:返回指令,接受一个可选参数,仅能在调用函数内使用,从调用栈中弹出上一层的call的调用位置和局部变量域并跳转,如果接受了参数还会将该参数值存入变量@,如 ret a。
- loop:循环指令,接受参数与if相同,与elop配对使用,满足条件时继续运行,否则跳转到对应elop指令的下一行,如 loop i < array[]。
- elop:循环结束指令,不接受参数,与loop配对使用,跳转回对应的loop。
- brk:跳出指令,不接受参数,用于循环内部,跳转到对应elop指令的下一行。
- ctn:继续指令,不接受参数,用于循环内部,跳转到对应loop指令。
2.操作指令
- push:压入指令,接受一个列表和一个可选参数,将第二个参数追加到列表末尾,如 push list var。无可选参数时 push list 相当于 push list @。
- pop:弹出指令,接受一个列表和一个可选参数,将列表末尾元素弹出放入第二个参数,如 pop list var。无可选参数时 pop list 相当于 pop list @。
- int:取整指令,接受一个数字变量,将其值的小数部分舍去,如 int num。
- inc:自增指令,接受一个数字变量,将其值增加1,如 inc x。
- dec:自减指令,接受一个数字变量,将其值减少1,如 dec x。
- add:加法指令,接受两个数字量,求和存入@,如 add n 1。
- sub:减法指令,接受两个数字量,求差存入@,如 sub n 2。
- mul:乘法指令,接受两个数字量,求积存入@,如 mul n 3。
- div:除法指令,接受两个数字量,求商存入@,如 div n 4。
- mod:模指令,接受两个数字量,求模存入@,如 mod n 5。
- pow:幂指令,接受两个数字量,求幂存入@,如 pow n 0.5。
3.逻辑指令
- eq:相等指令,接受两个参数,两个参数相等时将1存入@,否则将0存入@,如 eq i [1,2,3]。
- neq:不等指令,接受两个参数,两个参数相等时将0存入@,否则将1存入@,如 neq i 22。
- gt:大于指令,接受两个数字量,满足前者大于后者时将1存入@,否则将0存入@,如 gt x 0。
- ls:小于指令,接受两个数字量,满足前者小于后者时将1存入@,否则将0存入@,如 ls x 0。
- ge:大于等于指令,接受两个数字量,满足前者大于等于后者时将1存入@,否则将0存入@,如 ge x 0。
- le:小于等于指令,接受两个数字量,满足前者小于等于后者时将1存入@,否则将0存入@,如 le x 0。
- and:逻辑与指令,接受两个参数,两个参数都不为0时将1存入@,否则将0存入@,如 and a b。
- or:逻辑或指令,接受两个参数,两个参数任意一个不为0时将1存入@,否则将0存入@,如 or a b。
- not:逻辑与指令,接受一个参数,参数为0时将1存入@,否则将0存入@,如 not a。
4.外部指令
- out:输出指令,来自模块Outputer,接受任意个参数,将其依次输出到屏幕,如 out 1 x y。
- in:输入指令,来自模块Inputer,不接受参数,从键盘输入一个数字存入@。
……
Python实现的解释器
认真写了个解释器,应该没有没处理到的异常和BUG。
1.解释器文件interpreter.py
class InterpreterError(Exception): """ 解释器内部异常 """ pass class Interpreter: """ Y语言解释器 """ def __init__(self, codes: list, mod_cmds: dict): self.codes = codes # Y语言代码,字符串列表 self.ans = 0 # 特殊变量@ self.call_stack = [] # 解释器栈 self.global_vars = {} # 全局变量 self.local_vars = {} # 局部变量 self.pointer = 0 # 代码指针 self.prime_commands = {name[4:]: self.__getattribute__(name) for name in self.__dir__() if name.startswith('cmd_')} # Y语言原生指令集 self.module_commands = mod_cmds # 模块指令集 def run(self, cpt: int): """ 执行cpt条指令,顺利执行完毕返回True,已无指令可执行或执行出错返回False """ count = 0 while count < cpt: if self.pointer >= len(self.codes) or self.pointer < 0: return False code = self.codes[self.pointer].strip() try: count += self.exec(code) except InterpreterError as e: print('(line {}: {})Cpu Error: '.format(self.pointer, code) + str(e)) return False self.pointer += 1 return True def exec(self, code: str): """ 执行一条指令,执行成功返回1,否则返回0""" cmd, *args = code.split(' ') cmd = cmd.lower() if not cmd or cmd.startswith('//'): return 0 try: self.prime_commands[cmd](args) except KeyError: try: val = self.module_commands[cmd]([self.get_value(arg) for arg in args]) if val is not None: self.set_value('@', val) except KeyError: raise InterpreterError('Unknow command ' + cmd) return 1 def get_matched_end(self, ptr: int, start: str, end: str, step=1): """ 获取ptr行strat匹配的end所在行 """ count = 1 ptr += step while 0 <= ptr < len(self.codes): code = self.codes[ptr].upper().strip() if code.startswith(start): count += 1 elif code.startswith(end): count -= 1 if count == 0: return ptr ptr += step raise InterpreterError(end + ' not found') def get_matched_else_eif(self, ptr: int, elsed=False): """ 获取ptr行if或else匹配的else或eif所在行 """ count = 1 ptr += 1 while ptr < len(self.codes): code = self.codes[ptr].upper().strip() if code.startswith('IF'): count += 1 elif count == 1 and code.startswith('ELSE'): if elsed: self.pointer = ptr raise InterpreterError('ELSE is mismatched') return ptr elif code.startswith('EIF'): count -= 1 if count == 0: return ptr ptr += 1 raise InterpreterError('EIF not found') def get_array_index(self, var: str): """ 将var拆分为列表名和索引 """ left = self.left_bracket_index(var) index = self.get_value(var[left + 1: -1]) var = self.get_value(var[: left]) if not isinstance(index, int): raise InterpreterError(str(index) + ' is not a int') if not isinstance(var, list): raise InterpreterError(str(var) + ' is not a list') return var, index def get_operands(self, args: list): """ 获取操作数 """ oper1 = self.get_value(args[0]) oper2 = self.get_value(args[1]) if not (isinstance(oper1, int) or isinstance(oper1, float)): raise InterpreterError(str(oper1) + ' is not a number') if not (isinstance(oper2, int) or isinstance(oper2, float)): raise InterpreterError(str(oper2) + ' is not a number') return oper1, oper2 @staticmethod def left_bracket_index(var: str): """ var为末尾为]的表达式,返回和末尾]匹配的[ """ left = len(var) - 2 count = 1 while left != -1: if var[left] == '[': count -= 1 elif var[left] == ']': count += 1 if count == 0: break left -= 1 else: raise InterpreterError(var + ' missing [') return left @staticmethod def parse_list(lst: str): """ 分析列表字符串,返回元素列表 """ items = [] count = 0 tmp = '' for c in lst[1:-1]: if c == '[': count += 1 elif c == ']': count -= 1 elif count == 0 and c == ',': items.append(tmp) tmp = '' continue tmp += c if tmp: items.append(tmp) return items @staticmethod def return_number(var: str): """ var为数字字符串,返回var表示的数字 """ try: return int(var) except ValueError: try: return float(var) except ValueError: raise InterpreterError(var + ' is not a number or identifier') def return_var(self, var: str): """ var为变量名,返回var的值,优先返回局部变量 """ try: return self.local_vars[var] except KeyError: try: return self.global_vars[var] except KeyError: raise InterpreterError(var + ' is not defined') def get_value(self, var: str): """ var为变量名、立即数或者数组嵌套表达式字符串,获取数组嵌套表达式的值或者变量的值或者立即数本身 """ if not var: raise InterpreterError('invalid syntax') if var.endswith(']'): if var.startswith('['): return [self.get_value(item) for item in self.parse_list(var)] if var.endswith('[]'): item = self.get_value(var[:-2]) if isinstance(item, list): return len(item) else: return -1 var, index = self.get_array_index(var) try: return var[index] except IndexError: raise InterpreterError(str(index) + ' out of range') if var == '@': return self.ans if var.isidentifier(): return self.return_var(var) return self.return_number(var) def set_value(self, var: str, val, global_var=False): """ var为变量名或者数组嵌套表达式字符串,设置数组嵌套表达式的值或者变量的值为val """ if var.endswith(']'): if var.startswith('['): raise InterpreterError(var + ' is not a variable') if var.endswith('[]'): raise InterpreterError(var + ' is not a variable') var, index = self.get_array_index(var) try: var[index] = val except IndexError: raise InterpreterError(str(index) + ' out of range') return if var == '@': self.ans = val return if var.isidentifier(): if global_var: self.global_vars[var] = val else: self.local_vars[var] = val return raise InterpreterError(var + ' is not a variable') def cmp_expr(self, args: list): """ 计算比较表达式 """ oper1 = self.get_value(args[0]) op = args[1] oper2 = self.get_value(args[2]) if op not in {'==', '!=', '<', '<=', '>', '>='}: raise InterpreterError(op + ' is not a cmp operator') if not (isinstance(oper1, int) or isinstance(oper1, float)): raise InterpreterError(str(oper1) + ' is not a number') if not (isinstance(oper2, int) or isinstance(oper2, float)): raise InterpreterError(str(oper2) + ' is not a number') return eval('oper1 {} oper2'.format(op)) def cmd_at(self, args: list): if len(args) != 1: raise InterpreterError('AT takes 1 argument but {} were given'.format(len(args))) self.set_value(args[0], self.pointer) def cmd_go(self, args: list): if len(args) != 1: raise InterpreterError('GO takes 1 argument but {} were given'.format(len(args))) ptr = self.get_value(args[0]) if not isinstance(ptr, int): raise InterpreterError(str(ptr) + ' is not a int') self.pointer = ptr def cmd_mov(self, args: list): if not 1 <= len(args) <= 2: raise InterpreterError('MOV takes 1 or 2 argument(s) but {} were given'.format(len(args))) self.set_value(args[0], self.get_value(args[1] if len(args) == 2 else '@')) def cmd_glb(self, args: list): if not 1 <= len(args) <= 2: raise InterpreterError('GLB takes 1 or 2 argument(s) but {} were given'.format(len(args))) self.set_value(args[0], self.get_value(args[1] if len(args) == 2 else '@'), global_var=True) def cmd_if(self, args: list): if len(args) > 1 and len(args) != 3: raise InterpreterError('IF takes 0 or 1 or 3 argument(s) but {} were given'.format(len(args))) if (len(args) != 3 and self.get_value(args[0] if args else '@') == 0) \ or (len(args) == 3 and not self.cmp_expr(args)): self.pointer = self.get_matched_else_eif(self.pointer) def cmd_else(self, args: list): if len(args) != 0: raise InterpreterError('ELSE takes no argument but {} were given'.format(len(args))) self.pointer = self.get_matched_else_eif(self.pointer, elsed=True) @staticmethod def cmd_eif(args: list): if len(args) != 0: raise InterpreterError('EIF takes no argument but {} were given'.format(len(args))) def cmd_def(self, args: list): if len(args) < 1: raise InterpreterError('DEF takes at least 1 argument but {} were given'.format(len(args))) self.set_value(args[0], self.pointer, global_var=False if self.call_stack else True) self.pointer = self.get_matched_end(self.pointer, 'DEF', 'EDEF') def cmd_edef(self, args: list): if len(args) != 0: raise InterpreterError('EDEF takes no argument but {} were given'.format(len(args))) if not self.call_stack: raise InterpreterError('EDEF is mismatched') self.pointer, self.local_vars = self.call_stack.pop() def cmd_ret(self, args: list): if len(args) > 1: raise InterpreterError('RET takes 0 or 1 argument but {} were given'.format(len(args))) if not self.call_stack: raise InterpreterError('RET outside function') if len(args) == 1: self.set_value('@', self.get_value(args[0])) self.pointer, self.local_vars = self.call_stack.pop() def cmd_call(self, args: list): if len(args) < 1: raise InterpreterError('CALL takes at least 1 argument but {} were given'.format(len(args))) ptr = self.get_value(args[0]) if not isinstance(ptr, int): raise InterpreterError(args[0] + ' is not a function') cmd, *fun_args = self.codes[ptr].strip().split(' ') if cmd.lower() != 'def': raise InterpreterError(args[0] + ' is not a function') if len(args) != len(fun_args): raise InterpreterError( fun_args[0] + ' takes {} argument(s) but {} were given'.format(len(fun_args) - 1, len(args) - 1)) for arg in fun_args: if not arg.isidentifier(): raise InterpreterError(arg + ' is not a variable') self.call_stack.append((self.pointer, self.local_vars)) self.local_vars = {var: self.get_value(args[i]) for i, var in enumerate(fun_args)} self.pointer = ptr def cmd_loop(self, args: list): if len(args) > 1 and len(args) != 3: raise InterpreterError('LOOP takes 0 or 1 or 3 argument(s) but {} were given'.format(len(args))) if (len(args) != 3 and self.get_value(args[0] if args else '@') == 0) \ or (len(args) == 3 and not self.cmp_expr(args)): self.pointer = self.get_matched_end(self.pointer, 'LOOP', 'ELOP') def cmd_elop(self, args: list): if len(args) != 0: raise InterpreterError('ELOP takes no argument but {} were given'.format(len(args))) self.pointer = self.get_matched_end(self.pointer, 'ELOP', 'LOOP', -1) - 1 def cmd_brk(self, args: list): if len(args) != 0: raise InterpreterError('BRK takes no argument but {} were given'.format(len(args))) self.pointer = self.get_matched_end(self.pointer, 'LOOP', 'ELOP') def cmd_ctn(self, args: list): if len(args) != 0: raise InterpreterError('CTN takes no argument but {} were given'.format(len(args))) self.pointer = self.get_matched_end(self.pointer, 'ELOP', 'LOOP', -1) - 1 def cmd_push(self, args: list): if not 1 <= len(args) <= 2: raise InterpreterError('PUSH takes 1 or 2 argument(s) but {} were given'.format(len(args))) lst = self.get_value(args[0]) if not isinstance(lst, list): raise InterpreterError(args[0] + ' is not a list') lst.append(self.get_value(args[1] if len(args) == 2 else '@')) def cmd_pop(self, args: list): if not 1 <= len(args) <= 2: raise InterpreterError('POP takes 1 or 2 argument(s) but {} were given'.format(len(args))) lst = self.get_value(args[0]) if not isinstance(lst, list): raise InterpreterError(args[0] + ' is not a list') if len(lst) == 0: raise InterpreterError(args[0] + ' is empty') self.set_value(args[1] if len(args) == 2 else '@', lst.pop()) def cmd_int(self, args: list): if len(args) != 1: raise InterpreterError('INT takes 1 argument but {} were given'.format(len(args))) oper = self.get_value(args[0]) if not (isinstance(oper, int) or isinstance(oper, float)): raise InterpreterError(str(oper) + ' is not a number') self.set_value(args[0], int(oper)) def cmd_inc(self, args: list): if len(args) != 1: raise InterpreterError('INC takes 1 argument but {} were given'.format(len(args))) oper = self.get_value(args[0]) if not (isinstance(oper, int) or isinstance(oper, float)): raise InterpreterError(str(oper) + ' is not a number') self.set_value(args[0], oper + 1) def cmd_dec(self, args: list): if len(args) != 1: raise InterpreterError('DEC takes 1 argument but {} were given'.format(len(args))) oper = self.get_value(args[0]) if not (isinstance(oper, int) or isinstance(oper, float)): raise InterpreterError(str(oper) + ' is not a number') self.set_value(args[0], oper - 1) def cmd_add(self, args: list): if len(args) != 2: raise InterpreterError('ADD takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', oper1 + oper2) def cmd_sub(self, args: list): if len(args) != 2: raise InterpreterError('SUB takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', oper1 - oper2) def cmd_mul(self, args: list): if len(args) != 2: raise InterpreterError('MUL takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', oper1 * oper2) def cmd_div(self, args: list): if len(args) != 2: raise InterpreterError('DIV takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) if oper2 == 0: raise InterpreterError('division by zero') self.set_value('@', oper1 / oper2) def cmd_mod(self, args: list): if len(args) != 2: raise InterpreterError('MOD takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) if oper2 == 0: raise InterpreterError('modulo by zero') self.set_value('@', oper1 % oper2) def cmd_pow(self, args: list): if len(args) != 2: raise InterpreterError('POW takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', oper1 ** oper2) def cmd_eq(self, args: list): if len(args) != 2: raise InterpreterError('EQ takes 2 arguments but {} were given'.format(len(args))) oper1 = self.get_value(args[0]) oper2 = self.get_value(args[1]) self.set_value('@', 1 if oper1 == oper2 else 0) def cmd_neq(self, args: list): if len(args) != 2: raise InterpreterError('NEQ takes 2 arguments but {} were given'.format(len(args))) oper1 = self.get_value(args[0]) oper2 = self.get_value(args[1]) self.set_value('@', 1 if oper1 != oper2 else 0) def cmd_gt(self, args: list): if len(args) != 2: raise InterpreterError('GT takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', 1 if oper1 > oper2 else 0) def cmd_ls(self, args: list): if len(args) != 2: raise InterpreterError('LS takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', 1 if oper1 < oper2 else 0) def cmd_ge(self, args: list): if len(args) != 2: raise InterpreterError('GE takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', 1 if oper1 >= oper2 else 0) def cmd_le(self, args: list): if len(args) != 2: raise InterpreterError('LE takes 2 arguments but {} were given'.format(len(args))) oper1, oper2 = self.get_operands(args) self.set_value('@', 1 if oper1 <= oper2 else 0) def cmd_and(self, args: list): if len(args) != 2: raise InterpreterError('AND takes 2 arguments but {} were given'.format(len(args))) oper1 = self.get_value(args[0]) oper2 = self.get_value(args[1]) self.set_value('@', 1 if oper1 != 0 and oper2 != 0 else 0) def cmd_or(self, args: list): if len(args) != 2: raise InterpreterError('OR takes 2 arguments but {} were given'.format(len(args))) oper1 = self.get_value(args[0]) oper2 = self.get_value(args[1]) self.set_value('@', 1 if oper1 != 0 or oper2 != 0 else 0) def cmd_not(self, args: list): if len(args) != 1: raise InterpreterError('NOT takes 1 argument but {} were given'.format(len(args))) oper = self.get_value(args[0]) self.set_value('@', 0 if oper != 0 else 1)
2.外部模块文件moudle.py
class MoudleError(Exception): """ 外部模块异常 """ pass class BaseMoudle: def __init__(self, command: str, level=1): self.command = command self.level = level def run(self, args: list): pass class Inputer(BaseMoudle): def __init__(self): BaseMoudle.__init__(self, 'in') def run(self, args: list): if len(args) > 0: raise MoudleError('IN takes no argument but {} were given'.format(len(args))) ret = input() try: return int(ret) except ValueError: try: return float(ret) except ValueError: raise MoudleError(ret + ' is not a number') class Outputer(BaseMoudle): def __init__(self): BaseMoudle.__init__(self, 'out') def run(self, args: list): print(*args)
3.CPU文件cpu.py
from moudle import BaseMoudle from interpreter import Interpreter class Cpu: def __init__(self, command_per_turn: int): self.cpt = command_per_turn self.bus = Bus() self.interpreter = None def boot(self, codestr: str): codes = codestr.split('\n') mod_cmds = {mod.command: mod.run for mod in self.bus.modules.values()} self.interpreter = Interpreter(codes, mod_cmds) def run(self): return self.interpreter.run(self.cpt) class Bus: def __init__(self): self.modules = {} def install(self, moudle: BaseMoudle): if moudle.command in self.modules.keys(): raise RuntimeError('Moudle Access Conflict: ' + moudle.command) self.modules[moudle.command] = moudle def uninstall(self, moudle: BaseMoudle): if moudle.command in self.modules.keys(): del self.modules[moudle.command] raise RuntimeError('Moudle Access Not Existent: ' + moudle.command)
4.测试文件
from cpu import Cpu from moudle import Inputer, Outputer code = ''' // 函数flatten可以将一个多层嵌套列表list压平成一维列表 def flatten list mov tmp [] mov i 0 // list[] 返回list的长度,对于非列表则返回-1 loop i < list[] if list[i][] == -1 push tmp list[i] else // 激动人心的递归调用 call flatten list[i] mov res mov j 0 loop j < res[] push tmp res[j] inc j elop eif inc i elop ret tmp edef // 定义复杂的嵌套列表x mov x [1,[2,[3,[4,[5],6,[[7,8],9]]]],10,11,12] out x x[] call flatten x // 返回值放入特殊变量@,此时@ = [1,2,3,4,5,6,7,8,9,10,11,12] out @ @[] ''' cpu = Cpu(1) cpu.bus.install(Inputer()) cpu.bus.install(Outputer()) cpu.boot(code) count = 1 while cpu.run(): count += 1 print('\ncount: {}'.format(count))
案例代码和运行效果
1、质数输出
def isprime n pow n 0.5 mov t mov i 2 loop i <= t mod n i if @ == 0 ret 0 eif inc i elop ret 1 edef def print_prime n mov primes [] mov i 2 loop i <= n call isprime i if push primes i eif inc i elop ret primes edef call print_prime 1000 out @
输出:[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
2、快速排序
def Qsort array sub array[] 1 call quick_sort array 0 @ edef def quick_sort array low high loop low < high mov i low mov j high mov key array[low] loop i < j loop key <= array[j] if i >= j brk eif dec j elop mov array[i] array[j] loop key >= array[i] if i >= j brk eif inc i elop mov array[j] array[i] elop mov array[i] key sub i 1 call quick_sort array low @ add i 1 mov low elop edef mov array [3,5,6,8,3,1,2,5,7,9,5,6,8,6,5] call Qsort array out array
输出:[1, 2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 7, 8, 8, 9]
-
九、解释器设计模式
2016-04-28 22:44:00解释器模式在实际运用上相对来说少很多,因为很少会自己去构造语言的文法。2.解释器模式使用场景 重复发生的问题可以使用解释器模式。 一个简单语法需要解释的场景。 3. 解释器模式的UML类图由于解释器模式很好使用...1.介绍
解释器模式是类的行为模式,给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。
解释器模式在实际运用上相对来说少很多,因为很少会自己去构造语言的文法。
2.解释器模式使用场景
- 重复发生的问题可以使用解释器模式。
- 一个简单语法需要解释的场景。
3. 解释器模式的UML类图
由于解释器模式很好使用,下面的术语解释直接复制网络(侵删)
- 解释器模式的结构
抽象解释器:
声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。
具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。终结符表达式:
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
非终结符表达式:
文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
环境角色:
这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
4.解释器模式简单实现
- 抽象的算术运算解释器 ArithmeticExpression
public abstract class ArithmeticExpression { /** * 抽象的解析方法 * 具体的解析逻辑由具体子类实现 * @return */ public abstract int interpret(); }
- 数字解释器
public class NumExpression extends ArithmeticExpression { private int num; public NumExpression(int num) { this.num = num; } @Override public int interpret() { return num; } }
- 运算符号抽象解释器,为所有运算符号解释器共性的提取
public class OperationExpression extends ArithmeticExpression { //声明两个成员变量存储运算符号两边的数字解释器 protected ArithmeticExpression exp1, exp2; public OperationExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) { this.exp1 = exp1; this.exp2 = exp2; } @Override public int interpret() { return 0; } }
- 加法运算抽象解释器
public class AdditionExpression extends OperationExpression { public AdditionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) { super(exp1, exp2); } @Override public int interpret() { return exp1.interpret() + exp2.interpret(); } }
- 处理与解释相关的一些业务:
public class Calculator { //声明一个Stack栈存储并操作所有相关的解释器 private Stack<ArithmeticExpression> expressions = new Stack<ArithmeticExpression>(); public Calculator(String expression) { //声明两个ArithmeticExpressioin类型的临时变量,存储运算符左右两边的数字解释器 ArithmeticExpression exp1, exp2; String[] elements = expression.split(" "); for (int i = 0; i < elements.length; i++) { switch (elements[i].charAt(0)) { case '+': //将栈中的解释器弹出作为运算符号左边的解释器 exp1 = expressions.pop(); //同时将运算符号数组角标下一个元素构成 exp2 = new NumExpression(Integer.valueOf(elements[++i])); //通过上面两个数字解释器构造加法运算解释器 expressions.push(new AdditionExpression(exp1, exp2)); break; default://如果为数字 /** * 如果不是运算符则数字 * 则是数字,直接构造数字解释器并压入栈 */ expressions.push(new NumExpression(Integer.valueOf(elements[i]))); break; } } } /** * 计算 * @return */ public int calculate() { return expressions.pop().interpret(); } }
- 测试类:
public class Client { public static void main(String[] args) { Calculator calculator = new Calculator("100 + 30 + 5"); System.out.println(calculator.calculate()); } }
这里简单说一下Calculator里面的流程,以为”100 + 30 +5”为例:
首先将其拆分为五个元素组成的字符串数组。
循环遍历数组,首先遍历到的是100,那么将其构造成一个NumExpression对象压入栈。
接着遍历到了加号运算符,此时将我们刚刚压入栈的100出栈,作为加号运算符左边的数字解释器。
接着,将当前加号运算符下一个角标构造成一个数字解释器,作为加号运算符右边的数字解释器。
将左右两个数字解释器作为参数传入AdditionExpression构造一个加法解释器,同时压入栈中。
重复步骤3的操作
重复步骤4的操作。
最后调用interpret(),结果相当于exp1.interpret() + exp2.interpret()+exp3.interpret()
5.解释器模式在Android源码中的实现
解释器模式在Android源码中并不常见,这里就不做具体分析了。
6. 总结:
- 优点:
- 灵活的扩展性,当我们想对文法规则进行扩展时,只需要增加相应的非终结符号解释器,并在构建抽象语法树时,使用到新增的解释器对象进行具体的解释即可,非常方便。
- 缺点:
- 过于复杂的语法,构建其抽象语法树会显得异常繁琐,甚至有可能出现需要构建多棵抽象语法树的情况,因此,对于复杂的文法并不推荐使用解释器模式。
- 对于每一条文法都可以对应至少一个解释器,其会生成大量的类,导致后期维护困难。
-
两种语言实现设计模式(C++和Java)(十五:解释器模式)
2019-10-02 07:07:48解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式... -
【Java设计模式】解释器模式
2019-07-11 18:22:24概述 ◆ 定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 文法可以理解为语法 ...解释器模式-相关设计模式 ◆ 解释器模式和适配器... -
java设计模式——解释器模式
2021-01-19 23:06:59一、解释器模式的定义与类型 1、定义 给定一个语言,定义它的语法的一种表示,并定义一个解释器,这个解释器...五、解释器模式的相关设计模式 解释器模式和适配器模式。 六、解释器模式示例 以商品的优惠为例进行代 -
[设计模式]解释器模式
2020-04-17 08:40:30点我进入github 如需了解更多设计模式,请进入我的设计模式专栏 解释器模式 提供如何定义语言的文法,以及对语言句子的解释方法 ...扩展性好,由于在解释器模式中使用类来表示语言的文法规则,因... -
设计模式 - 解释器模式
2021-01-24 20:13:051.前言 在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。...解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句 -
Android编程设计模式之解释器模式详解
2021-01-04 18:52:54在这么多的设计模式中,解释器模式在实际运用上相对来说要少很多,因为我们很少会自己去构造一个语言的文法。虽然如此,既然它能够在设计模式中有一席之位,那么必定有它的可用之处。 二、定义 给定一个语言,定义它... -
设计模式之解释器模式
2020-09-28 17:50:15定义:解释器模式(InterPreter Pattern)给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 特征:为了解释一种语言,而为语言创建的解释器. 属于行为型模式 ... -
Android设计模式之——解释器模式
2017-10-29 20:10:20在这么多的设计模式中,解释器模式在实际运用上相对来说要少很多,因为我们很少会自己去构造一个语言的文法。虽然如此,既然它能够在设计模式中有一席之位,那么必定有它的可用之处。二、定义给定一个语言,定义它的... -
设计模式——解释器模式
2020-10-15 10:03:14给定一门语言,定义它文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子 解释器这个名词想必大家都不会陌生,比如编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元... -
设计模式-解释器模式
2020-06-11 18:53:18随着中国加入wto各国贸易频繁,不同的国度使用的语言不一样,但是在交流过程中很多国家以英文作为交流的对象,而翻译官就是将将两种不同的语言互相翻译,传达各自听得懂的语言,这里翻译,可以通过解释器模式一样来... -
21. 设计模式 解释器
2020-02-18 09:39:39解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,... -
设计模式是什么鬼(解释器)
2019-01-21 13:30:46原文链接:设计模式是什么鬼(解释器) 更多文章,欢迎访问:Java知音,一个专注于技术分享的网站 解释,一定是针对某种语言的拆解、释意,并按照文法翻译、转换成另一种表达形式以达到目标能够理解的目的。... -
JAVA设计模式之解释器模式
2021-02-10 17:57:33解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,... -
设计模式--解释器模式
2019-04-29 21:14:34定义:给定一个语言,定义它的文法(语法)的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 为了解释一种语言,而为语言创建的解释器 类型:行为型 适用场景:(1)某个特定类型问题... -
java设计模式之解释器模式
2019-08-25 14:24:55》定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 》为了解释一种语言,二位语言创建的解释器。 》类型:行为型 二、适用场景: 》某个特定类型问题... -
设计模式系列--解释器模式
2020-11-26 15:32:52在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可;若扩展语法时,只需添加相应非终结符类即可。 特征:为了解释一种语言,而为语言创建的解释器。 实现 给出一个运算... -
Java 设计模式--解释器模式
2020-05-23 13:44:40定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中句子 为了解释一种语言,而为语言创建的解释器 类型:行为型 2,适用场景 某个特定类型问题发生频率足够高 3,... -
小话设计模式(十九)解释器模式
2016-10-04 19:39:38解释器(Interpreter)给定一个语言,定义它的文法的一种表示,并且定义一个解释器,这个解释器使用该表示来解释语言中的句子。 很多脚本语言例如Lua、Ruby、Python都是解释器的一种应用。 -
设计思想学习—解释器模式
2018-01-15 19:55:54解释器模式解释器模式是最后一个模式了,而且接触到的名词也多,所以会把定义、UML和结构一起列完之后,统一说明理解。 解释器模式(Interpreter):给定一个语言之后,解释器模式可以定义出其文法的一种表示,并... -
python编译器与解释器_python – 编译器与解释器(基于构造和设计)
2021-01-15 00:59:19在查看了很多关于编译器和解释器之间差异的帖子后,我仍然无法弄清楚它们的构造和内部机制的区别.我读到的最常见的区别是编译器生成一个可执行的目标程序{意味着机器代码作为其输出},它可以在系统上运行而不是输入.而... -
自定义语言的实现——解释器模式(六)
2017-11-26 17:02:55解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,但是它在正则表达式、XML文档解释等领域还是得到了...
-
第一章 C语言 PTA 基本数据类型与表达式——作业-答案.html
-
【考研初试】安徽建筑大学501建筑设计考研真题库资料
-
深度学习自学第四周:近几年的经典神经网络结构
-
五指山(线性同余方程,扩展gcd)
-
删除本地镜像(5)
-
[全案]非付费推广之QQ群推广方案(2011年4月1日更新).pdf
-
2021 年该学的 CSS 框架 Tailwind CSS 实战视频
-
骑士人才招聘系统 5.2.6商业版.zip
-
Samba 服务配置与管理
-
第三章 C语言 PTA循环控制——作业-答案.html
-
MySQL 性能优化(思路拓展及实操)
-
leetcode string 14 最长公共前缀
-
Localyum配置
-
合同证明正版一元付费
-
基于python的dango框架购物商城毕业设计毕设源代码使用教程
-
基于遗传算法的平面阵列阵列稀疏(matlab程序).zip
-
git常用命令
-
第二章 分支程序结构设计——作业-答案.html
-
网上行销原则.txt
-
NFS 实现高可用(DRBD + heartbeat)