- python使用textX构建属于自己的语言模型
- python使用textx-model包
文章目录
- 系列文章目录
- 一、textX是什么?
- 二、安装
- 三、快速介绍
- 四、定义一个可执行字符串表达式的模型
- 1. 定义元模型
- 2. 构建解析器
- 3. 构建完成
- 4. 使用构建的模型
- textx-model介绍
一、textX是什么?
textX是一种元语言,用于在Python中构建领域特定语言(DSL)。它的灵感来自Xtext。
简而言之,textX将帮助您以一种简单的方式构建文本语言。您可以发明自己的语言,或者对现有的文本语言或文件格式建立支持。
textX将从单一语言描述(语法)为该语言构建一个解析器和一个元模型(也称为抽象语法)。有关详细信息,请参阅文档。
textX遵循Xtext的语法和语义,但在某些地方有所不同,并使用Arpeggio-PEG解析器在Python中100%实现——没有语法歧义、无限制的前瞻性、解释器风格。
github上的地址:https://github.com/textX/textX
二、安装pip install textX
三、快速介绍下面是一个完整的示例,展示了用于绘图的简单DSL的定义。我们还展示了如何定义自定义类、解释模型和搜索特定类型的实例。
代码如下(示例):
rom textx import metamodel_from_str, get_children_of_type
grammar = """
Model: commands*=DrawCommand;
DrawCommand: MoveCommand | ShapeCommand;
ShapeCommand: LineTo | Circle;
MoveCommand: MoveTo | MoveBy;
MoveTo: 'move' 'to' position=Point;
MoveBy: 'move' 'by' vector=Point;
Circle: 'circle' radius=INT;
LineTo: 'line' 'to' point=Point;
Point: x=INT ',' y=INT;
"""
# We will provide our class for Point.
# Classes for other rules will be dynamically generated.
class Point(object):
def __init__(self, parent, x, y):
self.parent = parent
self.x = x
self.y = y
def __str__(self):
return "{},{}".format(self.x, self.y)
def __add__(self, other):
return Point(self.parent, self.x + other.x, self.y + other.y)
# Create meta-model from the grammar. Provide `Point` class to be used for
# the rule `Point` from the grammar.
mm = metamodel_from_str(grammar, classes=[Point])
model_str = """
move to 5, 10
line to 10, 10
line to 20, 20
move by 5, -7
circle 10
line to 10, 10
"""
# Meta-model knows how to parse and instantiate models.
model = mm.model_from_str(model_str)
# At this point model is a plain Python object graph with instances of
# dynamically created classes and attributes following the grammar.
def cname(o):
return o.__class__.__name__
# Let's interpret the model
position = Point(None, 0, 0)
for command in model.commands:
if cname(command) == 'MoveTo':
print('Moving to position', command.position)
position = command.position
elif cname(command) == 'MoveBy':
position = position + command.vector
print('Moving by', command.vector, 'to a new position', position)
elif cname(command) == 'Circle':
print('Drawing circle at', position, 'with radius', command.radius)
else:
print('Drawing line from', position, 'to', command.point)
position = command.point
print('End position is', position)
# Output:
# Moving to position 5,10
# Drawing line from 5,10 to 10,10
# Drawing line from 10,10 to 20,20
# Moving by 5,-7 to a new position 25,13
# Drawing circle at 25,13 with radius 10
# Drawing line from 25,13 to 10,10
# Collect all points starting from the root of the model
points = get_children_of_type("Point", model)
for point in points:
print('Point: {}'.format(point))
# Output:
# Point: 5,10
# Point: 10,10
# Point: 20,20
# Point: 5,-7
# Point: 10,10
四、定义一个可执行字符串表达式的模型
过程分两步进行:
- 定义元模型
- 构建解析器
新建一个 .tx格式的文件,文件内容如下:
Model:
assignments*=Assignment expression=Expression
;
Assignment:
variable=ID '=' expression=Expression ';'
;
Expression:
op=Not (op=Logic op=Not)*
;
Logic:
"and" | "or" | "in"
;
Not:
_not?='not' op=Compare
;
Compare:
op=AddSub (op=CompareSymbol op=AddSub)*
;
CompareSymbol:
"<=" | ">=" | ">" | "<" | "=" | "!="
;
AddSub:
op=Term (op=PlusOrMinus op=Term)*
;
PlusOrMinus:
'+' | '-'
;
Term:
op=Factor (op=MulOrDiv op=Factor)*
;
MulOrDiv:
'*' | '/'
;
Factor:
(sign=PlusOrMinus)? op=Operand
;
Operand:
op=NUMBER | op=BOOL | op=ID | op=STRING | ('(' op=Expression ')')
;
2. 构建解析器
新建一个py文件,文件内容如下:
from __future__ import unicode_literals
from textx import metamodel_from_file
namespace = {}
def str_to_list(str_data: str):
str_data = str_data
return str_data
class Model(object):
def __init__(self, **kwargs):
self.assignments = kwargs.pop('assignments')
self.expression = kwargs.pop('expression')
@property
def value(self):
# Evaluate variables in the order of definition
for a in self.assignments:
namespace[a.variable] = a.expression.value
return self.expression.value
class ExpressionElement(object):
def __init__(self, **kwargs):
# textX will pass in parent attribute used for parent-child
# relationships. We can use it if we want to.
self.parent = kwargs.get('parent', None)
# We have 'op' attribute in all grammar rules
self.op = kwargs['op']
super(ExpressionElement, self).__init__()
class Expression(ExpressionElement):
@property
def value(self):
ret = self.op[0].value
for operation, operand in zip(self.op[1::2], self.op[2::2]):
if operation == 'in':
tag = True
if ret[0] == '[':
ret = ret.lstrip('[')
if ret[-1] == ']':
ret = ret.rstrip(']').split(',')
for r in ret:
if r not in operand.value:
tag = False
break
ret = tag
else:
ret = ret in operand.value
else:
ret = ret in operand.value
elif operation == 'and':
ret = ret and operand.value
elif operation == 'or':
ret = ret or operand.value
elif operation == 'not':
ret = not operand.value
return ret
class Compare(ExpressionElement):
@property
def value(self):
ret = self.op[0].value
for operation, operand in zip(self.op[1::2], self.op[2::2]):
if operation == '<':
ret = ret < operand.value
elif operation == '>':
ret = ret > operand.value
elif operation == '=':
ret = ret == operand.value
elif operation == '>=':
ret = ret >= operand.value
elif operation == '<=':
ret = ret <= operand.value
elif operation == '!=':
ret = ret != operand.value
return ret
class Not(ExpressionElement):
def __init__(self, **kwargs):
self._not = kwargs.pop('_not')
super(Not, self).__init__(**kwargs)
@property
def value(self):
ret = self.op.value
return not ret if self._not else ret
class AddSub(ExpressionElement):
@property
def value(self):
ret = self.op[0].value
for operation, operand in zip(self.op[1::2], self.op[2::2]):
if operation == '+':
ret += operand.value
else:
ret -= operand.value
return ret
class Term(ExpressionElement):
@property
def value(self):
ret = self.op[0].value
for operation, operand in zip(self.op[1::2], self.op[2::2]):
if operation == '*':
ret *= operand.value
else:
ret /= operand.value
return ret
class Factor(ExpressionElement):
def __init__(self, **kwargs):
self.sign = kwargs.pop('sign', '+')
super(Factor, self).__init__(**kwargs)
@property
def value(self):
value = self.op.value
return -value if self.sign == '-' else value
class Operand(ExpressionElement):
@property
def value(self):
op = self.op
if op in namespace:
return namespace[op]
elif type(op) in {int, float, bool, str}:
return op
elif isinstance(op, ExpressionElement):
return op.value
else:
raise Exception('Unknown variable "{}" at position {}'
.format(op, self._tx_position))
# Meta.tx 为上面定义的元模型
meta = metamodel_from_file('Meta.tx',
classes=[Model, Expression, Not, Compare, AddSub, Term, Factor, Operand])
3. 构建完成
经过上面两个步骤,生成两个文件就代表模型已构建完成
4. 使用构建的模型
文件结构如上图: Meta.tx 是元模型, Model.py 是构建的解析器。
代码如下(示例)
from example.Model import meta
# 该模型用于判断字符串表达式
# 字符串表达式
rule = "(current_medical_program = '白细胞计数') and (specimen_type = '全血') and "
"(medicine_projects in '[1,2,3]') and (time < 6)"
# 正例数据
true_example = "current_medical_program = '白细胞计数';specimen_type = '全血';medicine_projects = '2';time = 5;"
# 反例数据
false_example = "current_medical_program = '白细胞计数';specimen_type = '全血';medicine_projects = '2';time = 8;"
print(meta.model_from_str(true_example + rule).value) # 结果:True
print(meta.model_from_str(false_example + rule).value) # 结果:False
textx-model介绍
textx-model是以textX为基础构建出可运行条件规则语句然后输出结果的模型,文章链接:https://blog.csdn.net/weixin_44836662/article/details/124812067



