bit of a mess, turn lexer and parser into classes
[soc.git] / src / soc / decoder / power_pseudo.py
1 # Based on GardenSnake - a parser generator demonstration program
2 # GardenSnake was released into the Public Domain by Andrew Dalke.
3
4 # Portions of this work are derived from Python's Grammar definition
5 # and may be covered under the Python copyright and license
6 #
7 # Andrew Dalke / Dalke Scientific Software, LLC
8 # 30 August 2006 / Cape Town, South Africa
9
10 # Modifications for inclusion in PLY distribution
11 import sys
12 from pprint import pprint
13 from copy import copy
14 from ply import lex, yacc
15 import astor
16
17 # I use the Python AST
18 #from compiler import ast
19 import ast
20
21 # Helper function
22 def Assign(left, right):
23 names = []
24 if isinstance(left, ast.Name):
25 # Single assignment on left
26 return ast.Assign([ast.Name(left.id, ast.Store())], right)
27 elif isinstance(left, ast.Tuple):
28 # List of things - make sure they are Name nodes
29 names = []
30 for child in left.getChildren():
31 if not isinstance(child, ast.Name):
32 raise SyntaxError("that assignment not supported")
33 names.append(child.name)
34 ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
35 return ast.Assign([ast.AssTuple(ass_list)], right)
36 else:
37 raise SyntaxError("Can't do that yet")
38
39
40 ## I implemented INDENT / DEDENT generation as a post-processing filter
41
42 # The original lex token stream contains WS and NEWLINE characters.
43 # WS will only occur before any other tokens on a line.
44
45 # I have three filters. One tags tokens by adding two attributes.
46 # "must_indent" is True if the token must be indented from the
47 # previous code. The other is "at_line_start" which is True for WS
48 # and the first non-WS/non-NEWLINE on a line. It flags the check so
49 # see if the new line has changed indication level.
50
51 # Python's syntax has three INDENT states
52 # 0) no colon hence no need to indent
53 # 1) "if 1: go()" - simple statements have a COLON but no need for an indent
54 # 2) "if 1:\n go()" - complex statements have a COLON NEWLINE and must indent
55 NO_INDENT = 0
56 MAY_INDENT = 1
57 MUST_INDENT = 2
58
59 # turn into python-like colon syntax from pseudo-code syntax
60 def python_colonify(lexer, tokens):
61
62 forwhile_seen = False
63 for token in tokens:
64 #print ("track colon token", token, token.type)
65
66 if token.type == 'THEN':
67 # turn then into colon
68 token.type = "COLON"
69 yield token
70 elif token.type == 'ELSE':
71 yield token
72 token = copy(token)
73 token.type = "COLON"
74 yield token
75 elif token.type in ['WHILE', 'FOR']:
76 forwhile_seen = True
77 yield token
78 elif token.type == 'NEWLINE':
79 if forwhile_seen:
80 ctok = copy(token)
81 ctok.type = "COLON"
82 yield ctok
83 forwhile_seen = False
84 yield token
85 else:
86 yield token
87
88
89 # only care about whitespace at the start of a line
90 def track_tokens_filter(lexer, tokens):
91 oldignore = lexer.lexignore
92 lexer.at_line_start = at_line_start = True
93 indent = NO_INDENT
94 saw_colon = False
95 for token in tokens:
96 #print ("track token", token, token.type)
97 token.at_line_start = at_line_start
98
99 if token.type == "COLON":
100 at_line_start = False
101 indent = MAY_INDENT
102 token.must_indent = False
103
104 elif token.type == "NEWLINE":
105 at_line_start = True
106 if indent == MAY_INDENT:
107 indent = MUST_INDENT
108 token.must_indent = False
109
110 elif token.type == "WS":
111 assert token.at_line_start == True
112 at_line_start = True
113 token.must_indent = False
114
115 else:
116 # A real token; only indent after COLON NEWLINE
117 if indent == MUST_INDENT:
118 token.must_indent = True
119 else:
120 token.must_indent = False
121 at_line_start = False
122 indent = NO_INDENT
123
124 # really bad hack that changes ignore lexer state.
125 # when "must indent" is seen (basically "real tokens" seen)
126 # then ignore whitespace.
127 if token.must_indent:
128 lexer.lexignore = ('ignore', ' ')
129 else:
130 lexer.lexignore = oldignore
131
132 token.indent = indent
133 yield token
134 lexer.at_line_start = at_line_start
135
136 def _new_token(type, lineno):
137 tok = lex.LexToken()
138 tok.type = type
139 tok.value = None
140 tok.lineno = lineno
141 tok.lexpos = -1
142 return tok
143
144 # Synthesize a DEDENT tag
145 def DEDENT(lineno):
146 return _new_token("DEDENT", lineno)
147
148 # Synthesize an INDENT tag
149 def INDENT(lineno):
150 return _new_token("INDENT", lineno)
151
152
153 # Track the indentation level and emit the right INDENT / DEDENT events.
154 def indentation_filter(tokens):
155 # A stack of indentation levels; will never pop item 0
156 levels = [0]
157 token = None
158 depth = 0
159 prev_was_ws = False
160 for token in tokens:
161 if 1:
162 print ("Process", depth, token.indent, token,)
163 if token.at_line_start:
164 print ("at_line_start",)
165 if token.must_indent:
166 print ("must_indent",)
167 print
168
169 # WS only occurs at the start of the line
170 # There may be WS followed by NEWLINE so
171 # only track the depth here. Don't indent/dedent
172 # until there's something real.
173 if token.type == "WS":
174 assert depth == 0
175 depth = len(token.value)
176 prev_was_ws = True
177 # WS tokens are never passed to the parser
178 continue
179
180 if token.type == "NEWLINE":
181 depth = 0
182 if prev_was_ws or token.at_line_start:
183 # ignore blank lines
184 continue
185 # pass the other cases on through
186 yield token
187 continue
188
189 # then it must be a real token (not WS, not NEWLINE)
190 # which can affect the indentation level
191
192 prev_was_ws = False
193 if token.must_indent:
194 # The current depth must be larger than the previous level
195 if not (depth > levels[-1]):
196 raise IndentationError("expected an indented block")
197
198 levels.append(depth)
199 yield INDENT(token.lineno)
200
201 elif token.at_line_start:
202 # Must be on the same level or one of the previous levels
203 if depth == levels[-1]:
204 # At the same level
205 pass
206 elif depth > levels[-1]:
207 raise IndentationError("indentation increase but not in new block")
208 else:
209 # Back up; but only if it matches a previous level
210 try:
211 i = levels.index(depth)
212 except ValueError:
213 raise IndentationError("inconsistent indentation")
214 for _ in range(i+1, len(levels)):
215 yield DEDENT(token.lineno)
216 levels.pop()
217
218 yield token
219
220 ### Finished processing ###
221
222 # Must dedent any remaining levels
223 if len(levels) > 1:
224 assert token is not None
225 for _ in range(1, len(levels)):
226 yield DEDENT(token.lineno)
227
228
229 # The top-level filter adds an ENDMARKER, if requested.
230 # Python's grammar uses it.
231 def filter(lexer, add_endmarker = True):
232 token = None
233 tokens = iter(lexer.token, None)
234 tokens = python_colonify(lexer, tokens)
235 tokens = track_tokens_filter(lexer, tokens)
236 for token in indentation_filter(tokens):
237 yield token
238
239 if add_endmarker:
240 lineno = 1
241 if token is not None:
242 lineno = token.lineno
243 yield _new_token("ENDMARKER", lineno)
244
245 ## No using Python's approach because Ply supports precedence
246
247 # comparison: expr (comp_op expr)*
248 # arith_expr: term (('+'|'-') term)*
249 # term: factor (('*'|'/'|'%'|'//') factor)*
250 # factor: ('+'|'-'|'~') factor | power
251 # comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
252
253 def make_lt_compare(arg):
254 (left, right) = arg
255 return ast.Compare(left, [ast.Lt()], [right])
256 def make_gt_compare(arg):
257 (left, right) = arg
258 return ast.Compare(left, [ast.Gt()], [right])
259 def make_eq_compare(arg):
260 (left, right) = arg
261 return ast.Compare(left, [ast.Eq()], [right])
262
263
264 binary_ops = {
265 "+": ast.Add(),
266 "-": ast.Sub(),
267 "*": ast.Mult(),
268 "/": ast.Div(),
269 "<": make_lt_compare,
270 ">": make_gt_compare,
271 "=": make_eq_compare,
272 }
273 unary_ops = {
274 "+": ast.Add,
275 "-": ast.Sub,
276 }
277 precedence = (
278 ("left", "EQ", "GT", "LT"),
279 ("left", "PLUS", "MINUS"),
280 ("left", "MULT", "DIV"),
281 )
282
283 def check_concat(node): # checks if the comparison is already a concat
284 print (node)
285 if not isinstance(node, ast.Call):
286 return [node]
287 if node[0].id != 'concat':
288 return node
289 return node[1]
290 ##### Lexer ######
291 #import lex
292 import decimal
293
294 class PowerLexer:
295 tokens = (
296 'DEF',
297 'IF',
298 'THEN',
299 'ELSE',
300 'FOR',
301 'TO',
302 'DO',
303 'WHILE',
304 'BREAK',
305 'NAME',
306 'NUMBER', # Python decimals
307 'BINARY', # Python binary
308 'STRING', # single quoted strings only; syntax of raw strings
309 'LPAR',
310 'RPAR',
311 'LBRACK',
312 'RBRACK',
313 'COLON',
314 'EQ',
315 'ASSIGN',
316 'LT',
317 'GT',
318 'PLUS',
319 'MINUS',
320 'MULT',
321 'DIV',
322 'APPEND',
323 'RETURN',
324 'WS',
325 'NEWLINE',
326 'COMMA',
327 'SEMICOLON',
328 'INDENT',
329 'DEDENT',
330 'ENDMARKER',
331 )
332
333 # Build the lexer
334 def build(self,**kwargs):
335 self.lexer = lex.lex(module=self, **kwargs)
336
337 def t_BINARY(self, t):
338 r"""0b[01]+"""
339 t.value = int(t.value, 2)
340 return t
341
342 #t_NUMBER = r'\d+'
343 # taken from decmial.py but without the leading sign
344 def t_NUMBER(self, t):
345 r"""(\d+(\.\d*)?|\.\d+)([eE][-+]? \d+)?"""
346 t.value = int(t.value)
347 return t
348
349 def t_STRING(self, t):
350 r"'([^\\']+|\\'|\\\\)*'" # I think this is right ...
351 t.value=t.value[1:-1].decode("string-escape") # .swapcase() # for fun
352 return t
353
354 t_COLON = r':'
355 t_EQ = r'='
356 t_ASSIGN = r'<-'
357 t_LT = r'<'
358 t_GT = r'>'
359 t_PLUS = r'\+'
360 t_MINUS = r'-'
361 t_MULT = r'\*'
362 t_DIV = r'/'
363 t_COMMA = r','
364 t_SEMICOLON = r';'
365 t_APPEND = r'\|\|'
366
367 # Ply nicely documented how to do this.
368
369 RESERVED = {
370 "def": "DEF",
371 "if": "IF",
372 "then": "THEN",
373 "else": "ELSE",
374 "leave": "BREAK",
375 "for": "FOR",
376 "to": "TO",
377 "while": "WHILE",
378 "do": "DO",
379 "return": "RETURN",
380 }
381
382 def t_NAME(self, t):
383 r'[a-zA-Z_][a-zA-Z0-9_]*'
384 t.type = self.RESERVED.get(t.value, "NAME")
385 return t
386
387 # Putting this before t_WS let it consume lines with only comments in
388 # them so the latter code never sees the WS part. Not consuming the
389 # newline. Needed for "if 1: #comment"
390 def t_comment(self, t):
391 r"[ ]*\043[^\n]*" # \043 is '#'
392 pass
393
394
395 # Whitespace
396 def t_WS(self, t):
397 r'[ ]+'
398 if t.lexer.at_line_start and t.lexer.paren_count == 0 and \
399 t.lexer.brack_count == 0:
400 return t
401
402 # Don't generate newline tokens when inside of parenthesis, eg
403 # a = (1,
404 # 2, 3)
405 def t_newline(self, t):
406 r'\n+'
407 t.lexer.lineno += len(t.value)
408 t.type = "NEWLINE"
409 if t.lexer.paren_count == 0 and t.lexer.brack_count == 0:
410 return t
411
412 def t_LBRACK(self, t):
413 r'\['
414 t.lexer.brack_count += 1
415 return t
416
417 def t_RBRACK(self, t):
418 r'\]'
419 # check for underflow? should be the job of the parser
420 t.lexer.brack_count -= 1
421 return t
422
423 def t_LPAR(self, t):
424 r'\('
425 t.lexer.paren_count += 1
426 return t
427
428 def t_RPAR(self, t):
429 r'\)'
430 # check for underflow? should be the job of the parser
431 t.lexer.paren_count -= 1
432 return t
433
434 #t_ignore = " "
435
436 def t_error(self, t):
437 raise SyntaxError("Unknown symbol %r" % (t.value[0],))
438 print ("Skipping", repr(t.value[0]))
439 t.lexer.skip(1)
440
441 # Combine Ply and my filters into a new lexer
442
443 class IndentLexer(PowerLexer):
444 def __init__(self, debug=0, optimize=0, lextab='lextab', reflags=0):
445 self.build(debug=debug, optimize=optimize,
446 lextab=lextab, reflags=reflags)
447 self.token_stream = None
448 def input(self, s, add_endmarker=True):
449 self.lexer.paren_count = 0
450 self.lexer.brack_count = 0
451 self.lexer.input(s)
452 self.token_stream = filter(self.lexer, add_endmarker)
453
454 def token(self):
455 try:
456 return next(self.token_stream)
457 except StopIteration:
458 return None
459
460
461 ########## Parser (tokens -> AST) ######
462
463 # also part of Ply
464 #import yacc
465
466 class PowerParser:
467
468 # The grammar comments come from Python's Grammar/Grammar file
469
470 ## NB: compound_stmt in single_input is followed by extra NEWLINE!
471 # file_input: (NEWLINE | stmt)* ENDMARKER
472
473 def p_file_input_end(self, p):
474 """file_input_end : file_input ENDMARKER"""
475 print ("end", p[1])
476 p[0] = p[1]
477
478 def p_file_input(self, p):
479 """file_input : file_input NEWLINE
480 | file_input stmt
481 | NEWLINE
482 | stmt"""
483 if isinstance(p[len(p)-1], str):
484 if len(p) == 3:
485 p[0] = p[1]
486 else:
487 p[0] = [] # p == 2 --> only a blank line
488 else:
489 if len(p) == 3:
490 p[0] = p[1] + p[2]
491 else:
492 p[0] = p[1]
493
494
495 # funcdef: [decorators] 'def' NAME parameters ':' suite
496 # ignoring decorators
497 def p_funcdef(self, p):
498 "funcdef : DEF NAME parameters COLON suite"
499 p[0] = ast.Function(None, p[2], list(p[3]), (), 0, None, p[5])
500
501 # parameters: '(' [varargslist] ')'
502 def p_parameters(self, p):
503 """parameters : LPAR RPAR
504 | LPAR varargslist RPAR"""
505 if len(p) == 3:
506 p[0] = []
507 else:
508 p[0] = p[2]
509
510
511 # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) |
512 # highly simplified
513 def p_varargslist(self, p):
514 """varargslist : varargslist COMMA NAME
515 | NAME"""
516 if len(p) == 4:
517 p[0] = p[1] + p[3]
518 else:
519 p[0] = [p[1]]
520
521 # stmt: simple_stmt | compound_stmt
522 def p_stmt_simple(self, p):
523 """stmt : simple_stmt"""
524 # simple_stmt is a list
525 p[0] = p[1]
526
527 def p_stmt_compound(self, p):
528 """stmt : compound_stmt"""
529 p[0] = [p[1]]
530
531 # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
532 def p_simple_stmt(self, p):
533 """simple_stmt : small_stmts NEWLINE
534 | small_stmts SEMICOLON NEWLINE"""
535 p[0] = p[1]
536
537 def p_small_stmts(self, p):
538 """small_stmts : small_stmts SEMICOLON small_stmt
539 | small_stmt"""
540 if len(p) == 4:
541 p[0] = p[1] + [p[3]]
542 else:
543 p[0] = [p[1]]
544
545 # small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
546 # import_stmt | global_stmt | exec_stmt | assert_stmt
547 def p_small_stmt(self, p):
548 """small_stmt : flow_stmt
549 | break_stmt
550 | expr_stmt"""
551 p[0] = p[1]
552
553 # expr_stmt: testlist (augassign (yield_expr|testlist) |
554 # ('=' (yield_expr|testlist))*)
555 # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
556 # '<<=' | '>>=' | '**=' | '//=')
557 def p_expr_stmt(self, p):
558 """expr_stmt : testlist ASSIGN testlist
559 | testlist """
560 if len(p) == 2:
561 # a list of expressions
562 #p[0] = ast.Discard(p[1])
563 p[0] = p[1]
564 else:
565 p[0] = Assign(p[1], p[3])
566
567 def p_flow_stmt(self, p):
568 "flow_stmt : return_stmt"
569 p[0] = p[1]
570
571 # return_stmt: 'return' [testlist]
572 def p_return_stmt(self, p):
573 "return_stmt : RETURN testlist"
574 p[0] = ast.Return(p[2])
575
576
577 def p_compound_stmt(self, p):
578 """compound_stmt : if_stmt
579 | while_stmt
580 | for_stmt
581 | funcdef
582 """
583 p[0] = p[1]
584
585 def p_break_stmt(self, p):
586 """break_stmt : BREAK
587 """
588 p[0] = ast.Break()
589
590 def p_for_stmt(self, p):
591 """for_stmt : FOR test EQ test TO test COLON suite
592 """
593 p[0] = ast.While(p[2], p[4], [])
594 # auto-add-one (sigh) due to python range
595 start = p[4]
596 end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
597 it = ast.Call(ast.Name("range"), [start, end], [])
598 p[0] = ast.For(p[2], it, p[8], [])
599
600 def p_while_stmt(self, p):
601 """while_stmt : DO WHILE test COLON suite ELSE COLON suite
602 | DO WHILE test COLON suite
603 """
604 if len(p) == 6:
605 p[0] = ast.While(p[3], p[5], [])
606 else:
607 p[0] = ast.While(p[3], p[5], p[8])
608
609 def p_if_stmt(self, p):
610 """if_stmt : IF test COLON suite ELSE COLON suite
611 | IF test COLON suite
612 """
613 if len(p) == 5:
614 p[0] = ast.If(p[2], p[4], [])
615 else:
616 p[0] = ast.If(p[2], p[4], p[7])
617
618 def p_suite(self, p):
619 """suite : simple_stmt
620 | NEWLINE INDENT stmts DEDENT"""
621 if len(p) == 2:
622 p[0] = p[1]
623 else:
624 p[0] = p[3]
625
626
627 def p_stmts(self, p):
628 """stmts : stmts stmt
629 | stmt"""
630 if len(p) == 3:
631 p[0] = p[1] + p[2]
632 else:
633 p[0] = p[1]
634
635 def p_comparison(self, p):
636 """comparison : comparison PLUS comparison
637 | comparison MINUS comparison
638 | comparison MULT comparison
639 | comparison DIV comparison
640 | comparison LT comparison
641 | comparison EQ comparison
642 | comparison GT comparison
643 | PLUS comparison
644 | MINUS comparison
645 | comparison APPEND comparison
646 | power"""
647 if len(p) == 4:
648 print (list(p))
649 if p[2] == '||':
650 l = check_concat(p[1]) + check_concat(p[3])
651 p[0] = ast.Call(ast.Name("concat"), l, [])
652 elif p[2] in ['<', '>', '=']:
653 p[0] = binary_ops[p[2]]((p[1],p[3]))
654 else:
655 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
656 elif len(p) == 3:
657 p[0] = unary_ops[p[1]](p[2])
658 else:
659 p[0] = p[1]
660
661 # power: atom trailer* ['**' factor]
662 # trailers enables function calls (and subscripts).
663 # I only allow one level of calls
664 # so this is 'trailer'
665 def p_power(self, p):
666 """power : atom
667 | atom trailer"""
668 if len(p) == 2:
669 p[0] = p[1]
670 else:
671 if p[2][0] == "CALL":
672 #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
673 p[0] = ast.Call(p[1], p[2][1], [])
674 #if p[1].id == 'print':
675 # p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
676 #else:
677 # p[0] = ast.CallFunc(p[1], p[2][1], None, None)
678 else:
679 print (p[2][1])
680 #raise AssertionError("not implemented %s" % p[2][0])
681 subs = p[2][1]
682 if len(subs) == 1:
683 idx = subs[0]
684 else:
685 idx = ast.Slice(subs[0], subs[1], None)
686 p[0] = ast.Subscript(p[1], idx)
687
688 def p_atom_name(self, p):
689 """atom : NAME"""
690 p[0] = ast.Name(p[1], ctx=ast.Load())
691
692 def p_atom_number(self, p):
693 """atom : BINARY
694 | NUMBER
695 | STRING"""
696 p[0] = ast.Constant(p[1])
697
698 #'[' [listmaker] ']' |
699
700 def p_atom_listmaker(self, p):
701 """atom : LBRACK listmaker RBRACK"""
702 p[0] = p[2]
703
704 def p_listmaker(self, p):
705 """listmaker : test COMMA listmaker
706 | test
707 """
708 if len(p) == 2:
709 p[0] = ast.List([p[1]])
710 else:
711 p[0] = ast.List([p[1]] + p[3].nodes)
712
713 def p_atom_tuple(self, p):
714 """atom : LPAR testlist RPAR"""
715 p[0] = p[2]
716
717 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
718 def p_trailer(self, p):
719 """trailer : trailer_arglist
720 | trailer_subscript
721 """
722 p[0] = p[1]
723
724 def p_trailer_arglist(self, p):
725 "trailer_arglist : LPAR arglist RPAR"
726 p[0] = ("CALL", p[2])
727
728 def p_trailer_subscript(self, p):
729 "trailer_subscript : LBRACK subscript RBRACK"
730 p[0] = ("SUBS", p[2])
731
732 #subscript: '.' '.' '.' | test | [test] ':' [test]
733
734 def p_subscript(self, p):
735 """subscript : test COLON test
736 | test
737 """
738 if len(p) == 4:
739 p[0] = [p[1], p[3]]
740 else:
741 p[0] = [p[1]]
742
743
744 # testlist: test (',' test)* [',']
745 # Contains shift/reduce error
746 def p_testlist(self, p):
747 """testlist : testlist_multi COMMA
748 | testlist_multi """
749 if len(p) == 2:
750 p[0] = p[1]
751 else:
752 # May need to promote singleton to tuple
753 if isinstance(p[1], list):
754 p[0] = p[1]
755 else:
756 p[0] = [p[1]]
757 # Convert into a tuple?
758 if isinstance(p[0], list):
759 p[0] = ast.Tuple(p[0])
760
761 def p_testlist_multi(self, p):
762 """testlist_multi : testlist_multi COMMA test
763 | test"""
764 if len(p) == 2:
765 # singleton
766 p[0] = p[1]
767 else:
768 if isinstance(p[1], list):
769 p[0] = p[1] + [p[3]]
770 else:
771 # singleton -> tuple
772 p[0] = [p[1], p[3]]
773
774
775 # test: or_test ['if' or_test 'else' test] | lambdef
776 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
777 def p_test(self, p):
778 "test : comparison"
779 p[0] = p[1]
780
781
782
783 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
784 # XXX INCOMPLETE: this doesn't allow the trailing comma
785 def p_arglist(self, p):
786 """arglist : arglist COMMA argument
787 | argument"""
788 if len(p) == 4:
789 p[0] = p[1] + [p[3]]
790 else:
791 p[0] = [p[1]]
792
793 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
794 def p_argument(self, p):
795 "argument : test"
796 p[0] = p[1]
797
798 def p_error(self, p):
799 #print "Error!", repr(p)
800 raise SyntaxError(p)
801
802
803 class GardenSnakeParser(PowerParser):
804 def __init__(self, lexer = None):
805 if lexer is None:
806 lexer = IndentLexer(debug=1)
807 self.lexer = lexer
808 self.tokens = lexer.tokens
809 self.parser = yacc.yacc(module=self, start="file_input_end",
810 debug=False, write_tables=False)
811
812 def parse(self, code):
813 self.lexer.input(code)
814 result = self.parser.parse(lexer = self.lexer, debug=False)
815 return ast.Module(result)
816
817
818 ###### Code generation ######
819
820 #from compiler import misc, syntax, pycodegen
821
822 class GardenSnakeCompiler(object):
823 def __init__(self):
824 self.parser = GardenSnakeParser()
825 def compile(self, code, mode="exec", filename="<string>"):
826 tree = self.parser.parse(code)
827 print ("snake")
828 pprint(tree)
829 return tree
830 #misc.set_filename(filename, tree)
831 return compile(tree, mode="exec", filename="<string>")
832 #syntax.check(tree)
833 gen = pycodegen.ModuleCodeGenerator(tree)
834 code = gen.getCode()
835 return code
836
837 ####### Test code #######
838
839 from soc.decoder.power_fieldsn import create_sigdecode
840
841 bpermd = r"""
842 perm <- [0] * 8
843 if index < 64:
844 index <- (RS)[8*i:8*i+7]
845 RA <- [0]*56 || perm[0:7]
846 print (RA)
847 """
848
849 bpermd = r"""
850 if index < 64 then index <- 0
851 else index <- 5
852 do while index < 5
853 index <- 0
854 leave
855 for i = 0 to 7
856 index <- 0
857 """
858
859 _bpermd = r"""
860 for i = 0 to 7
861 index <- (RS)[8*i:8*i+7]
862 if index < 64 then
863 permi <- (RB)[index]
864 else
865 permi <- 0
866 RA <- [0]*56|| perm[0:7]
867 """
868
869 cnttzd = """
870 n <- 0
871 do while n < 64
872 if (RS)[63-n] = 0b1 then
873 leave
874 n <- n + 1
875 RA <- EXTZ64(n)
876 """
877
878 code = cnttzd
879 #code = bpermd
880
881 lexer = IndentLexer(debug=1)
882 # Give the lexer some input
883 print ("code")
884 print (code)
885 lexer.input(code)
886
887 # Tokenize
888 while True:
889 tok = lexer.token()
890 if not tok:
891 break # No more input
892 print(tok)
893
894 #sys.exit(0)
895
896 sd = create_sigdecode()
897 print ("forms", sd.df.forms)
898 for f in sd.df.FormX:
899 print (f)
900
901 _compile = GardenSnakeCompiler().compile
902
903 tree = _compile(code, mode="single", filename="string")
904 import ast
905 tree = ast.fix_missing_locations(tree)
906 print ( ast.dump(tree) )
907
908 print ("astor dump")
909 print (astor.dump_tree(tree))
910 print ("to source")
911 source = astor.to_source(tree)
912 print (source)
913
914 #sys.exit(0)
915
916 from soc.decoder.power_fieldsn import create_sigdecode
917
918 # Set up the GardenSnake run-time environment
919 def print_(*args):
920 print ("args", args)
921 print ("-->", " ".join(map(str,args)))
922
923 def listconcat(l1, l2):
924 return l1 + l2
925
926 d = {}
927 d["print"] = print_
928 d["concat"] = listconcat
929
930 form = 'X'
931 sd = create_sigdecode()
932 getform = getattr(sd.df, "Form%s" % form)
933 print (getform)
934 for k, f in sd.df.instrs[form].items():
935 d[k] = getattr(getform, k)
936
937 compiled_code = compile(source, mode="exec", filename="<string>")
938
939 exec (compiled_code, d)
940 print ("Done")
941