繁体   English   中英

pyparsing-如何使用比较运算符解析字符串?

[英]pyparsing - How to parse string with comparison operators?

因此,我有一个NumericStringParser类(从此处提取),定义如下:

from __future__ import division
from pyparsing import Literal, CaselessLiteral, Word, Combine, Group, Optional, ZeroOrMore, Forward, nums, alphas, oneOf, ParseException
import math
import operator

class NumericStringParser(object):

    def __push_first__(self, strg, loc, toks):
        self.exprStack.append(toks[0])

    def __push_minus__(self, strg, loc, toks):
        if toks and toks[0] == "-":
            self.exprStack.append("unary -")

    def __init__(self):
        point = Literal(".")
        e = CaselessLiteral("E")
        fnumber = Combine(Word("+-" + nums, nums) +
                          Optional(point + Optional(Word(nums))) +
                          Optional(e + Word("+-" + nums, nums)))
        ident = Word(alphas, alphas + nums + "_$")
        plus = Literal("+")
        minus = Literal("-")
        mult = Literal("*")
        floordiv = Literal("//")
        div = Literal("/")
        mod = Literal("%")
        lpar = Literal("(").suppress()
        rpar = Literal(")").suppress()
        addop = plus | minus
        multop = mult | floordiv | div | mod
        expop = Literal("^")
        pi = CaselessLiteral("PI")
        tau = CaselessLiteral("TAU")
        expr = Forward()
        atom = ((Optional(oneOf("- +")) +
                 (ident + lpar + expr + rpar | pi | e | tau | fnumber).setParseAction(self.__push_first__))
                | Optional(oneOf("- +")) + Group(lpar + expr + rpar)
                ).setParseAction(self.__push_minus__)

        factor = Forward()
        factor << atom + \
            ZeroOrMore((expop + factor).setParseAction(self.__push_first__))
        term = factor + \
            ZeroOrMore((multop + factor).setParseAction(self.__push_first__))
        expr << term + \
            ZeroOrMore((addop + term).setParseAction(self.__push_first__))

        self.bnf = expr

        self.opn = {
            "+": operator.add,
            "-": operator.sub,
            "*": operator.mul,
            "/": operator.truediv,
            "//": operator.floordiv,
            "%": operator.mod,
            "^": operator.pow,
            "=": operator.eq,
            "!=": operator.ne,
            "<=": operator.le,
            ">=": operator.ge,
            "<": operator.lt,
            ">": operator.gt
            }

        self.fn = {
            "sin": math.sin,
            "cos": math.cos,
            "tan": math.tan,
            "asin": math.asin,
            "acos": math.acos,
            "atan": math.atan,
            "exp": math.exp,
            "abs": abs,
            "sqrt": math.sqrt,
            "floor": math.floor,
            "ceil": math.ceil,
            "trunc": math.trunc,
            "round": round,
            "fact": factorial,
            "gamma": math.gamma
            }

    def __evaluate_stack__(self, s):
        op = s.pop()
        if op == "unary -":
            return -self.__evaluate_stack__(s)
        if op in ("+", "-", "*", "//", "/", "^", "%", "!=", "<=", ">=", "<", ">", "="):
            op2 = self.__evaluate_stack__(s)
            op1 = self.__evaluate_stack__(s)
            return self.opn[op](op1, op2)
        if op == "PI":
            return math.pi
        if op == "E":
            return math.e
        if op == "PHI":
            return phi
        if op == "TAU":
            return math.tau
        if op in self.fn:
            return self.fn[op](self.__evaluate_stack__(s))
        if op[0].isalpha():
            raise NameError(f"{op} is not defined.")
        return float(op)

我有一个evaluate()函数,定义如下:

def evaluate(expression, parse_all=True):
    nsp = NumericStringParser()
    nsp.exprStack = []
    try:
        nsp.bnf.parseString(expression, parse_all)
    except ParseException as error:
        raise SyntaxError(error)
    return nsp.__evaluate_stack__(nsp.exprStack[:])

evaluate()是一个函数,它将解析字符串以计算数学运算,例如:

>>> evaluate("5+5")
10

>>> evaluate("5^2+1")
26

问题是它无法计算比较运算符( =!=<><=>= ),并且当我尝试: evaluate("5=5") ,它将引发SyntaxError: Expected end of text (at char 1), (line:1, col:2)而不是返回True 函数如何计算这六个比较运算符?

正如@rici指出的那样,您已添加了评估部分,但未添加解析部分。

解析器在以下几行中定义:

    factor = atom + \
        ZeroOrMore((expop + factor).setParseAction(self.__push_first__))
    term = factor + \
        ZeroOrMore((multop + factor).setParseAction(self.__push_first__))
    expr <<= term + \
        ZeroOrMore((addop + term).setParseAction(self.__push_first__))

这些语句的顺序很重要,因为它们使解析器识别您在中学数学中学到的运算的优先级。 也就是说,乘幂最高,然后是乘法和除法,然后是加法和减法。

您需要按照相同的模式将关系运算符插入此解析器定义。 添加之后,来自C语言运算符优先级的约定(我找到此参考-https : //www.tutorialspoint.com/cprogramming/c_operators_precedence.htm )为:

relational operations - <=, >=, >, <
equality operations - ==, !=

在您的情况下,您选择使用'='而不是'==',在这种情况下应该可以。 我建议您使用pyparsing的oneOf助手来定义这些运算符组,因为它将处理短字符串可能掩盖较长字符串的情况(例如,在上一篇文章中'/'掩盖了'//')。

请注意,通过将所有这些操作混合到一个表达式解析器中,您将获得5 + 2 > 3 由于'>'的优先级较低,因此将首先计算5 + 2并给出7,然后将评估7> 3,并且operator.__gt__将返回1或0。

将此示例扩展到其他运算符的困难是导致我在infixNotation编写infixNotation helper方法的原因。 您可能想看看。

编辑:

您询问有关使用Literal('<=') | Literal('>=) | etc. Literal('<=') | Literal('>=) | etc. Literal('<=') | Literal('>=) | etc. ,正如您所写的那样,它将正常工作。 您只需要注意要在较短的运算符之前寻找较长的运算符。 如果您写Literal('>') | Literal('>=') | ... Literal('>') | Literal('>=') | ... Literal('>') | Literal('>=') | ...然后匹配'> ='将失败,因为第一个匹配将匹配'>',然后您将剩下'='。 使用oneOf可以帮助您解决此问题。

要添加其他解析器步骤,只需要对最后一级执行expr <<= ...步骤。 再次查看语句模式。 更改expr <<= term + etc. ,以arith_expr = term + etc. ,遵循它来添加水平relational_exprequality_expr ,然后完成expr <<= equality_expr

此模式基于:

factor := atom (^ atom)...
term := factor (mult_op factor)...
arith_expr := term (add_op term)...
relation_expr := arith_expr (relation_op arith_expr)...
equality_expr := relation_expr (equality_op relation_expr)...

尝试自行转换为Python / pyparsing。

factor << atom + \
    ZeroOrMore((expop + factor).setParseAction(self.__push_first__))
term = factor + \
    ZeroOrMore((multop + factor).setParseAction(self.__push_first__))
arith_expr = term + \
    ZeroOrMore((addop + term).setParseAction(self.__push_first__))
relational = arith_expr + \
    ZeroOrMore((diffop + arith_expr).setParseAction(self.__push_first__))
expr <<= relational + \
    ZeroOrMore((compop + relational).setParseAction(self.__push_first__))

所以我测试了,它有效! 非常感谢PaulMcG! :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM