From a6237d68257dd3bb963850c205d0ca4a1c46f88a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:29:54 -0500 Subject: [PATCH 01/17] remove an Exception from preprocessor It wasn't meant to be left in there anyway. --- preprocessor.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index c4a93000a..b6bdfbe7d 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -532,10 +532,7 @@ def macro_translator(macro, token, line): 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 From 0f28e96e4d42b5fee5e7dec094f61fdf9d46577f Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:37:02 -0500 Subject: [PATCH 02/17] remove the show_original_lines global --- preprocessor.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index b6bdfbe7d..6ddd45539 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -31,9 +31,6 @@ macros += movement_command_classes macros += music_classes macros += effect_classes -# show lines before preprocessing in stdout -show_original_lines = False - # helpful for debugging macros do_macro_sanity_check = False @@ -434,11 +431,12 @@ def is_based_on(something, base): options += [something.__name__] return (base in options) -def macro_translator(macro, token, line): +def macro_translator(macro, token, line, show_original_lines=False): """ Converts a line with a macro into a rgbasm-compatible line. - """ + @param show_original_lines: show lines before preprocessing in stdout + """ assert macro.macro_name == token, "macro/token mismatch" original_line = line From 998fa0b198c19088189634920b2c9829a5324576 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:37:55 -0500 Subject: [PATCH 03/17] remove the do_macro_sanity_check global --- preprocessor.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index 6ddd45539..ae9b5624b 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -31,9 +31,6 @@ macros += movement_command_classes macros += music_classes macros += effect_classes -# helpful for debugging macros -do_macro_sanity_check = False - chars = { "ガ": 0x05, "ギ": 0x06, @@ -431,11 +428,12 @@ def is_based_on(something, base): options += [something.__name__] return (base in options) -def macro_translator(macro, token, line, show_original_lines=False): +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 """ assert macro.macro_name == token, "macro/token mismatch" From 1ce2bccd3747f5c311f40389fa3895e29dfae21b Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:45:12 -0500 Subject: [PATCH 04/17] generic preprocessor-specific exception classes These are basic python Exception subclasses that can be used to throw more specific errors and exceptions from within the preprocessor. AssertionError is not a good idea. --- preprocessor.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/preprocessor.py b/preprocessor.py index b6bdfbe7d..69121747d 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -302,6 +302,16 @@ chars = { "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. From ecedde19931bd7d1eb71c59498dab98944639b85 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:46:11 -0500 Subject: [PATCH 05/17] replace an assert in macro_translator Use a MacroException instead of an AssertionError. --- preprocessor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/preprocessor.py b/preprocessor.py index 69121747d..a6a9efd29 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -449,7 +449,8 @@ def macro_translator(macro, token, line): Converts a line with a macro into a rgbasm-compatible line. """ - assert macro.macro_name == token, "macro/token mismatch" + if macro.macro_name != token: + raise MacroException("macro/token mismatch") original_line = line From 2c22d9220c48b2a36550b5a5a2bae1e0988da0d6 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:50:17 -0500 Subject: [PATCH 06/17] fix "raise Exception" formatting in preprocessor --- preprocessor.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index a6a9efd29..905e27533 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -478,7 +478,7 @@ def macro_translator(macro, token, line): # check if there are no params (redundant) if len(params) == 1 and params[0] == "": - raise Exception, "macro has no params?" + raise Exception("macro has no params?") # write out a comment showing the original line if show_original_lines: @@ -519,10 +519,14 @@ def macro_translator(macro, token, line): 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" + 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 + raise Exception( + "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"): @@ -577,9 +581,13 @@ def macro_translator(macro, token, line): 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 Exception( + "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: From 95f7270141cbdb32c7d178a8a89a822e66e3a113 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:51:31 -0500 Subject: [PATCH 07/17] raise MacroException instead of Exception A more specific exception means that error handling can actually work in the future. --- preprocessor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index 905e27533..daed157d4 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -478,7 +478,7 @@ def macro_translator(macro, token, line): # 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: @@ -519,11 +519,11 @@ def macro_translator(macro, token, line): elif param_klass.size == 3: allowed_length += 2 # bank and label else: - raise Exception( + raise MacroException( "dunno what to do with a macro param with a size > 3" ) else: - raise Exception( + raise MacroException( "dunno what to do with this non db/dw macro param: {klass} in line {line}" .format(klass=param_klass, line=original_line) ) @@ -581,7 +581,7 @@ def macro_translator(macro, token, line): output += ("db " + param_klass.from_asm(param) + "\n") index += 1 else: - raise Exception( + raise MacroException( "dunno what to do with this macro param ({klass}) in line: {line}" .format( klass=param_klass, From 93514b18629fe42c0a2688dac57d90998ea5daaa Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:54:03 -0500 Subject: [PATCH 08/17] convert a macro_translator assert in preprocessor AssertionError -> PreprocessorException --- preprocessor.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index daed157d4..c6ae137c4 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -534,9 +534,15 @@ def macro_translator(macro, token, line): else: allowed_lengths = [allowed_length] - assert len(params) in allowed_lengths, \ - "mismatched number of parameters on this line: " + \ - original_line + if len(params) not in allowed_lengths: + raise PreprocessorException( + "mismatched number of parameters ({count}, instead of any of {allowed}) on this line: {line}" + .format( + count=len(params), + allowed=allowed_lengths, + line=original_line, + ) + ) # --- end of ridiculously long sanity check --- From ebb591a7ec624acce35d0378201c641724ac8d4c Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 09:56:24 -0500 Subject: [PATCH 09/17] make a MacroException more verbose in preprocessor --- preprocessor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/preprocessor.py b/preprocessor.py index c6ae137c4..a31386801 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -520,7 +520,8 @@ def macro_translator(macro, token, line): allowed_length += 2 # bank and label else: raise MacroException( - "dunno what to do with a macro param with a size > 3" + "dunno what to do with a macro param with a size > 3 (size={size})" + .format(size=param_klass.size) ) else: raise MacroException( From 63c2dc2f1ffb3ec93f8fb6b97b0b909c36ecc0bf Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 10:03:37 -0500 Subject: [PATCH 10/17] docstring for prequeue.py --- prequeue.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/prequeue.py b/prequeue.py index c9a9a8bcc..ec6cd9e39 100644 --- a/prequeue.py +++ b/prequeue.py @@ -1,8 +1,9 @@ # 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 From 5815edf382d586e84db8d08612d5f4d1c2e7ac96 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 10:13:17 -0500 Subject: [PATCH 11/17] refactor preprocessor macros into a function Remove the "macros" global and instead use a function to construct a list of macros. --- preprocessor.py | 36 ++++++++++++++++++++++-------------- prequeue.py | 2 +- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index b6bdfbe7d..e69089c18 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -16,20 +16,28 @@ from extras.pokemontools.crystal import ( 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, + ] + + 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 + + return ourmacros # show lines before preprocessing in stdout show_original_lines = False @@ -628,4 +636,4 @@ def preprocess(macros, lines=None): # only run against stdin when not included as a module if __name__ == "__main__": - preprocess(macros) + preprocess(load_pokecrystal_macros()) diff --git a/prequeue.py b/prequeue.py index ec6cd9e39..2c8f4cf5a 100644 --- a/prequeue.py +++ b/prequeue.py @@ -14,4 +14,4 @@ if __name__ == '__main__': dest = os.path.splitext(source)[0] + '.tx' sys.stdin = open(source, 'r') sys.stdout = open(dest, 'w') - preprocessor.preprocess(preprocessor.macros) + preprocessor.preprocess(preprocessor.load_pokecrystal_macros()) From 2fd792bbdad32891f131b5735f22230d48674954 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 10:39:49 -0500 Subject: [PATCH 12/17] move macro sanity check into separate function This moves out from macro_translator the macro/param length sanity check into a function called check_macro_sanity. --- preprocessor.py | 106 ++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index f5dd205b2..bac8d7020 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -446,6 +446,60 @@ def is_based_on(something, base): options += [something.__name__] return (base in options) +def check_macro_sanity(params, macro, original_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 + + 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. @@ -500,56 +554,10 @@ def macro_translator(macro, token, line, show_original_lines=False, do_macro_san 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: - - # 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 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] - - if len(params) not in allowed_lengths: - raise PreprocessorException( - "mismatched number of parameters ({count}, instead of any of {allowed}) on this line: {line}" - .format( - count=len(params), - allowed=allowed_lengths, - line=original_line, - ) - ) - - # --- end of ridiculously long sanity check --- + check_macro_sanity(params, macro, original_line) # used for storetext correction = 0 From 70be18427b069cae7ffcfb84811ae268fa18afce Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 11:04:27 -0500 Subject: [PATCH 13/17] don't call load_pokecrystal_macros 2000 times Also, don't call make_macro_table 2000 times by only calling it once and passing the result. --- preprocessor.py | 7 ++++--- prequeue.py | 10 ++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index f5dd205b2..aef1ff220 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -642,9 +642,8 @@ def read_line(l, macro_table): 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 @@ -658,4 +657,6 @@ def preprocess(macros, lines=None): # only run against stdin when not included as a module if __name__ == "__main__": - preprocess(load_pokecrystal_macros()) + macros = load_pokecrystal_macros() + macro_table = make_macro_table(macros) + preprocess(macro_table) diff --git a/prequeue.py b/prequeue.py index 2c8f4cf5a..58790f702 100644 --- a/prequeue.py +++ b/prequeue.py @@ -9,9 +9,15 @@ import os import sys import preprocessor -if __name__ == '__main__': +def main(): + macros = preprocessor.load_pokecrystal_macros() + macro_table = preprocessor.make_macro_table(macros) + 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.load_pokecrystal_macros()) + preprocessor.preprocess(macro_table) + +if __name__ == '__main__': + main() From 7eaf5bf72656fa84d2aa94a0ea6e534120f40131 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 11:07:49 -0500 Subject: [PATCH 14/17] reset stdout in prequeue.py near end Other output shouldn't be dumped into items/item_attributes.tx by default. --- prequeue.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/prequeue.py b/prequeue.py index 58790f702..6efc519d1 100644 --- a/prequeue.py +++ b/prequeue.py @@ -13,11 +13,16 @@ 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(macro_table) + # reset stdout + sys.stdout = stdout + if __name__ == '__main__': main() From 473bd192d9eb5767d5cda2ae21a0aa52552fabf6 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 11:57:01 -0500 Subject: [PATCH 15/17] better read_line performance in preprocessor Jump out of read_line early if the line is an empty string or a newline. --- preprocessor.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index aef1ff220..79fb9224f 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -427,12 +427,10 @@ def macro_test(asm, macro_table): 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) - - return (None, None) + 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) def is_based_on(something, base): """ @@ -611,6 +609,10 @@ def macro_translator(macro, token, line, show_original_lines=False, do_macro_san def read_line(l, macro_table): """Preprocesses a given line of asm.""" + if l in ["\n", ""]: + sys.stdout.write(l) + return # jump out early + # strip comments from asm asm, comment = separate_comment(l) @@ -624,7 +626,7 @@ def read_line(l, macro_table): 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) @@ -640,7 +642,8 @@ def read_line(l, macro_table): else: sys.stdout.write(asm) - if comment: sys.stdout.write(comment) + if comment: + sys.stdout.write(comment) def preprocess(macro_table, lines=None): """Main entry point for the preprocessor.""" From a74462bc52ee3050e13680cd35156589e7f45555 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 12:03:48 -0500 Subject: [PATCH 16/17] even better performance for read_line --- preprocessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preprocessor.py b/preprocessor.py index 79fb9224f..10e407380 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -609,7 +609,7 @@ def macro_translator(macro, token, line, show_original_lines=False, do_macro_san def read_line(l, macro_table): """Preprocesses a given line of asm.""" - if l in ["\n", ""]: + if l in ["\n", ""] or l[0] == ";": sys.stdout.write(l) return # jump out early From 6191559c539af5b4e6f05254d10c6f52993d0321 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 31 Aug 2013 12:12:09 -0500 Subject: [PATCH 17/17] give preprocessor.py a main() --- preprocessor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/preprocessor.py b/preprocessor.py index 10e407380..a8be0ee76 100644 --- a/preprocessor.py +++ b/preprocessor.py @@ -658,8 +658,11 @@ def preprocess(macro_table, lines=None): for l in lines: read_line(l, macro_table) -# only run against stdin when not included as a module -if __name__ == "__main__": +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__": + main()