Просмотр исходного кода

Updated printer format, also added inline comments as an option instead of a separate command.

Taddeus Kroes 14 лет назад
Родитель
Сommit
7e680e4a2c
4 измененных файлов с 59 добавлено и 31 удалено
  1. 3 2
      src/parser.py
  2. 4 4
      src/statement.py
  3. 43 17
      src/writer.py
  4. 9 8
      tests/test_statement.py

+ 3 - 2
src/parser.py

@@ -79,11 +79,12 @@ def p_line_instruction(p):
 
 def p_line_comment(p):
     'line : COMMENT NEWLINE'
-    statements.append(S('comment', p[1], inline=False))
+    statements.append(S('comment', p[1]))
 
 def p_line_inline_comment(p):
     'line : instruction COMMENT NEWLINE'
-    statements.append(S('comment', p[2], inline=True))
+    # Add the inline comment to the last parsed statement
+    statements[-1].options['comment'] = p[2]
 
 def p_instruction_command(p):
     'instruction : command'

+ 4 - 4
src/statement.py

@@ -38,12 +38,12 @@ class Statement:
     def __repr__(self):  # pragma: nocover
         return str(self)
 
+    def has_inline_comment(self):
+        return 'comment' in self.options and len(self.options['comment'])
+
     def is_comment(self):
         return self.stype == 'comment'
 
-    def is_inline_comment(self):
-        return self.is_comment() and self.options['inline']
-
     def is_directive(self):
         return self.stype == 'directive'
 
@@ -152,7 +152,7 @@ class Statement:
     def get_def(self):
         """Get the variable that this statement defines, if any."""
         instr = ['move', 'addu', 'subu', 'li', 'dmfc1', 'mov.d']
-        
+
         if self.is_command('mtc1'):
             return [self[1]]
         if self.is_load_non_immediate() or self.is_arith() \

+ 43 - 17
src/writer.py

@@ -1,50 +1,76 @@
 from math import ceil
 
 
+TABSIZE = 4                 # Size in spaces of a single tab
+INLINE_COMMENT_LEVEL = 6    # Number of tabs to inline commment level
+COMMAND_SIZE = 8            # Default length of a command name, used for
+                            # indenting
+ADD_COMMENT_BLOCKS = True   # Wether to add newlines before and after
+                            # non-inline comment
+ADD_ARGUMENT_SPACE = False  # Wether to add a space between command arguments
+                            # and the previous comma
+
+
 def write_statements(statements):
     """Write a list of statements to valid assembly code."""
     out = ''
     indent_level = 0
-    prevline = ''
+    prev_comment = False
 
     for i, s in enumerate(statements):
-        newline = '\n' if i else ''
+        current_comment = False
 
         if s.is_label():
             line = s.name + ':'
             indent_level = 1
         elif s.is_comment():
-            line = '#' + s.name
-
-            if s.is_inline_comment():
-                l = len(prevline.expandtabs(4))
-                tabs = int(ceil((24 - l) / 4.)) + 1
-                newline = '\t' * tabs
-            else:
-                line = '\t' * indent_level + line
+            line = '\t' * indent_level + '#' + s.name
+            current_comment = True
         elif s.is_directive():
             line = '\t' + s.name
         elif s.is_command():
             line = '\t' + s.name
 
+            # If there are arguments, add tabs until the 8 character limit has
+            # been reached. If the command name is 8 or more characers long,
+            # add a single space
             if len(s):
-                if len(s.name) < 8:
-                    line += '\t'
+                l = len(s.name)
+
+                if l < COMMAND_SIZE:
+                    line += '\t' * int(ceil((COMMAND_SIZE - l)
+                                       / float(TABSIZE)))
                 else:
                     line += ' '
 
-                line += ','.join(map(str, s))
+                delim = ', ' if ADD_ARGUMENT_SPACE else ','
+                line += delim.join(map(str, s))
         else:
             raise Exception('Unsupported statement type "%s"' % s.stype)
 
-        out += newline + line
-        prevline = line
+        # Add the inline comment, if there is any
+        if s.has_inline_comment():
+            start = INLINE_COMMENT_LEVEL * TABSIZE
+            diff = start - len(line.expandtabs(TABSIZE))
+
+            # The comment must not be directly adjacent to the command itself
+            tabs = int(ceil(diff / float(TABSIZE))) + 1 if diff > 0 else  1
 
-    # Add newline at end of file
-    out += '\n'
+            line += '\t' * tabs + '#' + s.options['comment']
+
+        # Add newline at end of command
+        line += '\n'
+
+        if ADD_COMMENT_BLOCKS:
+            if prev_comment ^ current_comment:
+                out += '\n'
+
+        out += line
+        prev_comment = current_comment
 
     return out
 
+
 def write_to_file(filename, statements):
     """Convert a list of statements to valid assembly code and write it to a
     file."""

+ 9 - 8
tests/test_statement.py

@@ -36,9 +36,10 @@ class TestStatement(unittest.TestCase):
         self.assertFalse(S('comment', 'foo', inline=False).is_label())
         self.assertFalse(S('directive', 'foo').is_command())
 
-    def test_is_inline_comment(self):
-        self.assertTrue(S('comment', 'foo', inline=True).is_inline_comment())
-        self.assertFalse(S('comment', 'foo', inline=False).is_inline_comment())
+    def test_has_inline_comment(self):
+        self.assertTrue(S('comment', 'foo', comment='bar').has_inline_comment())
+        self.assertFalse(S('comment', 'foo', comment='').has_inline_comment())
+        self.assertFalse(S('comment', 'foo').has_inline_comment())
 
     def test_jump_target(self):
         self.assertEqual(S('command', 'j', 'foo').jump_target(), 'foo')
@@ -115,16 +116,16 @@ class TestStatement(unittest.TestCase):
         self.assertEqual(S('command', 'dmfc1', 'a', '$f0').get_def(), a)
         self.assertEqual(S('command', 'mtc1', 'b', 'a').get_def(), a)
         self.assertEqual(S('command', 'trunc.w.d', 'a', 'b', 'c').get_def(), a)
-        
+
     def test_get_def_false(self):
         a = []
 
         self.assertEqual(S('command', 'bne', 'a', 'b', 'L1').get_def(), a)
-    
+
     def test_get_use_true(self):
         arg1 = ['$1']
         arg2 = ['$1', '$2']
-        
+
         self.assertEqual(S('command', 'addu', '$3', '$1', '$2').get_use(), \
                 arg2)
         self.assertEqual(S('command', 'subu', '$3', '$1', '$2').get_use(), \
@@ -165,5 +166,5 @@ class TestStatement(unittest.TestCase):
         self.assertEqual(S('command', 'c.lt.d', '$1', '$2').get_use(), arg2)
         self.assertEqual(S('command', 'bgez', '$1', '$2').get_use(), arg1)
         self.assertEqual(S('command', 'bltz', '$1', '$2').get_use(), arg1)
-        self.assertEqual(S('command', 'trunc.w.d', '$3', '$1', '$2').get_use()\
-                , arg2)
+        self.assertEqual(S('command', 'trunc.w.d', '$3', '$1', '$2').get_use(),
+                         arg2)