oppc: rename Assign classes
[openpower-isa.git] / src / openpower / oppc / pc_parser.py
1 import itertools
2
3 from ply import yacc
4
5 import openpower.oppc.pc_ast as pc_ast
6
7
8 class SyntaxError2(Exception):
9 """
10 Class used to raise a syntax error but get ply to stop eating errors
11 since it catches and discards SyntaxError after setting a flag.
12 """
13
14 def __init__(self, *args, cls=SyntaxError):
15 super().__init__(*args)
16 self.cls = cls
17
18 def __repr__(self):
19 return repr(self.cls(*self.args))
20
21 def __str__(self):
22 return str(self.cls(*self.args))
23
24 def raise_syntax_error(self):
25 raise self.cls(*self.args) from self
26
27
28 def raise_syntax_error(msg,
29 filename, lineno, lexpos, data,
30 cls=SyntaxError):
31 line_start = data.rfind('\n', 0, lexpos) + 1
32 line_end = data.find('\n', line_start)
33 col = (lexpos - line_start) + 1
34
35 raise SyntaxError2(str(msg),
36 (filename, lineno, col, data[line_start:line_end]),
37 cls=cls)
38
39
40 binary_ops = {
41 "^": pc_ast.BitXor,
42 "&": pc_ast.BitAnd,
43 "|": pc_ast.BitOr,
44 "+": pc_ast.Add,
45 "-": pc_ast.Sub,
46 "<<": pc_ast.LShift,
47 ">>": pc_ast.RShift,
48 "*": pc_ast.Mul,
49 "/": pc_ast.Div,
50 "%": pc_ast.Mod,
51 "<=": pc_ast.Le,
52 ">=": pc_ast.Ge,
53 "<": pc_ast.Lt,
54 ">": pc_ast.Gt,
55 "=": pc_ast.Eq,
56 "!=": pc_ast.NotEq,
57 }
58 unary_ops = {
59 "+": pc_ast.Plus,
60 "-": pc_ast.Minus,
61 "¬": pc_ast.Not,
62 }
63
64
65 class Parser:
66 REGS = {}
67 REGS.update(map(lambda reg: (reg, pc_ast.GPR), pc_ast.GPR))
68 REGS.update(map(lambda reg: (reg, pc_ast.FPR), pc_ast.FPR))
69 REGS.update(map(lambda reg: (reg, pc_ast.CR3), pc_ast.CR3))
70 REGS.update(map(lambda reg: (reg, pc_ast.CR5), pc_ast.CR5))
71 REGS.update(map(lambda reg: (reg, pc_ast.XER), pc_ast.XER))
72
73 def __init__(self, lexer, debug=False, optimize=False, write_tables=True):
74 ignore = lambda token: token in ("WS", "THEN")
75 self.tokens = tuple(itertools.filterfalse(ignore, lexer.tokens))
76
77 self.__lexer = lexer
78 self.__parser = yacc.yacc(
79 module=self,
80 start="file_input_end",
81 debug=debug,
82 optimize=optimize,
83 write_tables=write_tables,
84 tabmodule="yacctab")
85
86 return super().__init__()
87
88 precedence = (
89 ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
90 ("left", "BITOR"),
91 ("left", "BITXOR"),
92 ("left", "BITAND"),
93 ("left", "LSHIFT", "RSHIFT"),
94 ("left", "PLUS", "MINUS"),
95 ("left", "MULT", "DIV", "MOD"),
96 ("left", "INVERT"),
97 )
98
99 def p_file_input_end(self, p):
100 """
101 file_input_end : file_input ENDMARKER
102 """
103 p[0] = p[1]
104
105 def p_file_input(self, p):
106 """
107 file_input : file_input NEWLINE
108 | file_input stmt
109 | NEWLINE
110 | stmt
111 """
112 if isinstance(p[len(p)-1], pc_ast.Linebreak):
113 if len(p) == 3:
114 p[0] = p[1]
115 else:
116 p[0] = pc_ast.Scope()
117 else:
118 if len(p) == 3:
119 stmt = p[2]
120 if not isinstance(stmt, pc_ast.Scope):
121 stmt = pc_ast.Scope([stmt])
122 p[0] = pc_ast.Scope(p[1] + stmt)
123 else:
124 p[0] = p[1]
125
126 # funcdef: [decorators] 'def' NAME parameters ':' suite
127 # ignoring decorators
128 def p_funcdef(self, p):
129 """
130 funcdef : DEF NAME parameters COLON suite
131 """
132 raise NotImplementedError()
133
134 # parameters: '(' [varargslist] ')'
135 def p_parameters(self, p):
136 """
137 parameters : LPAR RPAR
138 | LPAR varargslist RPAR
139 """
140 raise NotImplementedError()
141
142 # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
143 # '**' NAME) |
144 # highly simplified
145
146 def p_varargslist(self, p):
147 """
148 varargslist : varargslist COMMA NAME
149 | NAME
150 """
151 raise NotImplementedError()
152
153 # stmt: simple_stmt | compound_stmt
154 def p_stmt_simple(self, p):
155 """
156 stmt : simple_stmt
157 """
158 # simple_stmt is a list
159 p[0] = p[1]
160
161 def p_stmt_compound(self, p):
162 """
163 stmt : compound_stmt
164 """
165 p[0] = pc_ast.Scope([p[1]])
166
167 # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
168 def p_simple_stmt(self, p):
169 """
170 simple_stmt : small_stmts NEWLINE
171 | small_stmts SEMICOLON NEWLINE
172 """
173 p[0] = p[1]
174
175 def p_small_stmts(self, p):
176 """
177 small_stmts : small_stmts SEMICOLON small_stmt
178 | small_stmt
179 """
180 if len(p) == 4:
181 p[0] = pc_ast.Scope(p[1] + (p[3],))
182 else:
183 p[0] = pc_ast.Scope([p[1]])
184
185 # small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
186 # import_stmt | global_stmt | exec_stmt | assert_stmt
187 def p_small_stmt(self, p):
188 """
189 small_stmt : flow_stmt
190 | break_stmt
191 | expr_stmt
192 """
193 p[0] = p[1]
194
195 # expr_stmt: testlist (augassign (yield_expr|testlist) |
196 # ('=' (yield_expr|testlist))*)
197 # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
198 # '<<=' | '>>=' | '**=' | '//=')
199 def p_expr_stmt(self, p):
200 """
201 expr_stmt : testlist ASSIGNEA testlist
202 | testlist ASSIGN testlist
203 | testlist
204 """
205 if len(p) == 2:
206 p[0] = p[1]
207 else:
208 (lvalue, rvalue) = (p[1], p[3])
209 if isinstance(p[2], pc_ast.AssignOp):
210 cls = pc_ast.AssignExpr
211 else:
212 cls = pc_ast.AssignIEAExpr
213 if (isinstance(lvalue, pc_ast.Symbol) and
214 (str(lvalue) in self.__class__.REGS)):
215 lvalue = self.__class__.REGS[str(lvalue)](lvalue)
216 p[0] = cls(lvalue=lvalue, rvalue=rvalue)
217
218 def p_flow_stmt(self, p):
219 "flow_stmt : return_stmt"
220 p[0] = p[1]
221
222 # return_stmt: 'return' [testlist]
223 def p_return_stmt(self, p):
224 "return_stmt : RETURN testlist"
225 p[0] = pc_ast.Return(p[2])
226
227 def p_compound_stmt(self, p):
228 """
229 compound_stmt : if_stmt
230 | while_stmt
231 | switch_stmt
232 | for_stmt
233 | funcdef
234 """
235 p[0] = p[1]
236
237 def p_break_stmt(self, p):
238 """
239 break_stmt : BREAK
240 """
241 p[0] = p[1]
242
243 def p_for_stmt(self, p):
244 """
245 for_stmt : FOR atom EQ comparison TO comparison COLON suite
246 | DO atom EQ comparison TO comparison COLON suite
247 """
248 p[0] = pc_ast.ForExpr(subject=p[2], start=p[4], end=p[6], body=p[8])
249
250 def p_while_stmt(self, p):
251 """
252 while_stmt : DO WHILE test COLON suite ELSE COLON suite
253 | DO WHILE test COLON suite
254 """
255 if len(p) == 9:
256 p[0] = pc_ast.WhileExpr(test=p[3], body=p[5], orelse=p[8])
257 else:
258 p[0] = pc_ast.WhileExpr(test=p[3], body=p[5], orelse=pc_ast.Scope())
259
260 def p_switch_smt(self, p):
261 """
262 switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT cases DEDENT
263 """
264 p[0] = pc_ast.SwitchExpr(subject=p[3], cases=p[8])
265
266 def p_cases(self, p):
267 """
268 cases : switch_list switch_default
269 | switch_default
270 """
271 if len(p) == 3:
272 p[0] = pc_ast.Cases(p[1] + (p[2],))
273 else:
274 p[0] = pc_ast.Cases([p[1]])
275
276 def p_switch_list(self, p):
277 """
278 switch_list : switch_case switch_list
279 | switch_case
280 """
281 if len(p) == 3:
282 p[0] = pc_ast.Sequence((p[1],) + p[2])
283 else:
284 p[0] = pc_ast.Sequence([p[1]])
285
286 def p_switch_case(self, p):
287 """
288 switch_case : CASE LPAR labels RPAR COLON suite
289 """
290 p[0] = pc_ast.Case(labels=p[3], body=p[6])
291
292 def p_switch_default(self, p):
293 """
294 switch_default : DEFAULT COLON suite
295 """
296 p[0] = pc_ast.Case(body=p[3],
297 labels=pc_ast.Labels([pc_ast.DefaultLabel()]))
298
299 def p_labels(self, p):
300 """
301 labels : atom COMMA labels
302 | atom
303 """
304 if not isinstance(p[1], pc_ast.IntLiteral):
305 raise_syntax_error(str(p),
306 self.filename, p.lineno, p.lexpos,
307 self.input_text)
308 label = pc_ast.Label(str(p[1]))
309 if len(p) == 4:
310 p[0] = pc_ast.Labels((label,) + p[3])
311 else:
312 p[0] = pc_ast.Labels([label])
313
314 def p_if_stmt(self, p):
315 """
316 if_stmt : IF test COLON suite ELSE COLON if_stmt
317 | IF test COLON suite ELSE COLON suite
318 | IF test COLON suite
319 """
320 (test, body) = (p[2], p[4])
321 if len(p) == 8:
322 orelse = p[7]
323 else:
324 orelse = pc_ast.Scope()
325 if not isinstance(body, pc_ast.Scope):
326 body = pc_ast.Scope([body])
327 if not isinstance(orelse, pc_ast.Scope):
328 orelse = pc_ast.Scope([orelse])
329 p[0] = pc_ast.IfExpr(test=test,
330 body=body, orelse=orelse)
331
332 def p_suite(self, p):
333 """
334 suite : simple_stmt
335 | NEWLINE INDENT stmts DEDENT
336 """
337 if len(p) == 2:
338 p[0] = p[1]
339 else:
340 p[0] = p[3]
341
342 def p_stmts(self, p):
343 """
344 stmts : stmts stmt
345 | stmt
346 """
347 if len(p) == 3:
348 p[0] = pc_ast.Scope(p[1] + p[2])
349 else:
350 p[0] = p[1]
351
352 def p_comparison(self, p):
353 """
354 comparison : comparison PLUS comparison
355 | comparison MINUS comparison
356 | comparison MULT comparison
357 | comparison LSHIFT comparison
358 | comparison RSHIFT comparison
359 | comparison DIV comparison
360 | comparison MOD comparison
361 | comparison EQ comparison
362 | comparison NE comparison
363 | comparison LE comparison
364 | comparison GE comparison
365 | comparison LTU comparison
366 | comparison GTU comparison
367 | comparison LT comparison
368 | comparison GT comparison
369 | comparison BITOR comparison
370 | comparison BITXOR comparison
371 | comparison BITAND comparison
372 | PLUS comparison
373 | MINUS comparison
374 | INVERT comparison
375 | comparison APPEND comparison
376 | power
377 """
378 if len(p) == 4:
379 def reg0(left, op, right):
380 if (isinstance(left, pc_ast.Symbol) and
381 isinstance(op, pc_ast.BitOr) and
382 (isinstance(right, pc_ast.DecLiteral) and (str(right) == "0")) and
383 (str(left) in frozenset(pc_ast.GPRZero))):
384 return pc_ast.GPRZero(str(left))
385 return None
386
387 def repeat(left, op, right):
388 if (isinstance(left, pc_ast.Sequence) and
389 (len(left) == 1) and
390 isinstance(op, pc_ast.Mul)):
391 return pc_ast.RepeatExpr(subject=left[0], times=right)
392 return None
393
394 (left, op, right) = p[1:]
395 for hook in (reg0, repeat):
396 p[0] = hook(left, op, right)
397 if p[0] is not None:
398 break
399 else:
400 p[0] = pc_ast.BinaryExpr(left=left, op=op, right=right)
401
402 elif len(p) == 3:
403 (op, value) = p[1:]
404 p[0] = pc_ast.UnaryExpr(op=op, value=value)
405 else:
406 p[0] = p[1]
407
408 # power: atom trailer* ['**' factor]
409 # trailers enables function calls (and subscripts).
410 # so this is 'trailerlist'
411 def p_power(self, p):
412 """
413 power : atom
414 | atom trailerlist
415 """
416 if len(p) == 2:
417 p[0] = p[1]
418 else:
419 attribute_or_subscript = (
420 pc_ast.Attribute,
421 pc_ast.Subscript,
422 pc_ast.RangeSubscript,
423 )
424 if isinstance(p[2], attribute_or_subscript):
425 node = p[2]
426 while isinstance(node.subject, attribute_or_subscript):
427 node = node.subject
428 if isinstance(node.subject, pc_ast.Arguments):
429 node.subject = pc_ast.Call(name=p[1], args=node.subject)
430 else:
431 node.subject = p[1]
432 p[0] = p[2]
433 elif isinstance(p[2], pc_ast.Arguments):
434 p[0] = pc_ast.Call(name=p[1], args=p[2])
435 else:
436 raise NotImplementedError()
437
438 def p_atom_name(self, p):
439 """
440 atom : NAME
441 """
442 p[0] = p[1]
443
444 def p_atom_number(self, p):
445 """
446 atom : BINARY
447 | NUMBER
448 | HEX
449 | STRING
450 """
451 p[0] = p[1]
452
453 # '[' [listmaker] ']' |
454 def p_atom_listmaker(self, p):
455 """
456 atom : LBRACK listmaker RBRACK
457 """
458 p[0] = p[2]
459
460 def p_listmaker(self, p):
461 """
462 listmaker : test COMMA listmaker
463 | test
464 """
465 if len(p) == 2:
466 p[0] = pc_ast.Sequence([p[1]])
467 else:
468 p[0] = pc_ast.Sequence((p[0],) + p[1])
469
470 def p_atom_tuple(self, p):
471 """
472 atom : LPAR testlist RPAR
473 """
474 value = p[2]
475 if (isinstance(value, pc_ast.Symbol) and
476 (str(value) in self.__class__.REGS)):
477 value = self.__class__.REGS[str(value)](value)
478 p[0] = value
479
480 def p_trailerlist(self, p):
481 """
482 trailerlist : trailer trailerlist
483 | trailer
484 """
485 if len(p) == 2:
486 p[0] = p[1]
487 else:
488 attribute_or_subscript = (
489 pc_ast.Attribute,
490 pc_ast.Subscript,
491 pc_ast.RangeSubscript,
492 )
493 if isinstance(p[2], attribute_or_subscript):
494 node = p[2]
495 while isinstance(node.subject, attribute_or_subscript):
496 node = node.subject
497 node.subject = p[1]
498 p[0] = p[2]
499 else:
500 p[0] = pc_ast.Sequence(p[1] + (p[2],))
501
502 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
503 def p_trailer(self, p):
504 """
505 trailer : trailer_arglist
506 | trailer_subscript
507 | trailer_attr
508 """
509 p[0] = p[1]
510
511 def p_trailer_arglist(self, p):
512 """
513 trailer_arglist : LPAR arglist RPAR
514 | LPAR RPAR
515 """
516 if len(p) == 3:
517 p[0] = pc_ast.Arguments()
518 else:
519 p[0] = p[2]
520
521 def p_trailer_subscript(self, p):
522 """
523 trailer_subscript : LBRACK subscript RBRACK
524 """
525 p[0] = p[2]
526
527 def p_trailer_attr(self, p):
528 """
529 trailer_attr : PERIOD NAME
530 """
531 p[0] = pc_ast.Attribute(name=p[2])
532
533 # subscript: '.' '.' '.' | test | [test] ':' [test]
534 def p_subscript(self, p):
535 """subscript : test COLON test
536 | test
537 """
538 if len(p) == 4:
539 p[0] = pc_ast.RangeSubscript(start=p[1], end=p[3])
540 else:
541 p[0] = pc_ast.Subscript(index=p[1])
542
543 # testlist: test (',' test)* [',']
544 # Contains shift/reduce error
545 def p_testlist(self, p):
546 """
547 testlist : testlist_multi COMMA
548 | testlist_multi
549 """
550 if len(p) == 2:
551 p[0] = p[1]
552 else:
553 if isinstance(p[1], pc_ast.Sequence):
554 p[0] = p[1]
555 else:
556 p[0] = pc_ast.Sequence([p[1]])
557
558 def p_testlist_multi(self, p):
559 """
560 testlist_multi : testlist_multi COMMA test
561 | test
562 """
563 if len(p) == 2:
564 p[0] = p[1]
565 else:
566 if isinstance(p[1], pc_ast.Sequence):
567 p[0] = pc_ast.Sequence(p[1] + (p[3],))
568 else:
569 p[0] = pc_ast.Sequence([p[1], p[3]])
570
571 # test: or_test ['if' or_test 'else' test] | lambdef
572 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
573 def p_test(self, p):
574 """
575 test : comparison
576 | comparison QMARK test COLON test
577 """
578 if len(p) == 2:
579 p[0] = p[1]
580 else:
581 p[0] = pc_ast.IfExpr(test=p[1],
582 body=pc_ast.Scope([p[3]]),
583 orelse=pc_ast.Scope([p[5]]))
584
585 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
586 # | '**' test)
587 # XXX INCOMPLETE: this doesn't allow the trailing comma
588 def p_arglist(self, p):
589 """
590 arglist : arglist COMMA argument
591 | argument
592 """
593 if len(p) == 4:
594 p[0] = pc_ast.Arguments(p[1] + (p[3],))
595 else:
596 p[0] = pc_ast.Arguments([p[1]])
597
598 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
599 def p_argument(self, p):
600 """
601 argument : test
602 """
603 p[0] = p[1]
604
605 def p_error(self, p):
606 raise_syntax_error(str(p.value),
607 self.filename, p.lineno, p.lexpos,
608 self.input_text)
609
610 def parse(self, code, filename=None, debug=False):
611 self.filename = filename
612 self.input_text = code
613 return self.__parser.parse(lexer=self.__lexer, debug=debug, input=code)