shithub: pokecrystal

Download patch

ref: 04b926c6cb528b6d873bb62f3875db8ad4ae7dee
parent: 2b2018a83abe4dce6a451070d234bd606c15200e
parent: c0a01c998240bacae77addbb08a5d2133cf58a21
author: yenatch <[email protected]>
date: Tue Sep 3 11:58:01 EDT 2013

Merge branch 'master' of github.com:kanzure/pokecrystal

--- a/preprocessor.py
+++ b/preprocessor.py
@@ -16,26 +16,28 @@
     effect_classes,
 )
 
-even_more_macros = [
-    Warp,
-    XYTrigger,
-    Signpost,
-    PeopleEvent,
-    DataByteWordMacro,
-]
+def load_pokecrystal_macros():
+    """
+    Construct a list of macros that are needed for pokecrystal preprocessing.
+    """
+    ourmacros = []
 
-macros = command_classes
-macros += even_more_macros
-macros += [each[1] for each in text_command_classes]
-macros += movement_command_classes
-macros += music_classes
-macros += effect_classes
+    even_more_macros = [
+        Warp,
+        XYTrigger,
+        Signpost,
+        PeopleEvent,
+        DataByteWordMacro,
+    ]
 
-# show lines before preprocessing in stdout
-show_original_lines = False
+    ourmacros += command_classes
+    ourmacros += even_more_macros
+    ourmacros += [each[1] for each in text_command_classes]
+    ourmacros += movement_command_classes
+    ourmacros += music_classes
+    ourmacros += effect_classes
 
-# helpful for debugging macros
-do_macro_sanity_check = False
+    return ourmacros
 
 chars = {
 "ガ": 0x05,
@@ -302,6 +304,16 @@
 "9": 0xFF
 }
 
+class PreprocessorException(Exception):
+    """
+    There was a problem in the preprocessor.
+    """
+
+class MacroException(PreprocessorException):
+    """
+    There was a problem with a macro.
+    """
+
 def separate_comment(l):
     """
     Separates asm and comments on a single line.
@@ -415,13 +427,11 @@
     token = extract_token(asm)
 
     # skip db and dw since rgbasm handles those and they aren't macros
-    if token not in ["db", "dw"]:
-        # check against all names
-        if token in macro_table:
-            return (macro_table[token], token)
+    if token is not None and token not in ["db", "dw"] and token in macro_table:
+        return (macro_table[token], token)
+    else:
+        return (None, None)
 
-    return (None, None)
-
 def is_based_on(something, base):
     """
     Checks whether or not 'something' is a class that is a subclass of a class
@@ -434,13 +444,70 @@
     options += [something.__name__]
     return (base in options)
 
-def macro_translator(macro, token, line):
+def check_macro_sanity(params, macro, original_line):
     """
-    Converts a line with a macro into a rgbasm-compatible line.
+    Checks whether or not the correct number of arguments are being passed to a
+    certain macro. There are a number of possibilities based on the types of
+    parameters that define the macro.
+
+    @param params: a list of parameters given to the macro
+    @param macro: macro klass
+    @param original_line: the line being preprocessed
     """
+    allowed_length = 0
 
-    assert macro.macro_name == token, "macro/token mismatch"
+    for (index, param_type) in macro.param_types.items():
+        param_klass = param_type["class"]
 
+        if param_klass.byte_type == "db":
+            allowed_length += 1 # just one value
+        elif param_klass.byte_type == "dw":
+            if param_klass.size == 2:
+                allowed_length += 1 # just label
+            elif param_klass.size == 3:
+                allowed_length += 2 # bank and label
+            else:
+                raise MacroException(
+                    "dunno what to do with a macro param with a size > 3 (size={size})"
+                    .format(size=param_klass.size)
+                )
+        else:
+            raise MacroException(
+                "dunno what to do with this non db/dw macro param: {klass} in line {line}"
+                .format(klass=param_klass, line=original_line)
+            )
+
+    # sometimes the allowed length can vary
+    if hasattr(macro, "allowed_lengths"):
+        allowed_lengths = macro.allowed_lengths + [allowed_length]
+    else:
+        allowed_lengths = [allowed_length]
+
+    # used twice, so precompute once
+    params_len = len(params)
+
+    if params_len not in allowed_lengths:
+        raise PreprocessorException(
+            "mismatched number of parameters ({count}, instead of any of {allowed}) on this line: {line}"
+            .format(
+                count=params_len,
+                allowed=allowed_lengths,
+                line=original_line,
+            )
+        )
+
+    return True
+
+def macro_translator(macro, token, line, show_original_lines=False, do_macro_sanity_check=False):
+    """
+    Converts a line with a macro into a rgbasm-compatible line.
+
+    @param show_original_lines: show lines before preprocessing in stdout
+    @param do_macro_sanity_check: helpful for debugging macros
+    """
+    if macro.macro_name != token:
+        raise MacroException("macro/token mismatch")
+
     original_line = line
 
     # remove trailing newline
@@ -467,7 +534,7 @@
 
         # check if there are no params (redundant)
         if len(params) == 1 and params[0] == "":
-            raise Exception, "macro has no params?"
+            raise MacroException("macro has no params?")
 
     # write out a comment showing the original line
     if show_original_lines:
