Advertisement
NovaYoshi

rgbds anonymous label

Jul 20th, 2018
196
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.15 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. """
  3. Indentation translator for RGBASM
  4. Copyright 2018 Damian Yerrick
  5. (insert zlib License here)
  6.  
  7. bgrdedent.py is a preprocessor for LR35902 assembly language source
  8. code files intended to be assembled using the RGBDS assembler
  9. ([RGBASM]).  It translates the "followed by colon" convention for
  10. denoting labels that [ca65] and other modern assemblers use to the
  11. "begins in first column" convention that punch-card-era assemblers
  12. used and RGBASM continues to use.
  13.  
  14. First all leading and trailing whitespace is removed.  Then decide
  15. whether or not to re-add a leading space based on the first of the
  16. following rules that applies for each line, where a "word" is a run
  17. of non-whitespace.
  18.  
  19. * If the previous line ended with a backslash, it is a line
  20.  continuation. Start this line at column 2.
  21. * If the line is empty, is blank, output a blank line.
  22. * If the first word is `SECTION`, `EXPORT`, `GLOBAL`, `UNION`,
  23.  `NEXTU`, or `ENDU` (case insensitive), start this line at column 1.
  24. * If the first word does not contain a quotation mark or semicolon,
  25.  and the second word is `EQU`, `SET`, `RB`, `RW`, `RL`, or `EQUS`
  26.  (case insensitive), start this line at column 1.
  27. * If the first word contains a colon or equal sign,
  28.  start this line at column 1.
  29. * If the second word begins with a colon or equal sign,
  30.  start this line at column 1.
  31. * Start everything else at column 2.
  32.  
  33.  
  34. [RGBASM]: https://rednex.github.io/rgbds/rgbasm.5.html
  35. [ca65]: https://cc65.github.io/doc/ca65.html
  36.  
  37. """
  38. import sys
  39. import argparse
  40.  
  41. anonymous_counter = 0
  42.  
  43. def anonymous_label(counter):
  44.     return ".anon%d" % counter
  45.  
  46. def fixcolumns(lines):
  47.     global anonymous_counter
  48.     last_was_continue = False
  49.     # first words that signal no indenting
  50.     word0s = {'section', 'export', 'global', 'union', 'nextu', 'endu'}
  51.     # second words that signal no indenting
  52.     word1s = {'equ', 'set', 'rb', 'rw', 'rl', 'equs'}
  53.     # instruction parts
  54.     jumps = {'jr', 'jp', 'call'}
  55.     conditions = {'z', 'nz', 'c', 'nc'}
  56.  
  57.     for line in lines:
  58.         line = line.strip()            # remove indenting
  59.         owords = line.split()          # keep the original words around
  60.         lwords = line.lower().split()  # separate it into words and lowercase it
  61.         start = ' '                    # start off with an automatic indent
  62.         # process indenting
  63.         if last_was_continue:
  64.             pass
  65.         elif len(lwords) == 0:         # empty line
  66.             start = ''
  67.         elif lwords[0] in word0s:
  68.             start = ''
  69.         elif (len(lwords) > 1 and lwords[1] in word1s
  70.               and ";" not in lwords[0] and ":" not in lwords[0]):
  71.             start = ''
  72.         elif lwords[0] == ':':         # anonymous label
  73.             anonymous_counter += 1
  74.             start = anonymous_label(anonymous_counter)
  75.         elif ':' in lwords[0] or '=' in lwords[0]:
  76.             start = ''
  77.         elif len(lwords) > 1 and lwords[1].startswith((':', '=')) and lwords[0] not in jumps:
  78.             start = ''
  79.        
  80.         # process the actual instructions
  81.         instruction_i = 0
  82.         if len(lwords) >= 1:
  83.             if ':' in lwords[0]:
  84.                 instruction_i = 1
  85.             if len(lwords)-instruction_i >= 2 and lwords[instruction_i] in jumps:
  86.                 # find the destination
  87.                 destination_i = instruction_i+1
  88.                 if len(lwords)-instruction_i >= 3 \
  89.                 and lwords[instruction_i+1].rstrip(',') in conditions:
  90.                     destination_i += 1
  91.  
  92.                 # parse destination
  93.                 destination = lwords[destination_i]
  94.                 if destination[0] == ':' and len(destination) > 1:
  95.                     anon_destination = anonymous_counter
  96.                     if destination[1] == '+':
  97.                         anon_destination += len(destination)-1
  98.                     elif destination[1] == '-':
  99.                         anon_destination -= len(destination)-2
  100.                     else:
  101.                         print("bad anonymous label reference")
  102.                     # patch the destination and recreate the line
  103.                     owords[destination_i] = anonymous_label(anon_destination)
  104.                     line = ' '.join(owords)
  105.  
  106.         yield "".join((start, line, "\n"))
  107.         last_was_continue = line.endswith("\\")
  108.  
  109. def parse_argv(argv):
  110.     parser = argparse.ArgumentParser()
  111.     parser.add_argument("input", default="-", nargs='?',
  112.                         help="file to dedent (standard input if omitted)")
  113.     parser.add_argument("-o", "--output", default="-",
  114.                         help="file to write (standard output if omitted)")
  115.     return parser.parse_args(argv[1:])
  116.  
  117. def main(argv=None):
  118.     args = parse_argv(argv or sys.argv)
  119.  
  120.     infp, outfp = sys.stdin, sys.stdout
  121.     try:
  122.         if args.input != '-':
  123.             infp = open(args.input, "r")
  124.         if args.output != '-':
  125.             outfp = open(args.output, "w")
  126.         outfp.writelines(fixcolumns(infp))
  127.     finally:
  128.         if outfp is not sys.stdout:
  129.             outfp.close()
  130.         if infp is not sys.stdin:
  131.             infp.close()
  132.  
  133. if __name__=='__main__':
  134.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement