赞
踩
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 ')') ;
新建一个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])
经过上面两个步骤,生成两个文件就代表模型已构建完成
文件结构如上图: 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为基础构建出可运行条件规则语句然后输出结果的模型,文章链接:https://blog.csdn.net/weixin_44836662/article/details/124812067
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。