Search code examples
pythonsyntaxpyparsing

Utility for syntax validation


I have very unique situation where the syntax needs to be validated only for the AND,OR,NOT without any value assignment using any programming language.Any direct utilities would be great.Any suggesstions are appreciated.

Example q or not (p and r) -Valid q not or p or q -Not Valid

My starting point is this any suggestions on top this.It parses but I am trying to figure out a way to find valid vs not valid.

import pyparsing as pp

operator = pp.Regex("AND|OR|NOT").setName("operator")
number = pp.Regex(r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?")
identifier = pp.Word(pp.alphas, pp.alphanums + "_")
comparison_term = identifier | number 
condition = pp.Group(comparison_term + operator + comparison_term)

expr = pp.operatorPrecedence(condition,[
                            ("NOT", 1, pp.opAssoc.RIGHT, ),
                            ("AND", 2, pp.opAssoc.LEFT, ),
                            ("OR", 2, pp.opAssoc.LEFT, ),
                            ])

#x=expr.parseString("P AND Q OR X AND Y")
x=expr.parseString("P AND Q NOT X AND Y")

print(x)

Solution

  • I think you meant to define operator here as:

    operator = pp.oneOf("< > = <= >= !=").setName("operator")
    

    and to get your parser to match your input strings, condition must also accept just plain identifiers:

    condition = pp.Group(comparison_term + operator + comparison_term) | identifier
    

    Now you can run your parser using expr.runTests:

    expr.runTests("""\
        P AND Q NOT X AND Y
        P AND Q AND NOT X AND Y
        P AND Q > 1000 OR NOT Z
        """, fullDump=False)
    

    prints:

    P AND Q NOT X AND Y
            ^
    FAIL: Expected end of text, found 'N'  (at char 8), (line:1, col:9)
    
    P AND Q AND NOT X AND Y
    [['P', 'AND', 'Q', 'AND', ['NOT', 'X'], 'AND', 'Y']]
    
    P AND Q > 1000 OR NOT Z
    [[['P', 'AND', ['Q', '>', '1000']], 'OR', ['NOT', 'Z']]
    

    EDIT: To capture parsing errors, you would not use runTests (though runTests is good for parser testing). Instead you would call parseString inside a Python try-except block, and capture ParseExceptions:

    try:
        results = expr.parseString(user_input)
    except ParseException as pe:
        print("error at", pe.loc)
    

    ParseExceptions have helpful attributes like lineno, col, loc and line. You can read more about how to use them here https://pyparsing-docs.readthedocs.io/en/pyparsing_2.4.6/pyparsing.html?highlight=ParseException#pyparsing.ParseException .