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