@@ -485,46 +552,11 @@
     if not macro.override_byte_check:
         sys.stdout.write("db ${0:02X}\n".format(macro.id))
 
-    # --- long-winded sanity check goes here ---
-
+    # Does the number of parameters on this line match any allowed number of
+    # parameters that the macro expects?
     if do_macro_sanity_check:
+        check_macro_sanity(params, macro, original_line)
 
-        # sanity check... this won't work because PointerLabelBeforeBank shows
-        # up as two params, so these two lengths will always be different.
-        #assert len(params) == len(macro.param_types), \
-        #       "mismatched number of parameters on this line: " + \
-        #       original_line
-
-        # v2 sanity check :) although it sorta sucks that this loop happens twice?
-        allowed_length = 0
-        for (index, param_type) in macro.param_types.items():
-            param_klass = param_type["class"]
-
-            if param_klass.byte_type == "db":
-                allowed_length += 1 # just one value
-            elif param_klass.byte_type == "dw":
-                if param_klass.size == 2:
-                    allowed_length += 1 # just label
-                elif param_klass.size == 3:
-                    allowed_length += 2 # bank and label
-                else:
-                    raise Exception, "dunno what to do with a macro param with a size > 3"
-            else:
-                raise Exception, "dunno what to do with this non db/dw macro param: " + \
-                                 str(param_klass) + " in line: " + original_line
-
-        # sometimes the allowed length can vary
-        if hasattr(macro, "allowed_lengths"):
-            allowed_lengths = macro.allowed_lengths + [allowed_length]
-        else:
-            allowed_lengths = [allowed_length]
-
-        assert len(params) in allowed_lengths, \
-               "mismatched number of parameters on this line: " + \
-               original_line
-
-    # --- end of ridiculously long sanity check ---
-
     # used for storetext
     correction = 0
 
@@ -532,10 +564,7 @@
 
     index = 0
     while index < len(params):
-        try:
-            param_type  = macro.param_types[index - correction]
-        except KeyError as exception:
-            raise Exception("line is: " + str(line) + " and macro is: " + str(macro))
+        param_type  = macro.param_types[index - correction]
         description = param_type["name"]
         param_klass = param_type["class"]
         byte_type   = param_klass.byte_type # db or dw
@@ -569,9 +598,13 @@
                 output += ("db " + param_klass.from_asm(param) + "\n")
                 index += 1
             else:
-                raise Exception, "dunno what to do with this macro " + \
-                "param (" + str(param_klass) + ") " + "on this line: " + \
-                original_line
+                raise MacroException(
+                    "dunno what to do with this macro param ({klass}) in line: {line}"
+                    .format(
+                        klass=param_klass,
+                        line=original_line,
+                    )
+                )
 
         # or just print out the byte
         else:
@@ -584,6 +617,10 @@
 def read_line(l, macro_table):
     """Preprocesses a given line of asm."""
 
+    if l in ["\n", ""] or l[0] == ";":
+        sys.stdout.write(l)
+        return # jump out early
+
     # strip comments from asm
     asm, comment = separate_comment(l)
 
@@ -597,7 +634,7 @@
         sys.stdout.write(asm)
 
     # ascii string macro preserves the bytes as ascii (skip the translator)
-    elif len(asm) > 6 and "ascii " == asm[:6] or "\tascii " == asm[:7]:
+    elif len(asm) > 6 and ("ascii " == asm[:6] or "\tascii " == asm[:7]):
         asm = asm.replace("ascii", "db", 1)
         sys.stdout.write(asm)
 
@@ -613,11 +650,11 @@
         else:
             sys.stdout.write(asm)
 
-    if comment: sys.stdout.write(comment)
+    if comment:
+        sys.stdout.write(comment)
 
-def preprocess(macros, lines=None):
+def preprocess(macro_table, lines=None):
     """Main entry point for the preprocessor."""
-    macro_table = make_macro_table(macros)
 
     if not lines:
         # read each line from stdin
@@ -629,6 +666,11 @@
     for l in lines:
         read_line(l, macro_table)
 
+def main():
+    macros = load_pokecrystal_macros()
+    macro_table = make_macro_table(macros)
+    preprocess(macro_table)
+
 # only run against stdin when not included as a module
 if __name__ == "__main__":
-    preprocess(macros)
+    main()
--- a/prequeue.py
+++ b/prequeue.py
@@ -1,16 +1,28 @@
 # coding: utf-8
+"""
+Starting a new python process to preprocess each source file creates too much
+overhead. Instead, a list of files to preprocess is fed into a script run from
+a single process.
+"""
 
-# Starting a new python process to preprocess each source file
-# creates too much overhead. Instead, a list of files to preprocess
-# is fed into a script run from a single process.
-
 import os
 import sys
 import preprocessor
 
-if __name__ == '__main__':
+def main():
+    macros = preprocessor.load_pokecrystal_macros()
+    macro_table = preprocessor.make_macro_table(macros)
+
+    stdout = sys.stdout
+
     for source in sys.argv[1:]:
         dest = os.path.splitext(source)[0] + '.tx'
         sys.stdin  = open(source, 'r')
         sys.stdout = open(dest, 'w')
-        preprocessor.preprocess(preprocessor.macros)
+        preprocessor.preprocess(macro_table)
+
+    # reset stdout
+    sys.stdout = stdout
+
+if __name__ == '__main__':
+    main()