22
22
23
23
from __future__ import absolute_import
24
24
from __future__ import unicode_literals
25
+ from __future__ import print_function
25
26
26
27
import inspect
27
28
import itertools
31
32
except NameError :
32
33
basestring = str # Python 3
33
34
35
+ # Set to True to enable tracing for parsing
36
+ TRACE_PARSE = False
34
37
35
38
# Token types for standard operators and parens
36
39
TOKEN_AND = 1
59
62
PARSE_UNBALANCED_CLOSING_PARENS = 2
60
63
PARSE_INVALID_EXPRESSION = 3
61
64
PARSE_INVALID_NESTING = 4
65
+ PARSE_INVALID_SYMBOL_SEQUENCE = 5
62
66
63
67
PARSE_ERRORS = {
64
68
PARSE_UNKNOWN_TOKEN : 'Unknown token' ,
65
69
PARSE_UNBALANCED_CLOSING_PARENS : 'Unbalanced parenthesis' ,
66
70
PARSE_INVALID_EXPRESSION : 'Invalid expression' ,
67
- PARSE_INVALID_NESTING : 'Invalid expression nesting such as (AND xx)'
71
+ PARSE_INVALID_NESTING : 'Invalid expression nesting such as (AND xx)' ,
72
+ PARSE_INVALID_SYMBOL_SEQUENCE : 'Invalid symbols sequence such as (A B)' ,
68
73
}
69
74
70
75
@@ -197,27 +202,54 @@ def parse(self, expr, simplify=False):
197
202
else :
198
203
tokenized = iter (expr )
199
204
205
+ if TRACE_PARSE :
206
+ tokenized = list (tokenized )
207
+ print ('tokens:' )
208
+ map (print , tokenized )
209
+ tokenized = iter (tokenized )
210
+
200
211
ast = [None , None ]
201
212
213
+ def is_sym (_t ):
214
+ return _t == TOKEN_SYMBOL or isinstance (_t , Symbol )
215
+
202
216
prev = None
203
217
for tok in tokenized :
218
+ if TRACE_PARSE : print ('\n processing token:' , repr (tok ))
204
219
token , tokstr , position = tok
220
+
221
+ if prev :
222
+ prev_token , _ , _ = prev
223
+ if is_sym (prev_token ) and is_sym (token ):
224
+ raise ParseError (token , tokstr , position , PARSE_INVALID_SYMBOL_SEQUENCE )
225
+
205
226
if token == TOKEN_SYMBOL :
206
227
ast .append (self .Symbol (tokstr ))
228
+ if TRACE_PARSE : print (' ast: token == TOKEN_SYMBOL: append new symbol' , repr (ast ))
229
+
207
230
elif isinstance (token , Symbol ):
208
231
ast .append (token )
232
+ if TRACE_PARSE : print (' ast: isinstance(token, Symbol): append existing symbol' , repr (ast ))
209
233
210
234
elif token == TOKEN_TRUE :
211
235
ast .append (self .TRUE )
236
+ if TRACE_PARSE : print ('ast4:' , repr (ast ))
237
+
212
238
elif token == TOKEN_FALSE :
213
239
ast .append (self .FALSE )
240
+ if TRACE_PARSE : print ('ast5:' , repr (ast ))
214
241
215
242
elif token == TOKEN_NOT :
216
243
ast = [ast , self .NOT ]
244
+ if TRACE_PARSE : print ('ast6:' , repr (ast ))
245
+
217
246
elif token == TOKEN_AND :
218
247
ast = self ._start_operation (ast , self .AND , precedence )
248
+ if TRACE_PARSE : print (' ast: token == TOKEN_AND: start_operation' , repr (ast ))
249
+
219
250
elif token == TOKEN_OR :
220
251
ast = self ._start_operation (ast , self .OR , precedence )
252
+ if TRACE_PARSE : print (' ast: token == TOKEN_OR: start_operation' , repr (ast ))
221
253
222
254
elif token == TOKEN_LPAR :
223
255
if prev :
@@ -227,13 +259,16 @@ def parse(self, expr, simplify=False):
227
259
if ptoktype not in (TOKEN_NOT , TOKEN_AND , TOKEN_OR , TOKEN_LPAR ):
228
260
raise ParseError (token , tokstr , position , PARSE_INVALID_NESTING )
229
261
ast = [ast , TOKEN_LPAR ]
262
+
230
263
elif token == TOKEN_RPAR :
231
264
while True :
232
265
if ast [0 ] is None :
233
266
raise ParseError (token , tokstr , position , PARSE_UNBALANCED_CLOSING_PARENS )
234
267
if ast [1 ] is TOKEN_LPAR :
235
268
ast [0 ].append (ast [2 ])
269
+ if TRACE_PARSE : print ('ast9:' , repr (ast ))
236
270
ast = ast [0 ]
271
+ if TRACE_PARSE : print ('ast10:' , repr (ast ))
237
272
break
238
273
239
274
if isinstance (ast [1 ], int ):
@@ -246,10 +281,12 @@ def parse(self, expr, simplify=False):
246
281
247
282
subex = ast [1 ](* ast [2 :])
248
283
ast [0 ].append (subex )
284
+ if TRACE_PARSE : print ('ast11:' , repr (ast ))
249
285
ast = ast [0 ]
286
+ if TRACE_PARSE : print ('ast12:' , repr (ast ))
250
287
else :
251
288
raise ParseError (token , tokstr , position , PARSE_UNKNOWN_TOKEN )
252
-
289
+
253
290
prev = tok
254
291
255
292
try :
@@ -259,16 +296,21 @@ def parse(self, expr, simplify=False):
259
296
if len (ast ) != 3 :
260
297
raise ParseError (error_code = PARSE_INVALID_EXPRESSION )
261
298
parsed = ast [2 ]
299
+ if TRACE_PARSE : print ('parsed1:' , repr (parsed ))
262
300
else :
263
301
parsed = ast [1 ](* ast [2 :])
302
+ if TRACE_PARSE : print ('parsed2:' , repr (parsed ))
264
303
break
265
304
else :
266
305
subex = ast [1 ](* ast [2 :])
267
306
ast [0 ].append (subex )
307
+ if TRACE_PARSE : print ('ast13:' , repr (ast ))
268
308
ast = ast [0 ]
309
+ if TRACE_PARSE : print ('ast14:' , repr (ast ))
269
310
except TypeError :
270
311
raise ParseError (error_code = PARSE_INVALID_EXPRESSION )
271
312
313
+ if TRACE_PARSE : print ('parsed3:' , repr (parsed ))
272
314
if simplify :
273
315
return parsed .simplify ()
274
316
return parsed
@@ -277,31 +319,42 @@ def _start_operation(self, ast, operation, precedence):
277
319
"""
278
320
Returns an AST where all operations of lower precedence are finalized.
279
321
"""
322
+ if TRACE_PARSE : print (' start_operation: ast, operation, precedence' , repr (ast ), repr (operation ), repr (precedence ))
280
323
op_prec = precedence [operation ]
281
324
while True :
282
325
if ast [1 ] is None : # [None, None, x]
326
+ if TRACE_PARSE : print (' start_op: ast[1] is None:' , repr (ast ))
283
327
ast [1 ] = operation
328
+ if TRACE_PARSE : print (' --> start_op: ast[1] is None:' , repr (ast ))
284
329
return ast
285
330
286
331
prec = precedence [ast [1 ]]
287
332
if prec > op_prec : # op=&, [ast, |, x, y] -> [[ast, |, x], &, y]
333
+ if TRACE_PARSE : print (' start_op: prec > op_prec:' , repr (ast ))
288
334
ast = [ast , operation , ast .pop (- 1 )]
335
+ if TRACE_PARSE : print (' --> start_op: prec > op_prec:' , repr (ast ))
289
336
return ast
290
337
291
338
if prec == op_prec : # op=&, [ast, &, x] -> [ast, &, x]
339
+ if TRACE_PARSE : print (' start_op: prec == op_prec:' , repr (ast ))
292
340
return ast
293
341
294
342
if not (inspect .isclass (ast [1 ]) and issubclass (ast [1 ], Function )):
295
343
# the top ast node should be a function subclass at this stage
296
344
raise ParseError (error_code = PARSE_INVALID_NESTING )
297
345
298
346
if ast [0 ] is None : # op=|, [None, &, x, y] -> [None, |, x&y]
347
+ if TRACE_PARSE : print (' start_op: ast[0] is None:' , repr (ast ))
299
348
subexp = ast [1 ](* ast [2 :])
300
- return [ast [0 ], operation , subexp ]
349
+ new_ast = [ast [0 ], operation , subexp ]
350
+ if TRACE_PARSE : print (' --> start_op: ast[0] is None:' , repr (new_ast ))
351
+ return new_ast
301
352
302
353
else : # op=|, [[ast, &, x], ~, y] -> [ast, &, x, ~y]
354
+ if TRACE_PARSE : print (' start_op: else:' , repr (ast ))
303
355
ast [0 ].append (ast [1 ](* ast [2 :]))
304
356
ast = ast [0 ]
357
+ if TRACE_PARSE : print (' --> start_op: else:' , repr (ast ))
305
358
306
359
def tokenize (self , expr ):
307
360
"""
0 commit comments