diff options
author | Bryan Bishop <kanzure@gmail.com> | 2013-08-31 12:43:05 -0500 |
---|---|---|
committer | Bryan Bishop <kanzure@gmail.com> | 2013-08-31 12:43:05 -0500 |
commit | 4998994c9a624f6298f8a54a665c788825c3e8f9 (patch) | |
tree | 89f8bfa6615f04e49a22071977ff2dc94de213be /pokemontools/preprocessor.py | |
parent | 4c56a6c57a4c21acff5dcf5aa1c23138f0a743c3 (diff) |
merge preprocessor from pokecrystal c0a01c99
That version was better anyway.
Diffstat (limited to 'pokemontools/preprocessor.py')
-rw-r--r-- | pokemontools/preprocessor.py | 179 |
1 files changed, 96 insertions, 83 deletions
diff --git a/pokemontools/preprocessor.py b/pokemontools/preprocessor.py index e33cda7..87faefd 100644 --- a/pokemontools/preprocessor.py +++ b/pokemontools/preprocessor.py @@ -13,9 +13,6 @@ default_macros = [ DataByteWordMacro, ] -class SkippableMacro(object): - macro_name = "db" - chars = { "ガ": 0x05, "ギ": 0x06, @@ -281,6 +278,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. @@ -392,8 +399,9 @@ def macro_test(asm, macro_table): """ # macros are determined by the first symbol on the line token = extract_token(asm) - # check against all names - if token in macro_table: + + # skip db and dw since rgbasm handles those and they aren't macros + 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) @@ -410,15 +418,69 @@ def is_based_on(something, base): options += [something.__name__] return (base in options) -def macro_translator(macro, token, line, skippable_macros, show_original_lines=False, do_macro_sanity_check=False): +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. @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" + if macro.macro_name != token: + raise MacroException("macro/token mismatch") original_line = line @@ -446,16 +508,15 @@ def macro_translator(macro, token, line, skippable_macros, show_original_lines=F # 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: sys.stdout.write("; original_line: " + original_line) - # "db" is a macro because of SkippableMacro - # rgbasm can handle "db" so no preprocessing is required - # (don't check its param count) - if macro.__name__ in skippable_macros or (macro.macro_name == "db" and macro in skippable_macros): + # rgbasm can handle "db" so no preprocessing is required, plus this wont be + # reached because of earlier checks in macro_test. + if macro.macro_name in ["db", "dw"]: sys.stdout.write(original_line) return @@ -465,45 +526,10 @@ def macro_translator(macro, token, line, skippable_macros, show_original_lines=F 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 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 --- + check_macro_sanity(params, macro, original_line) # used for storetext correction = 0 @@ -512,22 +538,7 @@ def macro_translator(macro, token, line, skippable_macros, show_original_lines=F index = 0 while index < len(params): - try: - param_type = macro.param_types[index - correction] - except KeyError as exception: - raise Exception( - "Got a KeyError on param_types. This usually happens when a " \ - "macro should have been skipped but wasn't. But there are also " \ - "other sources of this error.\n\n" \ - "line is: {line}\n" \ - "macro is: {macro}\n" \ - "error is: {error}\n" - .format( - line=line, - macro=macro, - error=exception, - ) - ) + 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 @@ -561,9 +572,13 @@ def macro_translator(macro, token, line, skippable_macros, show_original_lines=F 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: @@ -573,9 +588,13 @@ def macro_translator(macro, token, line, skippable_macros, show_original_lines=F sys.stdout.write(output) -def read_line(l, skippable_macros, macro_table): +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) @@ -589,7 +608,7 @@ def read_line(l, skippable_macros, 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) @@ -601,21 +620,15 @@ def read_line(l, skippable_macros, macro_table): else: macro, token = macro_test(asm, macro_table) if macro: - macro_translator(macro, token, asm, skippable_macros) + macro_translator(macro, token, asm) else: sys.stdout.write(asm) - if comment: sys.stdout.write(comment) + if comment: + sys.stdout.write(comment) -def preprocess(macros, skippable_macros=None, lines=None): +def preprocess(macro_table, lines=None): """Main entry point for the preprocessor.""" - if skippable_macros == None: - skippable_macros = [SkippableMacro] - - macro_table = make_macro_table(list(set(macros + skippable_macros))) - - # HACK for pokecrystal. Must be after make_macro_table call. - skippable_macros += ["TextEndingCommand"] if not lines: # read each line from stdin @@ -625,4 +638,4 @@ def preprocess(macros, skippable_macros=None, lines=None): lines = lines.split("\n") for l in lines: - read_line(l, skippable_macros, macro_table) + read_line(l, macro_table) |