Skip to content


Pyparsing introduction: BNF to code

Here’s some code to implement an expression parser:

from pyparsing import Word, nums
from string import lowercase

nonzero = ''.join([str(i) for i in range(1, 10)])
integer = Word(nonzero, nums)

varname = Word(lowercase)

equals = '='

operator = Word('+-*/', exact=1)
operand = (integer ^ varname)
unaryoperation = operand
binaryoperation = (operand + operator + operand)
operation = unaryoperation ^ binaryoperation

expression = varname + equals + operation

def main():
    for i in ('foo = 1', 'bar = 1 + 2', 'baz = foo + bar', 'bat = baz', 'foo = baz / 2'):
    print '%s: %s' % (i, str(expression.parseString(i)))

if __name__ == '__main__':
    main()

The output:

foo = 1: ['foo', '=', '1']
bar = 1 + 2: ['bar', '=', '1', '+', '2']
baz = foo + bar: ['baz', '=', 'foo', '+', 'bar']
bat = baz: ['bat', '=', 'baz']
foo = baz / 2: ['foo', '=', 'baz', '/', '2']

Before creating our little calculator program, one minor enhancement can be done: as we know the second element in the returned array-like object will always be a ‘=’ sign, we can ignore this. This can be easily expressed in our code: every Pyparsing object got a ‘suppress’ method. Currently our equals variable is a plain Python string, so we first need to convert this into a Pyparsing object. The object type to use in this case is ‘Literal’. Add this in the import statement, use a Literal object instead of a string in the expression definition, and suppress it:

expression = varname + Literal(equals).suppress() + operation

Now re-check the output of the script.

Posted in Development.

Tagged with , , , .


8 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. sb says

    Six pages?? Ouch. And no next button. Any chance you could put it all on one page next time? I feel like I’m reading some ad-infested hardware blog.

  2. jauco says

    very nice tutorial! thanks!

  3. Olivier Berger says

    What’s the difference to other parser systems like simpleparse ?

    Regards,

  4. Francis says

    I don’t see the difference in (except for the whitespaces)

    print sentence.parseString(‘hello world’) # notice >1 spaces
    # returns ['hello', 'world']
    print sentence.parseString(‘Hello world’)
    # raises a ParseException

    Why does the second one raise an exception ?

  5. Nicolas says

    Francis: I guess you’re referring to the snippet on page 2? It says:

    from pyparsing import OneOrMore
    sentence = OneOrMore(word)

    The definition of ‘word’ is given on the previous page:

    word = Word(lowercase)

    where ‘lowercase’ is imported from the ‘string’ module and equals

    abcdefghijklmnopqrstuvwxyz

    The definition of the BNF type ‘word’ is Word(lowercase), ie. a concatenation of any character in the string (or list, so you want) ‘lowercase’, which is a-z.

    A sentence is defined as OneOrMore words.

    The string ‘Hello world’ can not be parsed since it does not match OneOrMore(word): the first item in it (‘Hello’) contains characters not matching the definition of word: the ‘H’ (since we defined a word to be a concatenation of lowercase characters, it shouldn’t contain any uppercase characters).

    As you can see, on page 3 a better definition of sentence is constructed using a ‘startword’ definition which should be a concatenation of one uppercase character, followed by zero or more lowercase characters.The example shows ‘A valid sentence.’ can be parsed and validated. The string ‘Hello world!’ would be valid in this BNF construct too. ‘Hello world’ would not match since we’re missing a punctuation sign.

    Using the definitions from page 3

    almost_valid_sentence = startword + body

    or (even more limited)

    hello_caps = startword + word

    would validate and parse ‘Hello world’.

  6. GDR! says

    Good introduction – thank you!

    Although I share the feelings of “sb” about pagination.

  7. lerry says

    hi poh,, what if the expr is like this A=B+c?

  8. Wayne says

    Good introduction to pyparsing. Thanks Nicolas!



Some HTML is OK

or, reply to this post via trackback.