Edit: got rid of the rest of sloooooow recursion thanks to this article and added keyword options.
from lepl import * def ander(result): if len(result) == 2: return (result[0], result[1]) return result[0] text = String() | Word() andClausePrime = Delayed() label = text & Drop(':') with DroppedSpace(): parameter = label & text > (lambda r: {r[0]: r[1]}) andClause = (parameter | text) & andClausePrime > ander andClausePrime += (Drop('AND') & (andClause | parameter | text) & andClausePrime)[:] expr = andClause | parameter | text query = expr & (Drop('OR') & expr)[:]
Results:
>>> query.parse('a AND b') [('a', 'b')] >>> query.parse('a AND b AND c') [('a', ('b', 'c'))] >>> query.parse('a AND b AND c AND d') [('a', ('b', ('c', 'd')))] >>> query.parse('a AND b AND c AND d OR e AND f') [('a', ('b', ('c', 'd'))), ('e', 'f')] >>> query.parse('a AND b AND c AND d OR e OR f') [('a', ('b', ('c', 'd'))), 'e', 'f'] >>> query.parse('foo AND bar') [('foo', 'bar')] >>> query.parse('"bar none" OR foo') ['bar none', 'foo'] >>> query.parse('key:value AND "hey now":5 OR "what is":up') [({'key': 'value'}, {'hey now': '5'}), {'what is': 'up'}]
source share