Here what I got for now :
from pyparsing import nestedExpr
# Not the worse case but has enough complexity to start with
grouped_query_string = \
'((((a_table.integer_field > 1) & ' \
' (a_table.integer_field not equal 2) &' \
' (~a_table.integer_field in "200, 300, 400")) & ' \
'(a_table.integer_field =< 3000)) | ' \
'(a_table.boolean_field = "True"))'
list_of_nested_query_string = nestedExpr('(', ')').parseString(grouped_query_string).asList()
ops_translation = {
'==': '=',
'!=': '!=',
'<': '<',
'>': '>',
'<=': '<=',
'>=': '>=',
'<>': '!=',
'=<': '<=',
'=>': '>=',
'=': '=',
'less or equal than': '<=',
'greater or equal than': '>=',
'equal or less than': '<=',
'equal or greater than': '>=',
'less or equal': '<=',
'greater or equal': '>=',
'equal or less': '<=',
'equal or greater': '>=',
'not equal to': '!=',
'not equal': '!=',
'equal to': '=',
'equal': '=',
'equals': '=',
'less than': '<',
'greater than': '>',
'starts with': 'startswith', # ??
'ends with': 'endswith', # ??
'not in': 'notbelongs', # ??
'in': 'belongs', # ??
'is': '='
}
# Base traverse() function which I derive the rest
# def traverse(o, tree_types=(list, tuple)):
# if isinstance(o, tree_types):
# for value in o:
# for subvalue in traverse(value):
# yield subvalue
# else:
# yield o
# Here the beast
queries = {}
nested = []
def traverse(o, tree_types=(list, tuple)):
ops = ('&', '|')
q = None
negative = None
if isinstance(o, tree_types):
# nested.append('()')
# if len(o) > 2 and o[1] in ops:
# nested.append(o[1])
if len(o) > 2 and o[1] not in ops:
print o
table, field_name = o[0].split('.')
if table[0] == '~':
negative = True
table = table[1:]
field = db[table][field_name]
op = ops_translation[' '.join(o[1:-1])]
value = o[-1]
if op == '=':
q = field == value
elif op == '<':
q = field < value
elif op == '>':
q = field > value
elif op == '<=':
q = field <= value
elif op == '>=':
q = field >= value
elif op == '!=':
q = field != value
elif op == 'belongs':
q = field.belongs(value[1:-1].split(','))
elif op == 'notbelongs':
q = ~field.belongs(value[1:-1].split(','))
elif field.type in ('text', 'string', 'json'):
if op == 'contains':
q = field.contains(value)
elif op == 'like':
q = field.ilike(value)
elif op == 'startswith':
q = field.startswith(value)
elif op == 'endswith':
q = field.endswith(value)
else:
raise RuntimeError("Invalid operation")
queries[tuple(o)] = q
print q
for value in o:
for subvalue in traverse(value):
yield subvalue
else:
yield o
# For invoquing the generator
for e in traverse(list_of_nested_query_string): e # Notting come out except print
# Then have a look to queries
I build a dict of tuple query has key to try to replace the sublist in the "list_of_nested_query_string", but then I need to traverse it again and build nested query in another traverse derived function... Not clean code...
# Here model definition for testing purpose
db.define_table('a_table',
Field('string_field', 'string'),
Field('text_field', 'text'),
Field('boolean_field', 'boolean'),
Field('integer_field', 'integer'),
Field('double_field', 'double'),
# Field('decimal_field', 'decimal'),
# Field('date_field', 'date'),
# Field('time_field', 'time'),
# Field('datetime_field', 'datetime'),
# Field('reference_field', 'reference referred_table'),
# Field('list_string_field', 'list:string'),
# Field('list_integer_field', 'list:integer'),
# Field('list_reference_field', 'list:reference referred_table')
)
fields = [db.a_table.string_field,
db.a_table.text_field,
db.a_table.boolean_field,
db.a_table.integer_field,
db.a_table.double_field,
# db.a_table.decimal_field,
# db.a_table.date_field,
# db.a_table.time_field,
# db.a_table.reference_field,
# db.a_table.list_string_field,
# db.a_table.list_integer_field,
# db.a_table.list_reference_field
]