1from __future__ import unicode_literals 2 3import re 4from decimal import Decimal 5 6import ply.yacc as yacc 7 8from .ast import * # noqa 9from .compat import binary_type, text_type 10from .exceptions import DjangoQLParserError 11from .lexer import DjangoQLLexer 12 13 14unescape_pattern = re.compile( 15 '(' + DjangoQLLexer.re_escaped_char + '|' + 16 DjangoQLLexer.re_escaped_unicode + ')' 17) 18 19 20def unescape_repl(m): 21 contents = m.group(1) 22 if len(contents) == 2: 23 return contents[1] 24 else: 25 return contents.encode('utf8').decode('unicode_escape') 26 27 28def unescape(value): 29 if isinstance(value, binary_type): 30 value = value.decode('utf8') 31 return re.sub(unescape_pattern, unescape_repl, value) 32 33 34class DjangoQLParser(object): 35 def __init__(self, debug=False, **kwargs): 36 self.default_lexer = DjangoQLLexer() 37 self.tokens = self.default_lexer.tokens 38 kwargs['debug'] = debug 39 if 'write_tables' not in kwargs: 40 kwargs['write_tables'] = False 41 self.yacc = yacc.yacc(module=self, **kwargs) 42 43 def parse(self, input=None, lexer=None, **kwargs): 44 lexer = lexer or self.default_lexer 45 return self.yacc.parse(input=input, lexer=lexer, **kwargs) 46 47 start = 'expression' 48 49 def p_expression_parens(self, p): 50 """ 51 expression : PAREN_L expression PAREN_R 52 """ 53 p[0] = p[2] 54 55 def p_expression_logical(self, p): 56 """ 57 expression : expression logical expression 58 """ 59 p[0] = Expression(left=p[1], operator=p[2], right=p[3]) 60 61 def p_expression_comparison(self, p): 62 """ 63 expression : name comparison_number number 64 | name comparison_string string 65 | name comparison_equality boolean_value 66 | name comparison_equality none 67 | name comparison_in_list const_list_value 68 """ 69 p[0] = Expression(left=p[1], operator=p[2], right=p[3]) 70 71 def p_name(self, p): 72 """ 73 name : NAME 74 """ 75 p[0] = Name(parts=p[1].split('.')) 76 77 def p_logical(self, p): 78 """ 79 logical : AND 80 | OR 81 """ 82 p[0] = Logical(operator=p[1]) 83 84 def p_comparison_number(self, p): 85 """ 86 comparison_number : comparison_equality 87 | comparison_greater_less 88 """ 89 p[0] = p[1] 90 91 def p_comparison_string(self, p): 92 """ 93 comparison_string : comparison_equality 94 | comparison_greater_less 95 | comparison_contains 96 """ 97 p[0] = p[1] 98 99 def p_comparison_equality(self, p): 100 """ 101 comparison_equality : EQUALS 102 | NOT_EQUALS 103 """ 104 p[0] = Comparison(operator=p[1]) 105 106 def p_comparison_greater_less(self, p): 107 """ 108 comparison_greater_less : GREATER 109 | GREATER_EQUAL 110 | LESS 111 | LESS_EQUAL 112 """ 113 p[0] = Comparison(operator=p[1]) 114 115 def p_comparison_contains(self, p): 116 """ 117 comparison_contains : CONTAINS 118 | NOT_CONTAINS 119 """ 120 p[0] = Comparison(operator=p[1]) 121 122 def p_comparison_in_list(self, p): 123 """ 124 comparison_in_list : IN 125 | NOT IN 126 """ 127 if len(p) == 2: 128 p[0] = Comparison(operator=p[1]) 129 else: 130 p[0] = Comparison(operator='%s %s' % (p[1], p[2])) 131 132 def p_const_value(self, p): 133 """ 134 const_value : number 135 | string 136 | none 137 | boolean_value 138 """ 139 p[0] = p[1] 140 141 def p_number_int(self, p): 142 """ 143 number : INT_VALUE 144 """ 145 p[0] = Const(value=int(p[1])) 146 147 def p_number_float(self, p): 148 """ 149 number : FLOAT_VALUE 150 """ 151 p[0] = Const(value=Decimal(p[1])) 152 153 def p_string(self, p): 154 """ 155 string : STRING_VALUE 156 """ 157 p[0] = Const(value=unescape(p[1])) 158 159 def p_none(self, p): 160 """ 161 none : NONE 162 """ 163 p[0] = Const(value=None) 164 165 def p_boolean_value(self, p): 166 """ 167 boolean_value : true 168 | false 169 """ 170 p[0] = p[1] 171 172 def p_true(self, p): 173 """ 174 true : TRUE 175 """ 176 p[0] = Const(value=True) 177 178 def p_false(self, p): 179 """ 180 false : FALSE 181 """ 182 p[0] = Const(value=False) 183 184 def p_const_list_value(self, p): 185 """ 186 const_list_value : PAREN_L const_value_list PAREN_R 187 """ 188 p[0] = List(items=p[2]) 189 190 def p_const_value_list(self, p): 191 """ 192 const_value_list : const_value_list COMMA const_value 193 """ 194 p[0] = p[1] + [p[3]] 195 196 def p_const_value_list_single(self, p): 197 """ 198 const_value_list : const_value 199 """ 200 p[0] = [p[1]] 201 202 def p_error(self, token): 203 if token is None: 204 self.raise_syntax_error('Unexpected end of input') 205 else: 206 fragment = text_type(token.value) 207 if len(fragment) > 20: 208 fragment = fragment[:17] + '...' 209 self.raise_syntax_error( 210 'Syntax error at %s' % repr(fragment), 211 token=token, 212 ) 213 214 def raise_syntax_error(self, message, token=None): 215 if token is None: 216 raise DjangoQLParserError(message) 217 lexer = token.lexer 218 if callable(getattr(lexer, 'find_column', None)): 219 column = lexer.find_column(token) 220 else: 221 column = None 222 raise DjangoQLParserError( 223 message=message, 224 value=token.value, 225 line=token.lineno, 226 column=column, 227 ) 228