Search code examples
pythonhaskellpyparsingparsec

choose parser based on previous results


I'm parsing several outputs and these files have two different headers:

header1 = " MO EIGENVALUES, MO OCCUPATION NUMBERS, AND CARTESIAN MO EIGENVECTORS AFTER SCF STEP -1"

header2 =  "MO EIGENVALUES, MO OCCUPATION NUMBERS, AND SPHERICAL MO EIGENVECTORS AFTER SCF STEP -1"

Based on the statement that the results are in CARTESIAN or SPHERICAL coordinates, I want to apply the corresponding parser.

In Haskell I can write a parser that decides what to do next based on previous results as follows,

myparser = do
    xs <- someParser
    if xs == "foo"
       then parser1
       else parser2

How can I create the same function in python using pyparsing?

note: I don't know a priori if an output is on cartesian or spherical coordinates.


Solution

  • I think in pyparsing one would just write:

    oneParserToRuleThemAll = header1 + parser1 | header2 + parser2
    

    If the header line matches 'header1', then pyparsing will continue on and use parser1 for the rest of the parsing. Otherwise it will try to match 'header2' and if that matches, will use parser2.

    One can definitely get more exotic using dynamic parser elements and parse actions. That would look like this:

    foo_parser = ...
    bar_parser = ...
    
    variable_parser = Forward()
    switch_parser = Literal("foo") | Literal("bar")
    def select_variable_parser(tokens):
        if tokens[0] == "foo":
            variable_parser <<= foo_parser
        if tokens[0] == "bar":
            varaible_parser <<= bar_parser
    switch_parser.setParseAction(select_variable_parser)
    parser = switch_parser + variable_parser
    

    Note the use of the "shift-into" operator <<= to define the variable part of the parser by inserting into the previously-defined Forward().

    But this is easier to follow, I think:

    parser = "foo" + foo_parser | "bar" + bar_parser