Created
May 28, 2014 13:21
-
-
Save matrixise/c70e7d9391128347ab50 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from pprint import pprint as pp | |
import varas | |
from varas import Tokenizer, ExprSpec, Assoc, Parser | |
( | |
NULL, | |
IDENTIFIER, | |
STRING, | |
NUMBER, | |
OPERATOR_AND, | |
OPERATOR_OR, | |
OPERATOR_BETWEEN, | |
OPERATOR_DOT, | |
OPERATOR_EQUAL, | |
OPERATOR_ILIKE, | |
OPERATOR_NOT, | |
OPERATOR_NOT_EQUAL, | |
OPERATOR_NOT_ILIKE, | |
LPAREN, | |
RPAREN, | |
) = range(15) | |
tokenizer = Tokenizer( | |
("\d+", NUMBER), | |
("\.", OPERATOR_DOT), | |
("=", OPERATOR_EQUAL), | |
('!=', OPERATOR_NOT_EQUAL), | |
('null', NULL), | |
('and', OPERATOR_AND), | |
('or', OPERATOR_OR), | |
('not ilike', OPERATOR_NOT_ILIKE), | |
('ilike', OPERATOR_ILIKE), | |
("not", OPERATOR_NOT), | |
("between", OPERATOR_BETWEEN), | |
("[a-zA-Z_]+", IDENTIFIER), | |
("'[a-zA-Z%]*'", STRING), | |
("\(", LPAREN), | |
("\)", RPAREN), | |
) | |
class Node(object): | |
def __repr__(self): | |
return '%s(value=%s)' % (self.__class__.__name__, self.value) | |
def to_domain(self): | |
return self.value | |
class Null(Node): | |
def __init__(self): | |
self.value = None | |
class Identifier(Node): | |
def __init__(self, value): | |
self.value = value | |
def to_domain(self): | |
return self.value | |
class String(Node): | |
def __init__(self, value): | |
self.value = value | |
def to_domain(self): | |
return self.value | |
class Not(Node): | |
def __init__(self, value): | |
self.value = value | |
def to_domain(self): | |
return ('!', self.value) | |
class Number(Node): | |
def __init__(self, value): | |
self.value = int(value) | |
def to_domain(self): | |
return self.value | |
class Binary(Node): | |
def __init__(self, left, right): | |
self.left = left | |
self.right = right | |
def __repr__(self): | |
return '%s(left=%s, right=%s)' % (self.__class__.__name__, self.left, self.right) | |
class Equal(Binary): | |
def to_domain(self): | |
if isinstance(self.right, Null): | |
return (self.left.to_domain(), '=', False) | |
else: | |
return (self.left.to_domain(), '=', self.right.to_domain()) | |
class NotEqual(Binary): | |
def to_domain(self): | |
if isinstance(self.right, Null): | |
return (self.left.to_domain(), '!=', False) | |
else: | |
return (self.left.to_domain(), '!=', self.right.to_domain()) | |
class Dot(Binary): | |
def to_domain(self): | |
return '%s.%s' % (self.left.to_domain(), self.right.to_domain()) | |
class And(Binary): | |
def to_domain(self): | |
return ('&', self.left.to_domain(), self.right.to_domain()) | |
class Or(Binary): | |
def to_domain(self): | |
return ('|', self.left.to_domain(), self.right.to_domain()) | |
class Ilike(Binary): | |
def to_domain(self): | |
return (self.left.to_domain(), 'ilike', self.right.to_domain()) | |
class NotIlike(Binary): | |
def to_domain(self): | |
return (self.left.to_domain(), 'not ilike', self.right.to_domain()) | |
class Between(Binary): | |
def __init__(self, left, min_value, max_value): | |
self.left = left | |
self.min_value = min_value | |
self.max_value = max_value | |
def __repr__(self): | |
return 'Between(%s, min=%s, max=%s)' % (self.left, self.min_value, self.max_value) | |
def to_domain(self): | |
return '&', (self.left, '>=', self.min_value.to_domain()), (self.left, '<=', self.max_value.to_domain()) | |
def handle_lparen(parser, actions, token): | |
expr = parser.expression(actions) | |
parser.match(RPAREN) | |
return expr | |
def handle_between(parser, actions, token, left_value): | |
left = parser.expression(actions, 80) | |
current_token = parser.match(OPERATOR_AND) | |
right = parser.expression(actions, 80) | |
return Between(left_value, left, right) | |
es = ExprSpec() | |
es.add_word(IDENTIFIER, lambda t: Identifier(t.content)) | |
es.add_word(STRING, lambda t: String(t.content)) | |
es.add_word(NUMBER, lambda t: Number(t.content)) | |
es.add_word(NULL, lambda t: Null()) | |
es.add_unary_op(OPERATOR_NOT, lambda t, r: Not(r) ) | |
es.add_binary_op(OPERATOR_DOT, 80, Assoc.LEFT, lambda t, l, r: Dot(l, r)) | |
es.add_binary_op(OPERATOR_EQUAL, 60, Assoc.LEFT, lambda t, l, r: Equal(l, r)) | |
es.add_binary_op(OPERATOR_NOT_EQUAL, 60, Assoc.LEFT, lambda t, l, r: NotEqual(l, r)) | |
es.add_binary_op(OPERATOR_NOT_ILIKE, 60, Assoc.LEFT, lambda t, l, r: NotIlike(l, r)) | |
es.add_binary_op(OPERATOR_ILIKE, 60, Assoc.LEFT, lambda t, l, r: Ilike(l, r)) | |
es.add_binary_op(OPERATOR_AND, 50, Assoc.LEFT, lambda t, l, r: And(l, r)) | |
es.add_binary_op(OPERATOR_OR, 50, Assoc.LEFT, lambda t, l, r: Or(l, r)) | |
es.add_infix_handler(OPERATOR_BETWEEN, 80, handle_between) | |
es.add_prefix_handler(LPAREN, handle_lparen) | |
program = "parent_id and user_id = 10 or (parent_id and parent_id.name not ilike '%dup%') and (create_date between yesterday and today)" | |
#program = """ | |
#user.parent.name = 10 and create_date between yesterday and today | |
#""" | |
domain = [('parent_id', '=', 10),('name', 'ilike', 'toto')] | |
pp(list(tokenizer.tokenize(program))) | |
def parse_expr(input): | |
return list(Parser(es, tokenizer.tokenize(input)).parse_all()) | |
result = parse_expr(program) | |
print result | |
from pprint import pprint as pp | |
pp(result, indent=4) | |
print result[0].to_domain() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment