Skip to content

Instantly share code, notes, and snippets.

@matrixise
Created May 28, 2014 13:21
Show Gist options
  • Save matrixise/c70e7d9391128347ab50 to your computer and use it in GitHub Desktop.
Save matrixise/c70e7d9391128347ab50 to your computer and use it in GitHub Desktop.
#!/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