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