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()